Java. Мережі

Ті, хто працювали з сокетами в джаві, бачили там InputStream/OutputStream. Так так, це стріми, які відповідають за “відправку” і “отримання”. Але, як воно працює під капотом? 🤔

У нас є мережева карта і ядро операційної системи.
Драйвера забезпечують їхню взаємодію. Вони реалізують отримання і передачу даних між ядром і мережевим контролером, щоб дані, отримані мережевою картою надійшли у відкритий світ, або навпаки були отримані нашим застосунком.

Драйвер мережевої карти, зазвичай, реалізує 2 черги(в залежності від провайдера мережової карти, цих черг може бути більше) — TXQ і RXQ.

TXQ (Transmission Queue) - це черга передачі даних з мережевого контролера у світ. Коли мережевий контролер отримує дані, він зберігає їх у своєму TXQ перед тим, як відправити по мережі. TXQ допомагає зберегти порядок передачі даних та дозволяє мережевому контролеру керувати швидкістю передачі.

Аналогічно з RXQ(Receive queue), але в контексті отримання.

Черга драйвера не містить пакетних даних. Натомість вона складається з дескрипторів(“посилань”), які вказують на інші структури даних, які називаються буферами ядра сокетів (SKB), які містять пакетні дані та використовуються в усьому ядрі. SKB має бути доступний для різних процесів, які взаємодіють з сокетами. Якщо буфер зберігався в пам'яті процесу, то інші процеси не змогли б звертатися до нього без спеціального механізму міжпроцесової комунікації.

Socket kernel buffer має два окремі буфери: буфер для прийому даних та буфер для передачі даних. Ці буфери можуть містити дані для різних пакетів, які надходять або надсилаються через сокет.

App 1. Отримання доступу до сокету і записування даних у відповідний буфер SKB

Коли дані надходять до мережевої карти, вони зберігаються в RXQ черзі на рівні мережевого драйвера. Драйвер мережевої карти зберігає відомості про прийнятий пакет, включаючи адресу відправника та призначення, а також інші метадані.

Потім мережевий драйвер операційної системи отримує пакет з RXQ черги та розміщує дані у відповідному SKB буфері для прийому даних на рівні операційної системи.

Коли дані мають бути відправлені через мережу, вони записуються в SKB буфер для передачі даних програмою, що використовує сокет. Далі, операційна система зберігає ці дані у відповідному TXQ чергу на рівні мережевого драйвера. Коли мережевий драйвер мережевої карти готовий надіслати пакет, він бере дані з TXQ черги та передає їх через мережу. Таким чином, дані для відправки можуть бути записані в Socket kernel buffer буфер для передачі даних та збережені у відповідній черзі TXQ, поки вони не будуть передані через мережу.

SKB є проміжним буфером між програмою, яка використовує сокет, та мережевим контролером, який передає дані через мережу.

Тобто, записуючи/зчитуючи в/з Ouput/Input Stream, ми записуємо/зчитуємо дані у відповідний буфер в розрізі конкретного SKB.

Звідки в нього інформація, куди відправити дані?

Якщо ви чули за модель OSI, при відправці або отриманні даних, пакети інкапсулюються і декапсулюються.

Коли ми надсилаємо дані з рівня джави, він починає загортатися метаінформацією нижчих рівнів(TCP & IP), тобто наші дані доповнюються портом і IP адресом. Ця інформація і буде записана в SKB. Потім, при створенні пакету який буде поміщений в TXQ, він вже буде мати всю необіхдну інформацію, яка буде декапсульована отримувачем. І яка буде записана у відповідний SKB, бо як ми знаємо, сокет — це гніздо, яке складається з IP і порта.


PS.

FileDescriptor fd;

Якщо зайти в клас Socket і прослідкувати як він створюється, вийдемо на клас SocketImpl, який має одне цікаве поле fd;

Дескриптор файлу є унікальним ідентифікатором, який використовується для доступу до файлів, сокетів та інших ресурсів в операційній системі. У випадку сокета, дескриптор файлу відповідає файлу, який створюється в операційній системі, коли сокет відкривається. FileDescriptor fd зберігає це посилання на файловий дескриптор сокета, який потім використовується для здійснення взаємодії з операційною системою для передачі та отримання даних.

Тобто джавівський сокет — це зручна обгортка над об’єктом сокета в операційній системі, який надає зручний механізм для запису/зчитування, грубо кажучи, у файл. Ці дані, як цибулина, згорнуті метаінформацією рівнями OSI, будуть відправлені як пакети по мережі до отримувача.

Згадавши файлові дескриптори, можна зазначити, що запис у сокет не відрізняється від запису на диск у тому сенсі, що якийсь набір даних на рівні застосунку має потрапити на диск чи мережевий контролер. У цьому процесі не можна обійтись без використання буферів.
Про них непогано описано в цій статті: How java IO works internally?


Поділись своїми ідеями в новій публікації.
Ми чекаємо саме на твій довгочит!
Oleksandr Klymenko
Oleksandr Klymenko@overpathz

Java Software Engineer

4.6KПрочитань
1Автори
71Читачі
На Друкарні з 19 квітня

Більше від автора

  • Secure networking. Deep Dive

    Глибоке занурення в протоколи TLS/SSL та інфраструктуру відкритих ключів (PKI). Основні поняття, процес встановлення захищеного з'єднання, роль сертифікатів та ланцюжка довіри

    Теми цього довгочиту:

    Security
  • Поширені помилки у дизайні REST API

    У довгочиті розглядаються поширені помилки при проектуванні REST API та способи їх уникнення: версіонування, використання DTO, підхід CQRS, робота з мікросервісами, та інші практики для підвищення продуктивності, безпеки й зручності API

    Теми цього довгочиту:

    Java
  • Java. Короткий огляд еволюції багатопотоковості

    У перших версіях Java багатопоточність реалізовувалася за допомогою класу Thread, який дозволяв створювати нові потоки. Проте ця модель мала багато недоліків:

    Теми цього довгочиту:

    Java

Вам також сподобається

Коментарі (0)

Підтримайте автора першим.
Напишіть коментар!

Вам також сподобається