Як працює TCP? Встановлення та розрив TCP з'єднання Механізми надійної передачі. Узагальнення

Головна / 2 Cool Reader

Багатьом знайома абревіатура TCP, набагато менше людей знає, що це протокол передачі. Але практично ніхто не знає, як він улаштований.

Увага! Цей матеріал розрахований на тих, кого дійсно цікавиться питанням: "Як влаштована мережа, і що я можу зробити, якщо це знатиму". Якщо ж тебе ще бентежать слова на кшталт DNS, Telnet, Socket - то можеш відразу забити на цей матеріал - такі "страшні" слова тут, звичайно, не зустрінуться, але від цього змісту зрозуміліше не стане.

Для тих, хто залишився:

Напевно, багато хто з вас чув такі слова як SYN-flooding або IP-spoofing. Все це різновиди атак – перша D.O.S., друга
полягає в заміні IP-адреси. На перший погляд між цими прикладами немає нічого спільного, але між тим, це не так – обидві ці атаки не можливі без глибокого знання протоколу TCP, протоколу на якому стоїть
Inet.

Специфікація протоколу TCP описана в RFC793. Рекомендую тобі ознайомитися з цим документом, тому що хоч я і постараюся повести до тебе найважливіше, забезпечивши це важливе відповідними коментарями, яких ти не знайдеш у мануалі, але все ж таки через малий об'єм і практичний кут зору, можу й упустити деякі тонкощі .

Дані передаються у вигляді пакетів. Така організація передачі означає, що дані, якого розміру вони не були, розбиваються на окремі фрагменти, які формуються в пакети (формування пакетів передбачає, що до даних додається службовий заголовок), після чого у вигляді пакетів дані передаються через мережу (причому порядок передачі пактів може порушуватись). Система, що приймає "збирає" з пакетів вихідний масив даних на підставі заголовків пакетів. Це не дуже зрозуміло, але доти, доки не розглянемо структуру пакетів.

Структура TCP-пакету:

Поясню лише найважливіші місця:

Адреса одержувача, порт одержувача та адреса відправника, порт відправника – це сподіваюся зрозуміло.

Sequence Number(SYN) - номер черги або послідовний номер, показує порядковий номер пакета під час передачі, саме тому приймаюча система збирає пакети саме так, як треба, а не в тому порядку, як вони прийшли.

Acknowledgment Number(ACK) - номер підтвердження, показує, на пакет з яким SYN відповідає віддалена система, таким чином ми маємо уявлення, що віддалена система отримала наш пакет з даними
SYN.

Контрольні біти- 6 біт (на схемі між reversed та window). Значення бітів:

URG: поле термінового покажчика задіяне
ACK: поле підтвердження задіяне
PSH: функція проштовхування
RST: перезавантаження цього з'єднання
SYN: синхронізація номерів черги
FIN: немає більше даних для передачі

DATA - це ті дані, які ми хочемо передати.

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

Коли ми хочемо встановити з'єднання, ми відправляємо віддалену систему пакет наступної структури:

Client --- SYN (856779) --- Host

Де Client-це ми, a Host - це віддалена система. Як ти бачиш, ми посилаємо пакет лише із зазначенням SYN – це означає, що цей пакет перший, ми ні на що не відповідаємо (відсутня ACK). Цей пакетвиглядає приблизно так:

20 53 52 43 00 00 44 45 53 54 00 00 08 00 45 00 00 2C C3 00 40 00 20 06 10 0C CB 5E FD BA CB 5E F3 4 0 0 0 0 D9 70 00 00 02 04 05 B4 2D

Цікавий момент у тому, звідки береться SYN. SYN утворюється від початкового номера черги
(ISN) - це 32-бітний номер від 1 до 4294967295 (2 в 32-му ступені). ISN при перезавантаженні системи дорівнює 1, потім кожну секунду він збільшується на 128000 (строго кажучи, зміна відбувається кожні 4 мікросекунди) + при кожному встановленому з'єднанні він збільшується на 64000. Виходить, що цикл унікальності ISN, за умови того, що ніякі з'єднання не встановлювалися, становить приблизно 4,55 години. Оскільки жоден пакет так довго не подорожує по мережі, ми можемо вважати, що SYN буде абсолютно унікальним.

Отримавши наш пакет, віддалена система відповідає, що отримала та готова встановити з'єднання. Дані пакет виглядає так:

Host --- SYN (758684758) та ACK (856780) --- Client

Як бачиш, віддалена система дає зрозуміти, що одержала наш пакет. Для цього вона надсилає нам ACK із номером "наш SYN+1". На додаток до цього віддалена система посилає нам свій SYN (ми ж теж відповідатимемо). А відповідь наша буде такою:

Client --- SYN (856780) та ACK (758684759) --- Host

Думаю, тобі вже має бути все зрозуміло. Якщо хтось не зрозумів, то пакет означає наступне: ваш пакет з SYN (758684758) отримано, з'єднання встановлено, наш SYN дорівнює 856780.

Цю процедуру називають "триразовим підтвердженням" або "триразовим рукостисканням". Перші два етапи необхідні синхронізації SYN наших систем, а третій - підтвердження того, що синхронізація відбулася.

Далі ми йде обмін даними, тобто. те, навіщо з'єднання і встановлювалося. Причому слід зазначити, що у всіх стадіях забезпечення збереження даних, переданих з допомогою протоколу TCP, здійснюється так: посланий пакет поміщається в буфер і якщо за визначений часвід віддаленої системи не надходить пакет із підтвердженням (ACK), то пакет посилається знову; якщо ж підтвердження прийшло, то пакет вважається надісланим успішно і видаляється з буфера.

Ну з'єднання нам більше не потрібне, можна його і закрити. Цей етап знову буде
складатися з кількох стадій – сподіваюсь ти вже в змозі сам прочитати ці пакети.

Client --- FIN(4894376) та ACK (1896955378) --- Host

Host --- ACK (4894377) --- Client

Host --- FIN (1896955378) та ACK (4894377) --- Client

Client --- ACK (1896955378) --- Host

Думаю, нічого складного тут нема. Єдине, що варто відзначити – це прапор FIN, який означає бажання завершити з'єднання.

Підбиваючи невеликі підсумки вищевикладеного, зазначимо у яких випадках змінюються/не змінюються порядкові номери:

Передача одного FIN Пакету = +1
Передача одного SYN Пакет = +1
Передача одного ACK Пакет = 0
Передача одного SYN/ACK Пакет = +1
Передача одного FIN/ACK Пакет = +1
Зміна за 1 секунду = +128,000
Встановлення однієї сполуки = +64,000

Можливо, хтось запитає: "А що буде, якщо машин отримає пакет із таким ACK, якого не було?" (SYN=ACK-1, а пакет із таким SYN ми не посилали). Отримавши відповідь незрозуміло на що, ми у свою чергу відповімо віддаленій системі NACK-пакетом (означає "не знаю про що ти", жодного з'єднання не встановлюється), але, сподіваюся, докладніше ми поговоримо з тобою про це наступного разу.

Вступ

TCP це протокол, орієнтований з'єднання. Перед тим, як будь-яка сторона може надіслати дані іншій, між ними має бути встановлене з'єднання. У цьому розділі ми докладно розглянемо, як встановлюється з'єднання TCP і як воно розривається.

Оскільки для роботи TCP необхідно встановити з'єднання між двома кінцями, він відрізняється від протоколів без з'єднання, таких як UDP. Ми бачили, що при використанні UDP кожна сторона просто відсилає датаграми інший, не встановивши перед цим з'єднання.

Встановлення та розрив з'єднання

Щоб подивитися, що відбувається при встановленні та розриві TCP з'єднання, ми виконали на системі svr4 наступну команду:

svr4% telnet bsdi discard
Trying 192.82.148.3 ...
Connected to bsdi.
Escape character is "^]".
^] вводимо Control, праву квадратну дужку,
telnet> quitщоб Telnet клієнт розірвав з'єднання
Connection closed.

Команда telnet встановлює TCP з'єднання з хостом bsdi порт, відповідний discard сервісу (глава 1, розділ ). Це якраз той тип сервісу, який нам необхідний, щоб подивитися, що відбувається при встановленні та розриві з'єднання, але без обміну даними.

Висновок tcpdump

На малюнку 18.1 показаний висновок tcpdump для сегментів, згенерованих цією командою.

1 0.0 svr4.1037 > bsdi.discard: S 1415531521:1415531521 (0)
win 4096
2 0.002402 (0.0024) bsdi.discard > svr4.1037: S 1823083521:1823083521 (0)
ack 1415531522 win 4096

3 0.007224 (0.0048) svr4.1037 > bsdi.discard: . ack 1823083522 win 4096
4 4.155441 (4.1482) svr4.1037 > bsdi.discard: F 1415531522:1415531522 (0)
ack 1823083522 win 4096
5 4.156747 (0.0013) bsdi.discard > svr4.1037: . ack 1415531523 win 4096
6 4.158144 (0.0014) bsdi.discard > svr4.1037: F 1823083522:1823083522 (0)
ack 1415531523 win 4096
7 4.180662 (0.0225) svr4.1037 > bsdi.discard: . ack 1823083523 win 4096

Рисунок 18.1 Виведення tcpdump для встановлення та розриву TCP з'єднання.

Ці сім сегментів TCP містять тільки TCP заголовки. Обмін даними не здійснювалося.

Для TCP сегментів кожен вихідний рядок починається з

source > destination: flags (джерело > призначення: прапори)

де прапори (flags) є чотири із шести прапорних бітів TCP заголовка (). На малюнку 18.2 показано п'ять різних символів, які відповідають прапорам та можуть з'явитися у виведенні.

3-символьне скорочення

Опис

синхронізуючі номери послідовності
відправник закінчив передачу даних
скидання з'єднання
відправка даних приймаючого процесу настільки швидко, наскільки це можливо
жоден із чотирьох прапорів не встановлений

Рисунок 18.2 Символи прапорів, виведені командою tcpdump для прапорних бітів у заголовку TCP.

В даному прикладіми бачимо прапори S, F та точку. Ще два прапори (R та P) з'являться пізніше. Два інших біт прапорів в TCP заголовку - ACK і URG - надруковані командою tcpdump.

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

RFC 1025 [ Postel 1987] називає сегмент, у якому максимальна комбінація всіх доступних прапорних бітів зведена одночасно (SYN, URG, PSH, FIN та 1 байт даних) пакетом Камікадзе (в англійськоюіснує ще кілька визначень подібного пакета, а саме – "брудний пакет", "пакет Новорічної ялинки" тощо).

У рядку 1 поле 1415531521:1415531521 (0) означає, що номер послідовності пакета дорівнює 1415531521, а кількість байт даних у сегменті дорівнює 0. Команда tcpdump друкує початковий номер послідовності, двокрапка, передбачуваний заключний номер послідовності. При цьому існує можливість переглянути остаточний номер послідовності, коли кількість байтів більше ніж 0. У полі з'являється (1), якщо сегмент містить один або кілька байт даних, або (2) якщо зведені прапорець SYN, FIN або RST. У рядках 1, 2, 4 і 6 малюнку 18.1 це полі з'являється, оскільки зведені прапорні біти - обмін будь-якими даними у цьому прикладі не проводився.

У рядку 2 поле ack 1415531522 містить номер підтвердження. Воно друкується лише у випадку, якщо прапорець ACK зведений. Поле win 4096 у кожному рядку виводу показує розмір вікна, яке було оголошено відправником. У цьому прикладі, де не здійснювався обмін даними, розмір вікна залишався незмінним і використовувалася за замовчуванням величина - 4096. (Ми розглянемо розмір вікна TCP у розділі глави 20.)

І останнє поле у ​​виведенні малюнку 18.1, показує максимальний розмір сегмента (MSS - maximum segment size), опція, яку встановлює відправник. Відправник не хоче отримувати TCP сегменти більше, ніж це значення. Це зазвичай робиться для того, щоб уникнути фрагментації (глава 11, розділ ). Ми розглянемо максимальний розмір сегмента розділ цього розділу, а формат різних опцій TCP покажемо розділ цього розділу.

Тимчасові діаграми

На малюнку 18.3 показана тимчасова діаграма, що відповідає цьому обміну пакетами. (Ми описали деякі основні характеристики часових діаграм, коли вперше звернулися до .) На цьому малюнку показано, яка сторона надсилає пакети. Також наведено виведення команди tcpdump (на друк виводилося SYN замість S). У цій часовій діаграмі видалено значення розміру вікна, так як це не суттєво для нашого обговорення.

Протокол встановлення з'єднання

А тепер давайте повернемося до деталей протоколу TCP, які показані на малюнку 18.3. Щоб встановити з'єднання TCP, необхідно:

  1. Сторона, що запитує (яка, як правило, називається клієнт) відправляє SYN сегмент, вказуючи номер порту сервера, до якого клієнт хоче приєднатися, і вихідний номер послідовності клієнта (в даному прикладі ISN, 1415531521). Це сегмент 1.
  2. Сервер відповідає своїм сегментом SYN, що містить вихідний номер серії послідовності (сегмент 2). Сервер також підтверджує прихід клієнта SYN з використанням ACK (ISN клієнта плюс один). На SYN використається один номер послідовності.
  3. Клієнт повинен підтвердити надходження SYN від сервера з використанням ACK (ISN сервера плюс один, сегмент 3).

Цих трьох сегментів достатньо встановлення з'єднання. Часто це називається триразовим рукостисканням (three-way handshake).

Рисунок 18.3 Тимчасова діаграма встановлення та розриву з'єднання.

Вважається, що сторона, яка надсилає перший SYN, активізує з'єднання (активне відкриття). Інша сторона, яка отримує перший SYN та відправляє наступний SYN, бере пасивну участь у відкритті з'єднання (пасивне відкриття). (У розділі цього розділу ми докладно опишемо процедуру відкриття з'єднання, де обидві сторони вважаються активними під час встановлення з'єднання.)

Коли кожна сторона відправила свій SYN для встановлення з'єднання, вона вибирає вихідний номер послідовності (ISN) для цього з'єднання. ISN повинен змінюватися кожного разу, тому кожне з'єднання має свій, відмінний від інших ISN. RFC 793 [ Postel 1981c] вказує, що ISN є 32-бітним лічильником, який збільшується на одиницю кожні 4 мікросекунди. Завдяки номерам послідовностей, пакети, що затрималися в мережі та доставлені пізніше, не сприймаються як частина існуючого з'єднання.

Як вибирається номер послідовності? У 4.4BSD (і в більшості Berkeley реалізацій) при ініціалізації системи вихідний номер послідовності встановлюється в 1. Подібна практика засуджується вимогою до хостів Host Requirements RFC. Потім ця величина збільшується на 64000 кожні півсекунди і повертається значення 0 через кожні 9,5 годин. (Це відповідає лічильнику, який збільшується на одиницю кожні 8 мікросекунд, а не кожні 4 мікросекунди.) Крім того, щоразу, коли встановлюється з'єднання, ця змінна збільшується на 64000.

Проміжок в 4,1 секунд між сегментами 3 і 4 відповідає часу між встановленням з'єднання і введенням команди quit для telnet, щоб розірвати з'єднання.

Протокол розриву з'єднання

Для того щоб встановити з'єднання, необхідно 3 сегменти, а для того щоб розірвати - 4. Це пояснюється тим, що з'єднання TCP може бути в наполовину закритому стані. Оскільки TCP з'єднання повнодуплексне (дані можуть пересуватися в кожному напрямку незалежно від іншого напрямку), кожен напрямок має бути закритий незалежно від іншого. Правило полягає в тому, що кожна сторона повинна надіслати FIN, коли передача даних завершена. Коли TCP приймає FIN, він повинен повідомити, що віддалена сторона розриває з'єднання і припиняє передачу даних у цьому напрямку. FIN зазвичай відправляється в результаті того, що програма була закрита.

Можна сказати, що та сторона, яка першою закриває з'єднання (надсилає перший FIN), здійснює активне закриття, а інша сторона (яка прийняла цей FIN) здійснює пасивне закриття. Зазвичай одна сторона здійснює активне закриття, а інша пасивне, однак у розділі цього розділу ми побачимо, що обидві сторони можуть здійснити активне закриття.

Сегмент номер 4 малюнку 18.3 призводить до закриття з'єднання і посилається, коли Telnet клієнт припиняє роботу. Це відбувається коли ми вводимо quit. При цьому TCP клієнт змушений надіслати FIN, закриваючи потік даних від клієнта до сервера.

Коли сервер отримує FIN, він відправляє назад ACK із прийнятим номером послідовності плюс один (сегмент 5). На FIN витрачається один номер послідовності, як і на SYN. У цей момент TCP сервер також доставляє додатку ознаку кінця файлу (end-of-file) (щоб вимкнути сервер). Потім сервер закриває своє з'єднання, що змушує TCP послати FIN (сегмент 6), який клієнт повинен підтвердити (ACK), збільшивши на одиницю номер прийнятої послідовності (сегмент 7).

На малюнку 18.4 показаний типовий обмін сегментами під час закриття з'єднання. Номери послідовності опушені. На цьому малюнку FIN посилаються через те, що програми закривають свої з'єднання, тоді як ACK для цих FIN генерується автоматично програмним забезпеченням TCP.

З'єднання зазвичай встановлюються клієнтом, тобто перший SYN рухається від клієнта до сервера. Проте будь-яка сторона може активно закрити з'єднання (надіслати перший FIN). Часто, однак, саме клієнт визначає, коли з'єднання має бути розірвано, оскільки процес клієнта в основному управляється користувачем, який вводить щось подібне "quit", щоб закрити з'єднання. На малюнку 18.4 ми можемо поміняти місцями мітки, наведені зверху малюнка, назвавши ліву сторону сервером, а праву сторону клієнтом. Однак навіть у цьому випадку все працюватиме саме так, як показано на малюнку. (Перший приклад розділу 14, наприклад, показував, як сервер часу закриває з'єднання.)

Рисунок 18.4 Звичайний обмін сегментами під час закриття з'єднання.

Звичайний висновок tcpdump

Оскільки завдання відсортувати безліч номерів послідовності досить складна, у виведенні програми tcpdump містяться повні номери послідовності лише для SYN сегментів, проте наступні номери послідовностей показані як відносне зміщення від вихідних номерів послідовності. (Для того щоб отримати висновок, наведений на малюнку 18.1, ми повинні були вказати опцію -S.) Звичайний висновок tcpdump, що відповідає малюнку 18.1, показаний на малюнку 18.5.

1 0.0 svr4.1037 > bsdi.discard: S 1415531521:1415531521(0)
win 4096
2 0.002402 (0.0024) bsdi.discard > svr4.1037: S 1823083521:1823083521(0)
ack 1415531522
win 4096
3 0.007224 (0.0048) svr4.1037 > bsdi.discard: . ack 1 win 4096
4 4.155441 (4.1482) svr4.1037 > bsdi.discard: F 1:1 (0) ack 1 win 4096
5 4.156747 (0.0013) bsdi.discard > svr4.1037: . ack 2 win 4096
6 4.158144 (0.0014) bsdi.discard > svr4.1037: F 1:1 (0) ack 2 win 4096
7 4.180662 (0.0225) svr4.1037 > bsdi.discard: . ack 2 win 4096

Рисунок 18.5 Звичайний висновок команди tcpdump, що відповідає встановленню та розриву з'єднання.

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

Тайм-аут під час встановлення з'єднання

Існує кілька причин, через які не може бути встановлено з'єднання. Наприклад, хост (сервер) вимкнено. Щоб зімітувати подібну ситуацію, ми виконали команду telnet після того, як від'єднали Ethernet кабель від сервера. На малюнку 18.6 показано виведення команди tcpdump.

1 0.0 bsdi.1024 >
win 4096
2 5.814797 (5.8148) bsdi.1024 > svr4.discard: S 291008001:291008001(0)
win 4096
3 29.815436 (24.0006) bsdi.1024 > svr4.discard: S 291008001:291008001(0)
win 4096

Рисунок 18.6 Виведення команди tcpdump для встановлення з'єднання, яке було припинено за тайм-аутом.

У цьому висновку необхідно звернути увагу, як часто TCP клієнт відправляє SYN, намагаючись встановити з'єднання. Другий сегмент посилається через 5,8 секунд після першого, а третій посилається через 24 секунд після другого.

Необхідно зауважити, що цей приклад був запущений через 38 хвилин після того, як клієнт був перезавантажений. Тому відповідний вихідний номер послідовності дорівнює 291008001 (приблизно 38х60х6400х2). На початку глави ми сказали, що типові системи Berkeley встановлюють вихідний номер послідовності 1, а потім збільшують його на 64000 кожні півсекунди.

Також необхідно зазначити, що це перше TCP з'єднання з того моменту, як система була перезавантажена, оскільки номер порту клієнта дорівнює 1024.

Однак на малюнку 18.6 не показано, скільки часу TCP клієнт здійснював повторні передачі, перш ніж відмовитися від спроби. Для того, щоб подивитися це тимчасові значення, ми повинні виконати команду telnet наступним чином:

bsdi% date; telnet svr4 discard; date
Thu Sep 24 16:24:11 MST 1992
Trying 192.82.148.2...
telnet: Зовнішній зв'язок remote host: Connection timed out
Thu Sep 24 16:25:27 MST 1992

Час складає 76 секунд. Більшість систем Berkeley встановлюють межу часу 75 секунд, за цей час має бути встановлене нове з'єднання. У розділі глави 21 ми побачимо, що третій пакет, надісланий клієнтом, буде відкинутий по тайм-ауту приблизно о 16:25:29, тобто через 48 секунд після того, як його було відправлено, при цьому клієнт не припинить свої спроби через 75 секунд. .

Перший тайм-аут

На малюнку 18.6 слід звернути увагу на те, що перший тайм-аут, 5,8 секунд, близький до 6 секунд, однак не дорівнює 6 секунд, тоді як другий тайм-аут практично точно дорівнює 24 секунд. Було виконано ще десять подібних тестів, причому в кожному їх значення першого тайм-ауту коливалося в діапазоні від 5,59 секунди до 5,93 секунди. Другий тайм-аут, однак, завжди був 24,00 секунд.

Це пояснюється тим, що BSD реалізації TCP запускають таймер кожні 500 мілісекунд. Цей 500-мілісекундний таймер використовується для різних тайм-аутів TCP, всі вони будуть описані в наступних розділах. Коли ми вводимо команду telnet, встановлюється вихідний 6-секундний таймер (12 тиків годин), однак він може закінчитися будь-де між 5,5 і 6 секундами. На малюнку 18.7 показано, як це відбувається.

Малюнок 18.7 500-мілісекундний таймер TCP.

Оскільки таймер встановлено в 12 тиків, перше зменшення таймера може відбутися між 0 і 500 мілісекунд після його встановлення. З цього моменту таймер зменшується приблизно кожні 500 мілісекунд, однак перший період часу може бути різним. (Ми використовуємо слово "приблизно", тому що час, коли TCP отримує управління кожні 500 мілісекунд, зразкове, тому що може пройти інше переривання, яке оброблятиметься ядром.)

Коли цей 6-секундний таймер закінчиться на тику поміченому 0 на малюнку 18.7, таймер встановлюється в 24 секунди (48 тиків). Цей наступний таймер дорівнює 24 секундам, оскільки він був встановлений в той момент часу, коли 500-мілісекундний таймер TCP був викликаний ядром, а не користувачем.

Поле типу сервісу

На малюнку 18.6 бачимо вираз . Це поле типу сервісу (TOS - type-of-service) в IP-датаграмі (). Telnet клієнта в BSD/386 встановлює це поле таким чином, щоб отримати мінімальну затримку.

Максимальний розмір сегмента

Максимальний розмір сегмента (MSS) це найбільша порція даних, яку TCP надішле на віддалений кінець. Коли встановлюється з'єднання, кожна сторона може оголосити свій MSS. Значення, які ми бачили, були 1024. IP датаграма, яка вийде в результаті, зазвичай на 40 байт більше: 20 байт відводиться під заголовок TCP і 20 байт під IP заголовок.

У деяких публікаціях йдеться, що ця опція встановлюється "за домовленістю". Насправді домовленість у цьому випадку не використовується. Коли з'єднання встановлюється, кожна сторона повідомляє MSS, якою вона збирається приймати. ( Опція MSS може бути використана тільки в сегменті SYN.) Якщо одна сторона не приймає опцію MSS від іншої сторони, використовується розмір за замовчуванням 536 байт. (У цьому випадку, при 20-байтному IP заголовку та 20-байтному TCP заголовку, розмір IP датаграми становитиме 576 байт.)

У загальному випадку, чим більше MSS тим краще, поки не відбувається фрагментація. (Це не завжди вірно. Зверніться до і , щоб переконатися в цьому.) Великі розміри сегмента дозволяють надіслати більше даних у кожному сегменті, що зменшує відносну вартість IP і TCP заголовків. Коли TCP відправляє SYN сегмент, або коли локальна програма хоче встановити з'єднання, або коли прийнятий запит на з'єднання від віддаленого хоста, може бути встановлено значення MSS, що дорівнює MTU вихідного інтерфейсу мінус розмір фіксованих TCP і IP заголовків. Для Ethernet MSS може сягати 1460 байт. При використанні інкапсуляції IEEE 802.3 (глава 2, розділ) MSS може бути до 1452 байт.

Значення 1024, яке ми бачимо в цьому розділі, відповідає з'єднанням, у яких беруть участь BSD/386 і SVR4, тому що більшість BSD реалізацій вимагає, щоб MSS було кратно 512. Інші системи, такі як SunOS 4.1.3, Solaris 2.2 та AIX 3.2 .2, оголошують MSS рівний 1460 коли обидві сторони знаходяться на одному Ethernet. Розрахунки, наведені в [Mogul 1993], показують, що MSS дорівнює 1460 забезпечують кращу продуктивність на Ethernet, ніж MSS дорівнює 1024.

Якщо IP адреса призначення "не локальна", MSS зазвичай встановлюється за замовчуванням - 536. Чи є локальним або нелокальним кінцевий пункт призначення можна наступним чином. Пункт призначення, IP адреса якого має той самий ідентифікатор мережі і ту саму маску підмережі, що і у відправника є локальним; пункт призначення, IP адреса якого повністю відрізняється від ідентифікатора мережі, є нелокальною; пункт призначення з тим самим ідентифікатором мережі, однак з іншою маскою підмережі, може бути як локальним, так і нелокальним. Більшість реалізацій надають опцію конфігурації (і), яка дозволяє системному адміністратору вказати, які підмережі є локальними, а які нелокальними. Установка цієї опції визначає максимальний анонсований MSS (який за величиною може досягати MTU вихідного інтерфейсу), інакше використовується стандартне значення 536.

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

Уявіть наш хост slip, який має SLIP канал із MTU рівним 296, підключеним до маршрутизатора bsdi. На малюнку 18.8 показані ці системи та хост sun.

Рисунок 18.8 TCP з'єднання від sun до slip та значення MSS.

Ми встановили TCP з'єднання від sun до slip та переглянули сегменти з використанням tcpdump. На малюнку 18.9 показано лише встановлення з'єднання (оголошення розміру вікна видалено).

1 0.0 sun.1093 > slip.discard: S 517312000:517312000(0)

2 0.10 (0.00) slip.discard > sun.1093: S 509556225:509556225(0)
ack 517312001
3 0,10 (0,00) sun.1093 > slip.discard: . ack 1

Рисунок 18.9 Виведення tcpdump для встановлення з'єднання від sun до slip.

Тут важливо звернути увагу на те, що sun не може послати сегмент з порцією даних більше ніж 256 байт, тому що він отримав MSS рівний 256 (рядок 2). Більше того, тому що slip знає що MTU вихідного інтерфейсу дорівнює 296, навіть якщо sun оголосить MSS рівний 1460, він ніколи не зможе надіслати більше ніж 256 байт даних, щоб уникнути фрагментації. Проте, система може надіслати даних менше, ніж MSS оголошений віддаленою стороною.

Уникнути фрагментації таким чином можна тільки якщо хост безпосередньо підключений до мережі з MTU менше ніж 576. Якщо обидва хости підключені до Ethernet і обидва анонсують MSS рівний 536, проте проміжна мережа має MTU рівний 296 буде здійснена фрагментація. Єдиний спосіб уникнути цього – скористатися механізмом визначення транспортного MTU (глава 24, розділ).

Наполовину закритий TCP

TCP надає можливість одному учаснику з'єднання припинити передачу даних, але все ще отримувати дані від віддаленої сторони. Це називається наполовину закритий TCP. Як ми вже згадували раніше, деякі програми можуть користуватися цією можливістю.

Щоб використовувати цю характеристику програмного інтерфейсу, необхідно надати можливість додатку сказати: "Я закінчило передачу даних, тому посилаю ознаку кінця файлу (end-of-file) (FIN) на віддалений кінець, проте я все ще хочу отримувати дані з віддаленого кінця до тих поки він мені не пошле ознаку кінця файлу (end-of-file) (FIN)."

Сокети API підтримують напівзакритий режим, якщо програма викличе shutdown з другим аргументом 1 замість close. Більшість програм, однак, розривають з'єднання в обох напрямках викликом close.

На малюнку 18.10 показано стандартний сценарій для напівзакритого TCP. Ми показали клієнта з лівого боку, він ініціює напівзакритий режим, однак це може зробити будь-яка сторона. Перші два сегменти однакові: FIN від ініціатора, за ним слідує ACK і FIN від приймаючого. Однак далі сценарій відрізнятиметься від того, що наведено на малюнку 18.4, тому що сторона, яка прийняла наказ "напівзакрити", може все ще надсилати дані. Ми показали лише один сегмент даних, за яким слідує ACK, проте в цьому випадку може бути надіслана будь-яка кількість сегментів даних. (Ми розповімо більш детально про обмін сегментами даних та підтвердженнями в .) Коли кінець, який отримав наказ "напівзакрити", здійснив передачу даних, він закриває свою частину з'єднання, в результаті чого посилається FIN, при цьому ознака кінця файлу доставляється додатку, який ініціював "напівзакритий" режим. Коли другий FIN підтверджено, з'єднання вважається повністю закритим.

Малюнок 18.10 TCP у напівзакритому режимі.

Навіщо може бути використаний напівзакритий режим? Одним із прикладів може бути команда Unix rsh(1), яка виконує команду на іншій системі. Команда

sun % rsh bsdi sort< datafile

запустить команду sort на хості bsdi, причому стандартне введення команди rsh читатиметься з файлу з ім'ям datafile. Команда rsh створює TCP з'єднання між собою та програмою, яка буде виконана на віддаленому хості. Потім rsh функціонує досить просто: команда копіює стандартне введення (datafile) у з'єднання і копіює зі з'єднання в стандартний висновок (наш термінал). На малюнку 18.11 показано, як це відбувається. (Ми пам'ятаємо, що TCP повнодуплексне з'єднання.)

Рисунок 18.11 Команда: rsh bsdi sort< datafile.

На віддаленому хості bsdi сервер rshd виконує програму sort таким чином, що її стандартне введення та стандартне виведення направлені в TCP з'єднання. У розділі 14 наводиться докладний описструктури процесу Unix, який бере участь тут, однак нас цікавить, як використовується з'єднання TCP і напівзакритий режим TCP.

Програма sort не може почати генерацію виведення до тих пір, поки все її введення не буде прочитане. Усі вихідні дані, що надходять з'єднання від клієнта rsh на сервер sort, надсилаються у файл, який має бути відсортований. Коли досягається позначка кінця файлу у введенні (datafile), клієнт rsh здійснює напівзакриття TCP з'єднання. Потім сервер sort приймає мітку кінця файлу зі свого стандартного введення (TCP з'єднання), сортує файл і пише результат свого стандартного виведення (TCP з'єднання). Клієнт rsh продовжує читати з'єднання TCP на своєму кінці, копіюючи відсортований файл у свій стандартний висновок.

Без використання наполовину закритого режиму потрібна будь-яка додаткова техніка, яка дозволить клієнту повідомити сервер, що він закінчив посилку даних, проте клієнту все ще дозволено отримувати дані від сервера. Альтернативно необхідно використовувати дві сполуки, проте переважно використання напівзакритого режиму.

Діаграма станів передачі TCP

Ми описали кілька правил встановлення та розриву TCP з'єднання. Ці правила зібрані у діаграму станів передачі, яка наведена малюнку 18.12.

Слід зазначити, що це діаграма - діаграма стандартних станів. Ми помітили звичайну передачу клієнта суцільними жирними стрілками, а звичайну передачу сервера - пунктирними жирними стрілками.

Дві передачі, що ведуть стан ВСТАНОВЛЕНО ( ESTABLISHED), відповідають відкриттю з'єднання, а дві передачі, що ведуть стану ВСТАНОВЛЕНО (ESTABLISHED), відповідають розриву з'єднання. Стан ВСТАНОВЛЕНО (ESTABLISHED) настає у той момент, коли з'являється можливість здійснити передачу даних між двома сторонами в обох напрямках. У наступних розділах буде описано, що відбувається у цьому стані.

Ми об'єднали чотири квадратики в лівій нижній частині діаграми всередині пунктирної рамки та помітили їхнє "активне закриття" (active close). Два інших квадратики (ЧЕКАННЯ_ЗАКРИТТЯ - CLOSE_WAIT і ОСТАННЕ_ПІДТВЕРДЖЕННЯ - LAST_ACK) об'єднані пунктирною рамкою і позначені як "пасивне закриття" (passive close).

Назви 11-ти станів (ЗАКРИТО - CLOSED, СЛУХАЄ - LISTEN, SYN_ВІДПРАВЛЕНИЙ - SYN_SENT, і так далі) на цьому малюнку обрані таким чином, щоб відповідати станам, які виводить команда netstat. Імена ж netstat, у свою чергу, практично ідентичні іменам, описаним в RFC 793. Стан ЗАКРИТИЙ (CLOSED) насправді не є станом, однак є стартовою і кінцевою точкою для діаграми.

Зміна стану від СЛУХАЄ (LISTEN) до SYN_ВІДПРАВЛЕНИЙ (SYN_SENT) теоретично можлива, проте не підтримується в реалізаціях Berkeley.

А зміна стану від ОТРИМАНИЙ_SYN ( SYN_RCVD) назад до СЛУХАЄ (LISTEN) можлива тільки в тому випадку, якщо в стан ОТРИМАНИЙ_SYN (SYN_RCVD) увійшли зі стану СЛУХАЄ (LISTEN) (це звичайний сценарій), а не зі стану SY відкриття). Це означає, що якщо ми здійснили пасивне відкриття (увійшли в стан СЛУХАЄ - LISTEN), отримали SYN, послали SYN з ACK (увійшли в стан ОТРИМАНИЙ_SYN - SYN_RCVD) і потім отримали скидання замість ACK, кінцева точка повертається в стан СЛУХАЄ (LI очікує на прибуття іншого запиту на з'єднання.

18.12 Діаграма змін стану TCP.

На малюнку 18.13 показано звичайне встановлення та закриття TCP з'єднання. Також докладно описані різні станичерез які проходять клієнт і сервер.

Рисунок 18.13 Стан TCP, що відповідає звичайному відкриттю та розриву з'єднання.

На малюнку 18.13 ми припустили, що клієнт, що знаходиться з лівого боку, здійснює активне відкриття, а сервер, що знаходиться праворуч, здійснює пасивне відкриття. Також ми показали, що клієнт здійснює активне закриття (як ми згадували раніше, кожна сторона може здійснити активне закриття).

Вам слід простежити зміни станів на малюнку 18.13 з використанням датаграми зміни станів, наведеної на малюнку 18.12, що дозволить зрозуміти, чому здійснюється та чи інша зміна стану.

Стан очікування 2MSL

Стан ЧАС_ОЧІКУВАННЯ (TIME_WAIT) також іноді називається станом очікування 2MSL. У кожному реалізації вибирається значення максимального часу життя сегмента ( MSL - maximum segment lifetime) . Це максимальний час, протягом якого сегмент може існувати в мережі перед тим, як він буде відкинутий. Ми знаємо, що цей час обмежений, оскільки TCP сегменти передаються за допомогою IP датаграм, а кожна IP датаграма має поле TTL, яке обмежує час її життя.

RFC 793 [ Postel 1981c] вказує, що MSL має бути рівним 2 хвилинам. У різних реалізаціях ця величина має значення 30 секунд, 1 хвилину або 2 хвилини.

У говорилося, що життя IP датаграми обмежується кількістю пересилок, а чи не таймером.

При використанні MSL діють такі правила: коли TCP здійснює активне закриття і посилає останній сегмент, що містить підтвердження (ACK), з'єднання має залишитися в стані TIME_WAIT на час, що дорівнює двом MSL. Це дозволяє TCP повторно надіслати останній ACK у разі, якщо перший ACK втрачено (у разі віддалена сторона відпрацює тайм-аут і повторно передасть свій кінцевий FIN).

Інше призначення очікування 2MSL полягає в тому, що поки TCP з'єднання знаходиться в очікуванні 2MSL, пара сокетів, виділена для цього з'єднання (IP-адреса клієнта, номер порту клієнта, IP-адреса сервера і номер порту сервера), не може бути повторно використана. Це з'єднання може бути використане повторно лише коли закінчиться час очікування 2MSL.

На жаль, більшість реалізацій (Berkeley одна з них) підпорядковуються жорсткішим вимогам. За замовчуванням локальний номер порту не може бути повторно використаний, доки цей номер порту є локальним номером порту пари сокетів, який перебуває в стані очікування 2MSL. Нижче розглянемо приклади загальних вимог.

Деякі реалізації та API надають засоби, які дозволяють обминути ці обмеження. З використанням API сокет може бути вказано опцію сокету SO_REUSEADDR. Вона дозволяє призначати собі номер локального порту, який перебуває у стані 2MSL, проте ми побачимо, що правила TCP не дозволяють цьому номеру порту бути використаним у з'єднанні, яке перебуває у стані очікування 2MSL.

Кожен затриманий сегмент, що прибуває по з'єднанню, яке перебуває у стані очікування 2MSL, відкидається. Так як з'єднання визначається парою сокет в стані 2MSL, це з'єднання не може бути повторно використано доти, доки ми не зможемо встановити нове з'єднання. Це робиться для того, щоб пакети, що запізнилися, не були сприйняті як частина нового з'єднання. (З'єднання визначається парою сокет. Нове з'єднання називається відновленням або пожвавленням даного з'єднання.)

Як ми вже показали малюнку 18.13, зазвичай клієнт здійснює активне закриття і входить у режим TIME_WAIT. Сервер зазвичай здійснює пасивне закриття та не проходить через режим TIME_WAIT. Можна зробити висновок, що якщо ми вимкнемо клієнта і негайно його перестартуємо, цей новий клієнт не зможе використовувати той самий локальний номер порту. У цьому немає жодної проблеми, тому що клієнти зазвичай використовують порти, що динамічно призначаються, і не дбають, який динамічно призначається порт використовується в даний час.

Однак, з точки зору сервера все інакше, оскільки сервер використовують заздалегідь відомі порти. Якщо ми вимкнемо сервер, який має встановлене з'єднання, і спробуємо негайно перестартувати його, сервер не може використовувати свій заздалегідь відомий номер порту як кінцева точка з'єднання, тому що цей номер порту є частиною з'єднання, що знаходиться в стані очікування 2MSL. Тому може знадобитися від 1 до 4 хвилин перед тим, як сервер буде перестартований.

Подивитися такий сценарій можна з використанням програми sock. Ми стартували сервер, приєднали до нього клієнта, а потім вимкнули сервер:

sun % sock-v-s 6666
(запускаємо клієнта на bsdi, який приєднається до цього порту)
connection on 140.252.13.33.6666 from140.252.13.35.1081
^? вводимо символ переривання, щоб вимкнути сервер
sun % sock-s 6666і намагаємося негайно перестартувати сервер на той самий порт
can't bind local address: Address already in use
sun % netstatспробуємо перевірити стан з'єднання
Active Internet сonnections
Proto Recv-Q Send-Q Local Address Foreign Address (state)
tcp 0 0 sun.6666 bsdi.1081 TIME_WAIT
безліч рядків видалено

Коли ми намагаємося перестартувати сервер, програма видає повідомлення про помилку, що вказує на те, що вона не може захопити свій відомий номер порту, тому що він вже використовується (перебуває в стані очікування 2MSL).

Потім ми негайно виконуємо netstat, щоб переглянути стан з'єднання та перевірити, що воно дійсно перебуває в стані TIME_WAIT.

Якщо ми продовжуватимемо спроби перестартувати сервер і подивимося час, коли це вдасться, то можемо обчислити значення 2MSL. Для SunOS 4.1.3, SVR4, BSD/386 і AIX 3.2.2 перестартування сервера займе 1 хвилину, що означає, що MSL дорівнює 30 секунд. У Solaris 2.2 це перестартування сервера займає 4 хвилини, це означає, що MSL дорівнює 2 хвилинам.

Ми можемо побачити ту саму помилку, згенеровану клієнтом, якщо клієнт намагається захопити порт, який є частиною з'єднання, що знаходиться в режимі очікування 2MSL (зазвичай клієнт цього не робить):

sun % sock-v bsdi echoстартуємо клієнт, який приєднується до сервера echo
connected on 140.252.13.33.1162 to 140.252.13.35.7
hello thereдрукуємо цей рядок
hello there вона відбивається луною від сервера
^Dвводимо символ кінця файлу, щоб вимкнути клієнта
sun % sock -b1162 bsdi echo
can't bind local address: Address already in use

При першому запуску клієнта була вказана опція -v, яка дозволяє переглянути, який використовується локальний номер порту (1162). При другому запуску клієнта була вказана опція -b, яка повідомляє клієнту про необхідність призначити самому собі номер локального порту 1162. Як ми і очікували, клієнт не може цього зробити, тому що цей номер порту є частиною з'єднання, яке знаходиться в стані 2MSL.

Тут необхідно згадати про одну особливість стану очікування 2MSL, до якої ми повернемося в , коли розповідатимемо про протокол передачі файлів (FTP - File Transfer Protocol). Як уже згадувалося раніше, в стані очікування 2MSL залишається пара сокетів (що складається з локальної IP адреси, локального порту, віддаленого IP адреси та віддаленого порту). Однак, безліч реалізацій дозволяють процесу повторно використовувати номер порту, який є частиною з'єднання, що знаходиться в режимі 2MSL (зазвичай з використанням опції SO_REUSEADDR) TCP не може дозволити створити нове з'єднання з тією ж парою сокет. Це можна довести за допомогою наступного експерименту:

sun % sock-v-s 6666старт сервера, що слухає порт 6666
(запускаємо клієнта на bsdi, який приєднується до цього порту)
connection on 140.252.13.33.6666 from 140.252.13.35.1098
^? вводимо символ переривання, щоб вимкнути сервер
sun % sock -b6666 bsdi 1098стартуємо клієнта з локальним портом 6666
can't bind local address: Address already in use
sun % sock -A -b6666 bsdi 1098намагаємося знову, цього разу з опцією -A
active open error: Address already in use

Вперше ми запустили нашу програму sock як сервер на порт 6666 та приєднали до нього клієнта з хоста bsdi. Номер порту клієнта 1098, що динамічно призначається. Ми вимкнули сервер, таким чином, він здійснив активне закриття. При цьому 4 параметри - 140.252.13.33 (локальна IP адреса), 6666 (локальний номер порту), 140.252.13.35 (віддалена IP адреса) та 1098 (віддалений номер порту) на сервері потрапляють у стан 2MSL.

Вдруге ми запустили цю програму як клієнта, вказавши локальний номер порту 6666, при цьому була спроба приєднатися до хоста bsdi на порт 1098. При спробі повторно використовувати локальний порт 6666 була згенерована помилка, так як цей порт знаходиться в стані 2MSL.

Щоб уникнути цієї помилки, ми запустили програму знову, вказавши опцію -A, яка активізує опцію SO_REUSEADDR. Це дозволило програмі призначити собі номер порту 6666, однак було отримано помилку, коли програма спробувала здійснити активне відкриття. Навіть якщо програма зможе призначити собі номер порту 6666, вона зможе створити з'єднання з портом 1098 на хості bsdi, оскільки пара сокетів, визначальна це з'єднання, перебуває у стані очікування 2MSL.

Що, якщо ми спробуємо встановити з'єднання з іншого хоста? По-перше, ми повинні перестартувати сервер на sun з прапором -A, тому що порт, який йому необхідний (6666), є частиною з'єднання, що знаходиться в стані очікування 2MSL:

sun % sock-A-s 6666стартуємо сервер, що слухає порт 6666

Потім, перед тим, як стан очікування 2MSL закінчиться на sun, ми стартуємо клієнта на bsdi:

bsdi% sock -b1098 sun 6666
connected on 140.252.13.35.1098 to 140.252.13.33.6666

На жаль це працює! Це недолік TCP специфікації, проте підтримується більшістю реалізацій Berkeley. Ці реалізації сприймають прибуття запиту на нове з'єднання для з'єднання, яке перебуває у стані TIME_WAIT, якщо новий номерпослідовності більше, ніж останній номер послідовності, використаний у попередньому з'єднанні. У цьому випадку ISN для нового з'єднання встановлюється рівним останньому номеру послідовності попереднього з'єднання плюс 128000. Додаток до RFC 1185 показує можливі недоліки подібної технології.

Ця характеристика реалізації дозволяє клієнту і серверу повторно використовувати ті самі номери порту для успішного відновлення того ж самого з'єднання, в тому випадку, якщо сервер не здійснив активне закриття. Ми побачимо інший приклад стану очікування 2MSL на , коли обговорюватимемо FTP. Також зверніться до цього розділу.

Поняття тихого часу

Стан очікування 2MSL надає захист від запізнілих пакетів, що належать раннім з'єднанням, при цьому вони не будуть інтерпретуватися як частина нового з'єднання, яке використовує ті ж локальні і віддалені IP адреси і номери портів. Однак це працює тільки в тому випадку, якщо хост зі з'єднанням у стані 2MSL не вийшов з ладу.

Що якщо хост з портами в стані 2MSL вийшов з ладу, перезавантажився під час MSL і негайно встановив нові з'єднання з використанням тих же локальних і віддалених IP адрес і номерів портів, відповідних локальним портам, які були в стані 2MSL перед поломкою? У цьому випадку сегменти, що запізнилися, з'єднання, яке існувало перед поломкою, можуть бути помилково інтерпретовані як належать новому з'єднанню, створеному після перезавантаження. Це може статися незалежно від того, який вихідний номер послідовності вибрано після перезавантаження.

Для захисту від таких небажаних сценаріїв RFC 793 вказує, що TCP не повинен створювати нові з'єднання до закінчення MSL після моменту завантаження. Це називається тихий час (quiet time).

У деяких реалізаціях хости очікують навіть довше, ніж час MSL після перезавантаження.

Стан ОЧЕКАННЯ_І_ПІДТВЕРДЖЕННЯ_FIN (FIN_WAIT_2)

У стані FIN_WAIT_2 ми надсилаємо наш FIN, а віддалена сторона підтверджує його. Якщо ми не знаходимося в стані напівзакритого з'єднання, то очікуємо від програми на віддаленому кінці, що воно впізнає прийом ознаки кінця файлу і закриє свою сторону з'єднання, причому надішле нам FIN. Тільки коли процес на віддаленому кінці здійснить закриття, наша сторона перейде з режиму FIN_WAIT_2 в режим TIME_WAIT.

Це означає, що сторона з'єднання може залишитися в цьому режимі назавжди. Віддалена сторона все ще може CLOSE_WAIT і може залишатися в цьому стані завжди, доки програма не вирішить здійснити закриття.

Більшість Berkeley реалізацій запобігають такому вічному очікуванню в стані FIN_WAIT_2 наступним чином. Якщо програма, яка здійснила активне закриття, здійснила повне закриття, а не напівзакриття, що вказує на те, що воно очікує на прийом даних, в цьому випадку встановлюється таймер. Якщо з'єднання не використовується протягом 10 хвилин плюс 75 секунд, TCP переводить з'єднання в режим ЗАКРИТО ( CLOSED). У коментарях йдеться про те, що подібна характеристика суперечить специфікації протоколу.

Сегменти скидання (Reset)

Ми згадували, що в заголовку TCP існує біт, званий RST, що означає "скидання" (reset). В загальному випадку сигнал "скидання" (reset) посилається TCP у тому випадку, якщо сегменти, що прибувають, не належать зазначеному з'єднанню. (Ми використовуємо термін "вказане з'єднання" (referenced connection), який позначає з'єднання, що ідентифікується IP адресою призначення та номером порту призначення, а також IP адресою джерела та номером порту джерела. У RFC 793 це називається "сокет".)

Запит про з'єднання на неіснуючий порт

Найбільш загальний випадок, при якому генерується скидання (reset), це коли запит про з'єднання прибуває і при цьому немає процесу, який слухає порт призначення. У випадку UDP, як ми бачили в розділі глави 6, якщо датаграма прибуває на порт призначення, що не використовується - генерується помилка ICMP про недоступність порту. TCP натомість використовує скидання.

Ми наведемо простий приклад із використанням Telnet клієнта, вказавши номер порту, який не використовується на пункті призначення:

bsdi% telnet svr4 20000порт 20000 не використовується
Trying 140.252.13.34...
telnet: Зовнішній зв'язок для remote host: Connection refused

Повідомлення про помилку видається клієнту Telnet негайно. На малюнку 18.14 показаний обмін пакетами, що відповідає цій команді.

1 0.0 bsdi.1087 > svr4.20000: S 297416193:297416193(0)
win 4096
2 0.003771 (0.0038) svr4.20000 > bsdi.1087: R 0:0 (0) ack 297416194 win 0

Рисунок 18.14 Генерація скидання під час спроби відкрити з'єднання на порт, що не існує.

Значення, які нам необхідно докладніше розглянути на цьому малюнку, це поле номера послідовності та поле номера підтвердження у скиданні. Так як біт підтвердження (ACK) не був встановлений в сегменті, що прибув, номер послідовності скидання встановлено в 0, а номер підтвердження встановлено у вхідний вихідний номер послідовності (ISN) плюс кількість байт даних в сегменті. Незважаючи на те, що в сегменті, що прибув, не присутній реальних даних, біт SYN логічно займає 1 байт в просторі номера послідовності; таким чином, у цьому прикладі номер підтвердження в скиданні встановлюється ISN плюс довжина даних (0) плюс один SYN біт.

Розрив з'єднання

У розділі цього розділу ми бачили, що звичайний метод, який використовується для розриву з'єднання, полягає в тому, що одна із сторін посилає FIN. Іноді це називається правильним звільненням (orderly release), оскільки FIN посилається після того, як усі дані, раніше поставлені в чергу, були відправлені, і зазвичай при цьому не відбувається втрата даних. Однак існує можливість перервати з'єднання, надіславши скидання (reset) замість FIN. Іноді це називається переривним звільненням (abortive release).

Подібний розрив з'єднання надає додатку дві можливості: (1) будь-які дані, що стоять у черзі - губляться, і скидання відправляється негайно, і (2) сторона, яка прийняла RST, може сказати, що віддалена сторона розірвала з'єднання замість того, щоб закрити його звичайним чином . Програмний інтерфейс (API), який використовується програмою, повинен надавати спосіб згенерувати подібне скидання замість нормального закриття.

Ми можемо подивитися, що відбувається у разі подібного розриву з використанням нашої програми sock. Сокети API надають цю можливість за допомогою опції сокету "затримки закриття" (linger on close) ( SO_LINGER). Ми вказали опцію -L з часом затримки, що дорівнює 0. Це означає, що замість звичайного FIN буде надіслано скидання, щоб закрити з'єднання. Ми підключимося до версії програми sock, яка виступає в ролі сервера, на svr4:

bsdi% sock-L0 svr4 8888це клієнт; сервер показано далі
hello, worldвводимо один рядок, який буде відправлений на віддалений кінець
^Dвводимо символ кінця файлу, щоб вимкнути клієнта

На малюнку 18.15 показано виведення команди tcpdump для цього прикладу. (Ми видалили всі оголошення вікон у цьому малюнку, тому що вони не впливають на наші міркування.)

1 0.0 bsdi.1099 > svr4.8888: S 671112193:671112193(0)

2 0.004975 (0.0050) svr4.8888 > bsdi.1099: S 3224959489:3224959489(0)
ack 671112194
3 0.006656 (0.0017) bsdi.1099 > svr4.8888: . ack 1
4 4.833073 (4.8264) bsdi.1099 > svr4.8888: P 1:14 (13) ack 1
5 5.026224 (0.1932) svr4.8888 > bsdi.1099: . ack 14
6 9.527634 (4.5014) bsdi.1099 > svr4.8888: R 14:14 (0) ack 1

Рисунок 18.15 Розрив з'єднання із використанням скидання (RST) замість FIN.

У рядках 1-3 показано нормальне встановлення з'єднання. У рядку 4 надсилається рядок даних, який ми надрукували (12 символів плюс Unix символ нового рядка), та у рядку 5 приходить підтвердження про прийом даних.

Рядок 6 відповідає введеному символу кінця файлу (Control-D), за допомогою якого ми вимкнули клієнта. Оскільки ми вказали розрив замість звичайного закриття (опція командного рядка -L0), TCP на bsdi надішле RST замість звичайного FIN. RST сегмент містить номер послідовності та номер підтвердження. Також зверніть увагу на те, що сегмент RST не очікує відповіді з віддаленого кінця - він не містить підтвердження взагалі. Отримувач скидання перериває з'єднання і повідомляє програму, що з'єднання було перервано.

Ми отримаємо наступну помилку від сервера при такому обміні:

svr4% sock-s 8888запускаємо як сервер, слухаємо порт 8888
hello, world це те, що відправив клієнт
read error: Connection reset by peer

Цей сервер читає з мережі та копіює у стандартний висновок все що отримав. Зазвичай він завершує свою роботу, отримавши ознаку кінця файлу від свого TCP, однак тут бачимо, що він отримав помилку при прибутті RST. Помилка це саме те, що ми очікували: з'єднання було розірвано одним із учасників з'єднання.

Визначення напіввідкритого з'єднання

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

Ще одна причина, через яку може виникнути напіввідкрите з'єднання, полягає в тому, що на хості клієнта було вимкнено живлення, замість того, щоб погасити програму клієнта, а потім вимкнути комп'ютер. Це відбувається тоді, наприклад, Telnet клієнт запускається на PC, і користувачі вимикають комп'ютер наприкінці робочого дня. Якщо на момент вимкнення PC не здійснювалася передача даних, сервер ніколи не дізнається, що клієнт зник. Коли користувач приходить наступного ранку, включає свій PC і стартує новий клієнт Telnet, на хості сервера стартує новий сервер. Через це на хості сервера може з'явитися багато відкритих TCP з'єднань. (Ми побачимо спосіб, за допомогою якого один кінець TCP з'єднання може визначити, що інший зник. Це робиться з використанням TCP опції "залишайся в живих" (keepalive)).

Ми можемо легко створити напіввідкрите з'єднання. Запускаємо клієнта Telnet на bsdi і приєднуємося до сервера discard на svr4. Вводимо один рядок і дивимось з використанням tcpdump, як він проходить, а потім від'єднуємо Ethernet кабель від хоста сервера та перезапускаємо його. Цим самим ми імітували вихід з ладу сервера хоста. (Ми від'єднали Ethernet кабель перед перезавантаженням сервера, щоб той не послав FIN у відкрите з'єднання, що роблять деякі TCP модулі при вимкненні.) Після того як сервер перезавантажився, ми під'єднали кабель і спробували надіслати ще один рядок від клієнта на сервер. Оскільки сервер був перезавантажений і втратив усі дані про з'єднання, які існували до перезавантаження, він нічого не знає про з'єднання і не підозрює про те, якому з'єднанню належать сегменти, що прибули. У цьому випадку сторона TCP відповідає скиданням (reset).

bsdi% telnet svr4 discardзапуск клієнта
Trying 140.252.13.34...
Connected to svr4.
Escape character is "^]".
hi thereцей рядок надіслано нормально
тут ми перезавантажили хост сервера
another lineу цьому місці було здійснено скидання (reset)
Connection closed by foreign host.

На малюнку 18.16 показано виведення tcpdump для цього прикладу. (Ми видалили з виводу оголошення вікон, інформацію про тип сервісу та оголошення MSS, оскільки вони не впливають на наші міркування.)

1 0.0 bsdi.1102 > svr4.discard: S 1591752193:1591752193(0)
2 0.004811 (0.0048) svr4.discard > bsdi.1102: S 26368001:26368001(0)
ack 1591752194
3 0.006516 (0.0017) bsdi.1102 > svr4.discard: . ack 1

4 5.167679 (5.1612) bsdi.1102 > svr4.discard: P 1:11 (10) ack 1
5 5.201662 (0.0340) svr4.discard > bsdi.1102: . ack 11

6 194.909929 (189.7083) bsdi.1102 > svr4.discard: P 11:25 (14) ack 1
7 194.914957 (0.0050) arp who-has bsdi tell svr4
8 194.915678 (0.0007) arp reply bsdi is-at 0:0:c0:6f:2d:40
9 194.918225 (0.0025) svr4.discard > bsdi.1102: R 26368002:26368002(0)

Рисунок 18.16 Скидання у відповідь на прихід сегмента даних при напіввідкритому з'єднанні.

У рядках 1-3 здійснюється нормальне встановлення з'єднання. У рядку 4 відправляється рядок "hi there" (це можна приблизно перевести як "ей ви, там") на сервер discard, у рядку 5 приходить підтвердження.

У цьому місці ми від'єднали кабель Ethernet від svr4, перезавантажили його і під'єднали кабель знову. Уся процедура зайняла приблизно 190 секунд. Потім ми надрукували наступний рядок введення на клієнті ("another line"), і коли ми натиснули клавішу Return, рядок було надіслано на сервер (рядок 6 на малюнку 18.16). При цьому була отримана відповідь від сервера, однак оскільки сервер був перезавантажений, його ARP кеш порожній, тому в рядках 7 та 8 ми бачимо ARP запит та відгук. Потім у рядку 9 було надіслано скидання (reset). Клієнт отримав скидання і видав, що з'єднання було перервано віддаленим хостом. (Останнє повідомлення виведення від клієнта Telnet не так інформативне як могло б бути.)

Одночасне відкриття

Для двох додатків існує можливість здійснити активне відкриття в той самий час. З кожної сторони має бути переданий SYN, і ці SYN мають пройти мережею назустріч один одному. Також потрібно, щоб кожна сторона мала номер порту, який відомий іншій стороні. Це називається одночасним відкриттям (simultaneous open).

Наприклад, додаток на хості А має локальний порт 7777 здійснює активне відкриття на порт 8888 хоста В. Додаток на хості має локальний порт 8888 здійснює активне відкриття на порт 7777 хоста А.

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

TCP спеціально розроблений таким чином, щоб обробляти одночасне відкриття, при цьому результатом є одна сполука, а не дві. (В інших сімействах протоколів, наприклад, у транспортному рівні OSI, у такому разі створюється два з'єднання, а не одне.)

Коли здійснюється одночасне відкриття, зміни станів протоколу відрізняються від тих, що показані на малюнку 18.13. Обидва кінця відправляють SYN в один і той же час, при цьому входять у стан SYN_ВІДПРАВЛЕНИЙ (SYN_SENT). Коли кожна сторона приймає SYN, стан змінюється на SYN_ПРИНЯТИЙ ( SYN_RCVD) (див. малюнок 18.12), і кожен кінець повторно відправляє SYN з підтвердженням про те, що SYN прийнятий. Коли кожен кінець отримує SYN плюс ACK, стан змінюється на ВСТАНОВЛЕНО (ESTABLISHED). Зміни станів наведено малюнку 18.17.

Рисунок 18.17 Обмін сегментами у процесі одночасного відкриття.

Одночасне відкриття вимагає обміну чотирма сегментами, на один більше, ніж при "триразовому рукостисканні". Також зверніть увагу на те, що ми не називаємо один з кінців клієнтом, а інший сервером, тому що в даному випадкуобидва виступають як клієнт і як сервер.

Здійснити одночасне відкриття можна, проте досить складно. Обидві сторони повинні бути стартовані приблизно в той самий час, таким чином, щоб SYN перетнулися один з одним. У цьому випадку може допомогти великий час повернення між двома учасниками з'єднання, що дозволяє SYN перетнути. Щоб отримати це, ми використовуємо як один учасник з'єднання хост bsdi, а інший хост vangogh.cs.berkeley.edu. Оскільки між ними знаходиться SLIP канал з дозвоном, час повернення має бути досить великим (кілька сотень мілісекунд), що дозволяє SYN перетнутися.

Один кінець (bsdi) призначає локальний порт 8888 (опція командного рядка -b) і здійснює активне відкриття на порт 7777 іншого хоста:

bsdi% sock-v-b8888 vangogh.cs.berkeley.edu 7777
connected on 140.252.13.35.8888 to 128.32.130.2.7777
TCP_MAXSEG = 512
hello, worldвводимо цей рядок
and hi there цей рядок був надрукований на іншому кінці
connection closed by peer це висновок, коли було отримано FIN

Інший кінець був стартований приблизно в цей же час, він призначив собі номер локального порту 7777 і здійснив активне відкриття на порт 8888:

vangogh % sock -v -b7777 bsdi.tuc.noao.edu 8888
connected on 128.32.130.2.7777 to 140.252.13.35.8888
TCP_MAXSEG = 512
hello, world це введено на іншому кінці
and hi thereми надрукували цей рядок
^Dі потім ввели символ кінця файлу EOF

Ми вказали прапорець -v у командному рядку програми sock, щоб перевірити IP адреси та номери портів для кожного кінця з'єднань. Цей прапор також друкує MSS, який використовується на кожному кінці з'єднання. Ми також надрукували як введення по одному рядку на кожному кінці, які були відправлені на віддалений кінець і надруковані там, щоб переконатися, що обидва хости "бачать" один одного.

На малюнку 18.18 показаний обмін сегментами для цієї сполуки. (Ми видалили деякі нові опції TCP, що з'явилися у вихідних SYN, що прийшли від vangogh, яка працює під керуванням 4.4BSD. Ми опишемо ці нові опції в розділі цього розділу.) Зверніть увагу, що за двома SYN (рядки 1 і 2) слідують два SYN з ACK (рядки 3 та 4). У цьому відбувається одночасне відкриття.

У рядку 5 показаний введений рядок "hello, world", який йде від bsdi до vangogh з підтвердженням у рядку 6. Рядки 7 та 8 відповідають рядку "and hi there", що йде в іншому напрямку. У рядках 9-12 показано звичайне закриття з'єднання.

Більшість реалізацій Berkeley не підтримує коректне одночасне відкриття. У цих системах, якщо Ви можете досягти того, що SYN перетнуться, все закінчиться обміном сегментів, кожен з SYN і ACK, в обох напрямках. У більшості реалізацій який завжди здійснюється перехід стану SYN_SENT в стан SYN_RCVD, показаний малюнку 18.12.

1 0.0 bsdi.8888 > vangogh.7777: S 91904001:91904001(0)
win 4096
2 0.213782 (0.2138) vangogh.7777 > bsdi.8888: S 1058199041:1058199041(0)
win 8192
3 0.215399 (0.0016) bsdi.8888 > vangogh.7777: S 91904001:91904001(0)
ack 1058199042 win 4096

4 0.340405 (0.1250) vangogh.7777 > bsdi.8888: S 1058199041:1058199041(0)
ack 91904002 win 8192

5 5.633142 (5.2927) bsdi.8888 > vangogh.7777: P 1:14 (13) ack 1 win 4096
6 6.100366 (0.4672) vangogh.7777 > bsdi.8888: Додати замітку Редагувати zametkу | ack 14 win 8192

7 9.640214 (3.5398) vangogh.7777 > bsdi.8888: P 1:14 (13) ack 14 win 8192
8 9.796417 (0.1562) bsdi.8888 > vangogh.7777: Додати замітку Редагувати zametkу | ack 14 win 4096

9 13.060395 (3.2640) vangogh.7777 > bsdi.8888: F 14:14 (0) ack 14 win 8192
10 13.061828 (0.0014) bsdi.8888 > vangogh.7777: Додати замітку Редагувати zametkу | ack 15 win 4096
11 13.079769 (0.0179) bsdi.8888 > vangogh.7777: F 14:14 (0) ack 15 win 4096
12 13.299940 (0.2202) vangogh.7777 > bsdi.8888: Додати замітку Редагувати zametkу | ack 15 win 8192

Рисунок 18.18 Обмін сегментами при одночасному відкритті.

Одночасне закриття

Як ми сказали раніше, з одного боку (часто, але не завжди з боку клієнта) здійснюється активне закриття, при цьому посилається перший FIN. Також можливо для обох сторін здійснити активне закриття, оскільки протокол TCP дозволяє здійснити одночасне закриття (simultaneous close).

У термінах, наведених на малюнку 18.12, обидва кінці переходять від стану ВСТАНОВЛЕНО ( ESTABLISHED) до стану ОЧЕКАННЯ_FIN_1 ( FIN_WAIT_1) , коли програма видає сигнал до закриття. При цьому обидва посилають FIN, які можливо зустрінуться десь у мережі. Коли FIN прийнято, на кожному кінці відбувається перехід зі стану FIN_WAIT_1 у стан ЗАКРИВАЮ ( CLOSING) і з кожного боку надсилається завершальний ACK. Коли кожен кінець отримує завершальний ACK, стан змінюється на TIME_WAIT. На малюнку 18.19 показано зміни станів.

Рисунок 18.19 Обмін сегментами у процесі одночасного закриття.

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

Заголовок TCP може містити опції (). Єдині опції, визначені в оригінальній специфікації TCP, такі: кінець списку опцій, немає операції та максимальний розмір сегмента. Ми бачили в наших прикладах опцію MSS практично в кожному SYN сегменті.

Більш нові RFC, наприклад RFC 1323 визначають додаткові опції TCP, більшість з яких можна виявити лише в пізніших реалізаціях. (Ми опишемо нові опції в .) На малюнку 18.20 показаний формат поточних опцій TCP - тих, що описані в RFC 793 та RFC 1323.

Рисунок 18.20 TCP опції.

Кожна опція починається з 1-байтового типу (kind), який свідчить про тип опції. Опції, тип яких дорівнює 0 та 1, займають 1 байт. Інші опції мають довжину (len) байт, який слідує за байтом типу. Довжина - це повна довжина, що включає байти типу та довжини.

Опція "ні операції" (NOP) додана, щоб відправник міг заповнити поля, які мають бути кратні 4 байтам. Якщо ми встановимо TCP з'єднання від системи 4.4BSD, у початковому сегменті SYN за допомогою tcpdump можна побачити наступні опції:

Опція MSS встановлена ​​в 512, за нею слідує NOP, за нею слідує опція розміру вікна. Перша опція NOP використовується для того, щоб доповнити 3-байтову опцію розміру вікна до 4 байт. Так само, 10-байтова опція тимчасової марки передує двома NOP, щоб займати 12 байт.

Чотири інші опції, яким відповідає тип рівний 4, 5, 6 та 7, називаються опціями селективного ACK та луна опціями. Ми не показали їх на малюнку 18.20, тому що відлуння опції замінені опцією тимчасової марки, а селективні ACK, як визначено в даний час, все ще знаходяться в обговоренні і не були включені в RFC 1323. Треба зазначити, що пропозиція T/TCP для TCP транзакцій (розділ 24) вказує ще три опції з типами рівними 11, 12 і 13.

Реалізація TCP сервера

У розділі глави 1 ми сказали, більшість TCP серверів - конкурентні. Коли на сервер надходить запит про встановлення нового з'єднання, він приймає з'єднання та запускає новий процес, який обслуговуватиме нового клієнта. Залежно від операційної системи використовуються різні способистворення нового сервера. У Unix системах новий процес створюється з допомогою функції fork.

Нам необхідно обговорити, як TCP взаємодіє із конкурентними серверами. Хочеться відповісти на наступне запитання: як обробляються номери портів, коли сервер отримує запит на нове з'єднання від клієнта, і що станеться, якщо одночасно прибуде кілька запитів на з'єднання?

Номери портів сервера TCP

Ми можемо сказати, як TCP обробляє номери портів, розглянувши будь-який TCP-сервер. Розглянемо сервер Telnet з використанням команди netstat. Наступний висновок наведено для системи, які не мають активних Telnet з'єднань. (Ми видалили всі рядки за винятком одного, який показує Telnet сервер.)

sun % netstat -a -n -f inet
Active Internet connections (including servers)
Proto Recv-Q Send-Q Local Address Foreign Address (state)
tcp 0 0 *.23 *.* LISTEN

Прапор -a повідомляє про всі кінцеві точки мережі, а не тільки про які знаходяться в стані встановлено ( ESTABLISHED). Прапорець -n друкує IP адреси в цифровому десятковому поданні, замість того щоб використовувати DNS для конвертування адрес в імена, і друкує цифрові номери портів (наприклад, 23) замість друку імен сервісів (в даному випадку Telnet). Опція -f inet повідомляє лише про кінцеві точки TCP і UDP.

Локальна адреса виводиться як *.23, де зірочка зазвичай називається символом підстановки або метасимволом. Це означає, що вхідний запит про з'єднання (SYN) буде прийнято з будь-якого локального інтерфейсу. Якщо хост має кілька інтерфейсів, ми могли б вказати одну конкретну IP-адресу як локальну IP-адресу (один з IP-адрес хоста), і тільки запити на з'єднання, прийняті з цього інтерфейсу, будуть обслужені. (Ми побачимо, як це робиться пізніше в цьому розділі.) Локальний порт дорівнює 23, це заздалегідь відомий порт для Telnet.

Віддалена адреса показана як *.*, це означає, що віддалена IP адреса і віддалений номерпорту поки не відомі, тому що кінцева точка знаходиться в стані СЛУХАЄ (LISTEN), очікуючи прибуття запиту на з'єднання.

Зараз ми стартуємо Telnet клієнта на хості slip (140.252.13.65), який приєднається до цього сервера. Тут наведено відповідні рядки виведення команди netstat:


tcp 0 0 140.252.13.33.23 140.252.13.65.1029 ESTABLISHED
tcp 0 0 *.23 *.* LISTEN

Перший рядок для порту 23 - це встановлене з'єднання (ESTABLISHED). Для цього з'єднання заповнені всі чотири елементи локальної та віддаленої адрес: локальна IP адреса і номер порту, і віддалена IP адреса і номер порту. Локальна IP-адреса відповідає інтерфейсу, на який прибув запит про з'єднання (Ethernet інтерфейс, 140.252.13.33).

Кінцева точка залишилася у стані LISTEN. Це кінцева точка, яку конкурентний сервер використовує для того, щоб приймати запити на з'єднання, які прийдуть в майбутньому. В даному випадку TCP модуль, що знаходиться в ядрі, створив нову кінцеву точку в стані ESTABLISHED, в момент, коли запит про з'єднання прибув і був прийнятий. Також зверніть увагу на те, що номер порту для з'єднання, яке знаходиться в стані ESTABLISHED, не змінився: він дорівнює 23, як і для кінцевої точки, яка знаходиться в стані LISTEN.

Зараз ми стартуємо ще одного Telnet клієнта з того самого клієнта (slip) на цей сервер. Відповідний висновок команди netstat буде виглядати так:

Proto Recv-Q Send-Q Local Address Foreign Address (state)
tcp 0 0 140.252.13.33.23 140.252.13.65.1030 ESTABLISHED
tcp 0 0 140.252.13.33.23 140.252.13.65.1029 ESTABLISHED
tcp 0 0 *.23 *.* LISTEN

Зараз ми бачимо два встановлених (ESTABLISHED) з'єднання з того ж самого хоста на той самий сервер. Обидва мають локальний номер порту, що дорівнює 23. Це не проблема для TCP, тому що номери віддалених портів різні. Вони повинні бути різні, тому що кожен Telnet клієнт використовує порт, що динамічно призначається, а з визначення динамічно призначається порту ми знаємо, що динамічно призначеним може бути тільки той порт, який не використовується в даний час на хості (slip).

Цей приклад показує, що TCP демультиплексує вхідні сегменти з використанням всіх чотирьох значень, які порівнюються з локальними та віддаленими адресами: IP адреса призначення, номер порту призначення, IP адреса джерела та номер порту джерела. TCP не може визначити, який процес отримав вхідний сегмент, переглядаючи лише номер порту призначення. Також тільки одна з трьох кінцевих точок на порту 23, що знаходиться в стані LISTEN, приймає вхідні запити на з'єднання. Кінцеві точки, які перебувають у стані ESTABLISHED, що неспроможні приймати сегменти SYN, а кінцева точка, що у стані LISTEN, неспроможна приймати сегменти даних.

Зараз ми стартуємо ще одного клієнта Telnet із хоста solaris, який пройде через SLIP канал від sun, а не через Ethernet.

Proto Recv-Q Send-Q Local Address Foreign Address (state)
tcp 0 0 140.252.1.29.23 140.252.1.32.34603 ESTABLISHED
tcp 0 0 140.252.13.33.23 140.252.13.65.1030 ESTABLISHED
tcp 0 0 140.252.13.33.23 140.252.13.65.1029 ESTABLISHED
tcp 0 0 *.23 *.* LISTEN

Локальна IP-адреса для першого встановленого (ESTABLISHED) з'єднання тепер відповідає адресі інтерфейсу SLIP каналу на багатоінтерфейсному хості sun (140.252.1.29).

Обмеження локальних IP адрес

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

sun % sock-s 140.252.1.29 8888

обмежує цей сервер тільки для з'єднань, що прибувають з інтерфейсу SLIP (140.252.1.29). Висновок команди netstat покаже наступне:

Proto Recv-Q Send-Q Local Address Foreign Address (state)

Якщо ми приєднаємося до цього сервера через SLIP канал із хоста solaris, це спрацює.

Proto Recv-Q Send-Q Local Address Foreign Address (state)
tcp 0 0 140.252.1.29.8888 140.252.1.32.34614 ESTABLISHED
tcp 0 0 140.252.1.29.8888 *.* LISTEN

Однак якщо ми намагатимемося приєднатися до цього сервера з хоста через Ethernet (140.252.13), запит на з'єднання не буде прийнятий модулем TCP. Якщо ми подивимося за допомогою tcpdump, то побачимо, що на SYN отримано відгук RST, як показано на малюнку 18.21.

1 0.0 bsdi.1026 > sun.8888: S 3657920001:3657920001(0)
win 4096
2 0.000859 (0.0009) sun.8888 > bsdi.1026: R 0:0 (0) ack 3657920002 win 0

Рисунок 18.21 Обмеження запитів на з'єднання, що базується на локальній IP-адресі сервера.

Програма, що працює на сервері, ніколи не побачить запит на з'єднання - обмеження здійснюється TCP модулем в ядрі на основі локальної IP-адреси, зазначеного додатком.

Обмеження віддаленої IP-адреси

У розділі глави 11 ми бачили, що UDP сервер може визначити віддалену IP адресу та номер порту, на додаток до вказаних локальної IP адреси та номера порту. Функції інтерфейсу, наведені в RFC 793, дозволяють серверу здійснювати пасивне відкриття на основі повністю описаного віддаленого сокета (у цьому випадку очікується запит на активне відкриття від конкретного клієнта) або не зазначеного віддаленого сокета (у цьому випадку очікується запит на з'єднання від будь-якого клієнта).

На жаль, більшість API не надають таких можливостей. Сервер повинен залишати віддалений сокет неконкретизованим, чекаючи на прибуття з'єднання, а потім перевіряючи IP адресу і номер порту клієнта.

На малюнку 18.22 показано три типи адрес та взаємозв'язку адрес з портами, які TCP сервер може встановити для себе. У всіх випадках lport це наперед відомий порт сервера, а localIP повинен бути IP адресою локального інтерфейсу. Порядок, в якому розташовані три рядки в таблиці, відповідає порядку, в якому модуль TCP намагається визначити, яка локальна кінцева точка прийме вхідний запит на з'єднання. Спочатку здійснюється спроба, що відповідає першому рядку таблиці (якщо підтримується), а потім інші специфікації (останній рядок з IP адресами, зазначеними у вигляді символів підстановки) пробуються останньою.

Рисунок 18.22 Вказує локальні та віддалені IP-адреси та номери порту для сервера TCP.

Вхідна черга запитів на з'єднання

Конкурентний сервер запускає новий процес, який обслуговує кожного клієнта, тому сервер, що слухає, повинен бути завжди готовий обробити наступний вхідний запит на з'єднання. Це основна причина, через яку використовуються конкурентні сервери. Однак, існує ймовірність того, що кілька запитів на з'єднання прибудуть саме в той момент, коли сервер, що слухає, створює новий процес, або коли операційна система зайнята обробкою іншого процесу з більш високим пріоритетом. Як TCP обробляє ці вхідні запити на з'єднання, поки слухаюча програма зайнята?

Реалізації Berkeley використовують такі правила.

  1. Кожна кінцева точка, що слухає, має фіксовану довжину черги з'єднань, які можуть бути прийняті TCP ("триразове рукостискання" завершено), однак ще не прийняті додатком. Будьте уважні, проводячи різницю між прийняттям з'єднання TCP і розміщенням його в чергу, та програмою, яка приймає з'єднання з цієї черги.
  2. Програма вказує обмеження або межу для цієї черги, яка зазвичай називається backlog. Це обмеження має бути в діапазоні від 0 до 5. (Більшість додатків вказують максимальне значення 5.)
  3. Коли надходить запит на з'єднання (SYN сегмент), TCP переглядає поточну кількість з'єднань, поставлених у теперішній моменту чергу для цієї кінцевої точки, що слухає, при цьому він з'ясовує, чи можна прийняти з'єднання. Ми очікуємо, що значення backlog, вказане програмою, буде максимальним, тобто дозволено поставити в чергу максимальну кількість з'єднань для цієї точки, хоча це не дуже просто. На малюнку 18.23 показано взаємини між значенням backlog та реальною максимальною кількістю з'єднань, які можна поставити в чергу у традиційних Berkeley системах та Solaris 2.2.

    значення backlog

    Максимальна кількість з'єднань, поставлених у чергу

    Традиційний BSD

    Рисунок 18.23 Максимальна кількість прийнятих з'єднань для кінцевої точки, що слухає.

    Запам'ятайте, що це значення backlog вказує тільки на максимальну кількість з'єднань, поставлених у чергу для однієї кінцевої точки, що слухає, всі з яких вже прийняті TCP і очікують того, щоб бути прийнятими додатком. Значення backlog не надає жодного впливу на максимальну кількість з'єднань, що може бути встановлена ​​системою, або кількість клієнтів, яка може обслужити конкурентний сервер.

    Значення для Solaris на цьому малюнку саме такі, як ми очікували. Традиційні значення для BSD (з якихось незрозумілих причин) дорівнюють значенню backlog, помноженому на 3, поділеному на 2, плюс 1.

  4. Якщо в черзі для даної слухаючої кінцевої точки є місце для нового з'єднання (див. малюнок 18.23), TCP модуль підтверджує (ACK) SYN, що прийшов, і встановлює з'єднання. Програма сервера зі слухаючою кінцевою точкою не побачить цього нового з'єднання доти, доки не буде прийнято третій сегмент з "триразового рукостискання". Також клієнт може вважати, що сервер готовий прийняти дані, коли активне відкриття клієнта завершено успішно, перш ніж програма сервера буде повідомлено про нове з'єднання. (Якщо це станеться, сервер TCP просто поставить в чергу вхідні дані.)
  5. Якщо не вистачає місця для того, щоб поставити в чергу нове з'єднання, TCP просто ігнорує прийнятий SYN. У відповідь нічого не посилається (не посилається навіть сегмент RST). Якщо сервер, що слухає, не може відмовитися від прийому деяких вже прийнятих з'єднань, які заповнили собою чергу до межі, активне відкриття клієнта буде перервано по тайм-ауту.

Ми можемо переглянути цей сценарій за допомогою програми sock. Запустимо її з новою опцією (-O), яка повідомляє про необхідність зробити паузу після створення кінцевої точки, що слухає, перед прийомом будь-якого запиту на з'єднання. Якщо потім ми запустимо кілька клієнтів протягом паузи, сервер буде змушений поставити в чергу прийняті з'єднання, а те, що відбудеться, ми побачимо з використанням команди tcpdump.

bsdi% sock-s-v-q1-O30 5555

Опція -q1 встановлює backlog слухаючої кінцевої точки значення 1, для традиційної BSD системи це буде відповідати двом запитам на з'єднання (рисунок 18.23). Опція -O30 змушує програму "проспати" 30 секунд перед прийомом будь-якого з'єднання від клієнта. Це дає нам 30 секунд, щоб стартувати кілька клієнтів, які заповнять чергу. Ми стартуємо чотирьох клієнтів на Sun Sun.

На малюнку 18.24 показаний висновок програми tcpdump, що починається з першого SYN від першого клієнта. (Ми видалили оголошення розміру вікна та оголошення MSS. Також ми виділили номери портів клієнта жирним шрифтом, коли TCP з'єднання встановлюється - "триразове рукостискання".)

Перший запит на з'єднання від клієнта, що прийшов з порту 1090, приймається модулем TCP (сегменти 1-3). Другий запит на з'єднання від клієнта з порту 1091 також приймається модулем TCP (сегменти 4-6). Програма сервера все ще "спить" і не прийняла жодного з'єднання. Все виконане було здійснено модулем TCP в ядрі. Також треба зазначити, що два клієнти успішно здійснили активне відкриття, тобто "триразове рукостискання" було успішно завершено.

1 0.0 sun. 1090 > bsdi.7777: S 1617152000:1617152000(0)
2 0.002310 (0.0023) bsdi.7777 > sun. 1090 : S 4164096001:4164096001(0)
30.003098 (0.0008) sun. 1090 > bsdi.7777: . ack 1617152001
ack 1
4 4.291007 (4.2879) sun. 1091 > bsdi.7777: S 1617792000:1617792000(0)
5 4.293349 (0.0023) bsdi.7777 > sun. 1091 : S 4164672001:4164672001(0)
ack 1617792001
6 4.294167 (0.0008) sun. 1091 > bsdi.7777: . ack 1
7 7.131981 (2.8378) sun.1092 >
8 10.556787 (3.4248) sun.1093 > bsdi.7777: S 1618688000:1618688000(0)
9 12.695916 (2.1391) sun.1092 > bsdi.7777: S 1618176000:1618176000(0)
10 16.195772 (3.4999) sun.1093 >
11 24.695571 (8.4998) sun.1092 > bsdi.7777: S 1618176000:1618176000(0)
12 28.195454 (3.4999) sun. 1093 > bsdi.7777: S 1618688000:1618688000(0)
13 28.197810 (0.0024) bsdi.7777 > sun. 1093 : S 4167808001:4167808001(0)
14 28.198639 (0.0008) sun. 1093 > bsdi.7777: . ack 1618688001
ack 1
15 48.694931 (20.4963) sun. 1092 > bsdi.7777: S 1618176000:1618176000(0)
16 48.697292 (0.0024) bsdi.7777 > sun. 1092 : S 4170496001:4170496001(0)
ack 1618176001
17 48.698145 (0.0009) sun. 1092 > bsdi.7777: . ack 1

Рисунок 18.24 Виведення програми tcpdump для прикладу використання backlog.

Ми спробували стартувати третього клієнта в сегменті 7 (порт 1092) та четвертого в сегменті 8 (порт 1093). TCP ігнорувало обидва SYN, так як черга для цієї кінцевої точки, що слухає, заповнена. Обидва клієнти повторно передали свої SYN у сегментах 9, 10, 11, 12 та 15. Третя повторна передача четвертого клієнта прийнята (сегменти 12-14), тому що 30-секундна пауза сервера закінчилася, і сервер видалив два з'єднання, які були прийняті, очистивши чергу. (Причина, через яку це сталося, полягає в тому, що це з'єднання було прийнято сервером у момент часу 28.19, а не в момент часу, який більше 30; це сталося тому, що знадобилося кілька секунд, щоб стартувати першого клієнта [сегмент 1 , час старту у виведенні] після старту сервера.) Четверта повторна передача третього клієнта також прийнята (сегменти 15-17). З'єднання четвертого клієнта (порт 1093) прийнято сервером перед з'єднанням третього клієнта (порт 1092) через збіг часу між закінченням 30-секундної паузи і повторною передачею клієнта.

Ми могли очікувати, що черга прийнятих сполук буде оброблена програмою відповідно до принципу FIFO (перший увійшов, перший вийшов). Таким чином, після того, як TCP прийняв додаток на порти 1090 і 1091, ми очікували, що програма отримає з'єднання спочатку на порт 1090, а потім з'єднання на порт 1091. Однак, у більшості реалізацій Berkeley існує помилка (bug), в результаті чого використовується порядок LIFO (останній увійшов, перший вийшов). Виробники багато разів намагалися виправити цю помилку, проте вона досі існує у таких системах, як SunOS 4.1.3.

TCP ігнорує вхідний SYN, коли черга заповнена, і не відповідає з використанням RST, через помилку. Зазвичай черга заповнена, тому що програма або операційна система зайняті, тому програма не може обробити вхідні з'єднання. Такий стан може змінитися за короткий проміжок часу. Однак, якщо TCP-сервер відповів скиданням (reset), активне відкриття клієнта буде перервано (якраз це відбудеться, якщо сервер не був стартований). Так як SYN ігнорований, TCP клієнт буде змушений повторно передати SYN пізніше, сподіваючись, що в черзі з'явиться місце для нового з'єднання.

Тут необхідно обговорити ще одну дуже важливу деталь, яка є практично у всіх реалізаціях TCP/IP. Вона у тому, що TCP приймає вхідний запит на з'єднання (SYN) у разі, якщо у черзі є місце. При цьому програма не може подивитися, від кого надійшов запит (IP адреса джерела та номер порту джерела). Це не потрібно TCP, це лише загальна техніка, що використовується в реалізаціях. Якщо API, такий як TLI (розділ 1), повідомляє додаток про прибуття запиту на з'єднання і дозволяє застосунку вибрати, прийняти це з'єднання чи ні, то при використанні TCP виходить так, що коли програмі повідомляється, що з'єднання щойно прибуло, насправді TCP вже завершив "триразове рукостискання"! В інших транспортних рівнях існує можливість розмежувати прибуте та прийняте з'єднання (OSI транспортний рівень), проте TCP такої можливості не надає.

Solaris 2.2 надає опцію, яка не дозволяє TCP приймати вхідний запит на з'єднання, доки йому це не дозволить програму (tcp_eager_listeners у розділі програми E).

Ця поведінка також означає, що TCP сервер не може зробити так, що активне відкриття клієнта буде перервано. Коли з'єднання від нового клієнта потрапляє до сервера, "тристороннє рукостискання" TCP вже закінчено і активне відкриття клієнта завершено успішно. Якщо сервер потім дивиться на IP-адресу клієнта і номер порту і вирішує, що він не хоче обслуговувати цього клієнта, всі сервери можуть просто закрити з'єднання (при цьому буде надіслано FIN) або скинути з'єднання (буде послано RST). У будь-якому випадку клієнт вважатиме, що з сервером все нормально, оскільки завершилося активне відкриття, і, можливо, вже надіслав серверу будь-який запит.

Короткі висновки

Перед тим, як два процеси зможуть обмінюватися даними з використанням TCP, вони повинні встановити з'єднання між собою. Коли робота між ними закінчена, з'єднання має бути розірване. У цьому розділі детально розглянуто, як встановлюється з'єднання з використанням "триразового рукостискання" і як воно розривається з використанням чотирьох сегментів.

Ми використовували tcpdump, щоб показати всі поля у заголовку TCP. Ми також подивилися, як встановлене з'єднання може бути перервано по тайм-ауту, як скидається з'єднання, що відбувається з напіввідкритим з'єднанням і як TCP надає напівзакритий режим, одночасне відкриття та одночасне закриття.

Щоб зрозуміти функціонування TCP, необхідно розглянути фундаментальну діаграму зміни станів TCP. Ми розглянули пункти, як встановлюється та розривається з'єднання, і які при цьому відбуваються зміни в стані. Також ми розглянули, як сервери TCP здійснюють встановлення з'єднань TCP.

TCP з'єднання унікально ідентифікуються 4 параметрами: локальною IP адресою, локальним номером порту, віддаленою IP адресою та віддаленим номером порту. Якщо з'єднання розривається, одна сторона все одно повинна пам'ятати про це з'єднання, у цьому випадку ми говоримо, що працює режим TIME_WAIT. Правило говорить, що ця сторона може здійснити активне відкриття, увійшовши в цей режим, після того як минув подвоєний час MSL, прийнятий для цієї реалізації.

Вправи

  1. У розділі ми сказали, що вихідний номер послідовності (ISN) зазвичай встановлюється в 1 і збільшується на 64000 кожні півсекунди і щоразу здійснюється активне відкриття. Це означає, що молодші три цифри в ISN завжди будуть 001. Однак на малюнку 18.3 ці молодші три цифри для кожного напряму дорівнюють 521. Як це сталося?
  2. На малюнку 18.15 ми надрукували 12 символів, але бачили, що TCP надіслав 13 байт. На малюнку 18.16 ми надрукували 8 символів, але TCP надіслав 10 байт. Чому в першому випадку було додано 1 байт, а в другому 2 байти?
  3. У чому полягає відмінність між напіввідкритим з'єднанням та напівзакритим з'єднанням?
  4. Якщо ми стартуємо програму sock як сервер, а потім перервемо її роботу (при цьому до неї не було підключено жодного клієнта), ми можемо негайно перестартувати сервер. Це означає, що він не перебуватиме в стані очікування 2MSL. Поясніть це у термінах діаграми зміни стану.
  5. У розділі ми показали, що клієнт не може повторно використовувати той самий локальний номер порту, поки порт є частиною з'єднання в стані очікування 2MSL. Однак, якщо ми запустимо програму sock двічі поспіль як клієнт, приєднуючись до сервера часу, ми можемо використовувати той самий локальний номер порту. Крім того, ми можемо створити нове з'єднання, яке буде в стані очікування 2MSL. Як це відбувається?

    sun % sock -v bsdi daytime

    Wed Jul 7 07:54:51 1993
    connection closed by peer

    sun % sock -v -b1163 bsdi daytimeповторне використання того ж номера локального порту
    connected on 140.252.13.33.1163 to 140.252.13.35.13
    Wed Jul 7 07:55:01 1993
    connection closed by peer

  6. Наприкінці розділу , коли ми описували стан FIN_WAIT_2, ми вказали, що більшість реалізацій переводить з'єднання з цього стану стан CLOSED, якщо додаток здійснило повне закриття (не наполовину закритий) приблизно через 11 хвилин. Якщо інша сторона (в стані CLOSE_WAIT) чекає 12 хвилин перед закриттям (відправлення свого FIN), що його TCP отримає у відповідь на FIN?
  7. Яка сторона телефонної розмови здійснює активне відкриття, а яка здійснює пасивне відкриття? Чи можливе одночасне відкриття? Чи можливе одночасне закриття?
  8. На малюнку 18.6 ми не бачили ARP запиту або ARP відгук. Однак апаратна адреса хоста svr4 має бути в ARP кеші bsdi. Що зміниться на цьому малюнку, якщо цього пункту в ARP кеші немає?
  9. Поясніть наступний висновок команди tcpdump. Порівняйте його з рисунком 18.13.

    1 0.0 solaris.32990 > bsdi.discard: S 40140288:40140288 (0)
    win 8760
    2 0.003295 (0.0033) bsdi.discard > solaris.32990: S 4208081409:4208081409 (0)
    ack 40140289 win 4096

    3 0.419991 (0.4167) solaris.32990 > bsdi.discard: P 1:257 (256) ack 1 win 9216
    4 0.449852 (0.0299) solaris.32990 > bsdi.discard: F 257:257 (0) ack 1 win 9216
    5 0,451965 (0,0021) bsdi.discard > solaris.32990: . ack 258 win 3840
    6 0.464569 (0.0126) bsdi.discard > solaris.32990: F 1:1 (0) ack 258 win 4096
    7 0.720031 (0.2555) solaris.32990 > bsdi.discard: . ack 2 win 9216

  10. Чому серверу на малюнку 18.4 не скомбінувати ACK на FIN клієнта зі своїм власним FIN, зменшивши тим самим кількість сегментів до трьох?
  11. На малюнку 18.16 чому номер послідовності RST дорівнює 26368002?
  12. Скажіть, чи базується запит TCP до канального рівня про його MTU на принципі розбиття на рівні?
  13. демультиплексуються з урахуванням номера порту призначення TCP. Чи правильно це?
Потрійне рукостискання TCP

Процес початку сеансу TCP (також званий «рукостискання», складається з трьох кроків).

1. Клієнт, який має намір встановити з'єднання, надсилає серверу сегмент із номером послідовності та прапором SYN.

  • Сервер отримує сегмент, запам'ятовує номер послідовності та намагається створити сокет (буфери та керуючі структури пам'яті) для обслуговування нового клієнта.
    • У разі успіху сервер посилає клієнту сегмент з номером послідовності та прапорами SYN та ACK, і переходить у стан SYN-RECEIVED.
    • У разі невдачі сервер посилає клієнту сегмент із прапором RST.

2. Якщо клієнт отримує сегмент з прапором SYN, він запам'ятовує номер послідовності і посилає сегмент з прапором ACK.

  • Якщо він одночасно отримує і прапор ACK (що зазвичай і відбувається), він переходить у стан ESTABLISHED.
  • Якщо клієнт отримує сегмент з прапором RST, він припиняє спроби з'єднатися.
  • Якщо клієнт не отримує відповіді протягом 10 секунд, він повторює процес з'єднання заново.

3. Якщо сервер може SYN-RECEIVED отримує сегмент з прапором ACK, він перетворюється на стан ESTABLISHED.

  • Інакше після тайм-ауту він закриває сокет і перетворюється на стан CLOSED.

Процес називається «трьохетапним узгодженням», оскільки незважаючи на те, що можливий процес встановлення з'єднання з використанням чотирьох сегментів (SYN у бік сервера, ACK у бік клієнта, SYN у бік клієнта, ACK у бік сервера), на практиці для економії часу використовується три сегмента.

TCP - вікно

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

TCP вікно – алгоритм управління інтенсивністю потоку даних, заснований на зміні максимальної кількості даних, яку одержувач готовий прийняти і підтвердити одним сегментом у відповідь
Розмір вікна завжди призначає одержувач, виходячи із статистики кількості помилок

Домени колізій

Домен колізій- це область мережіEthernet, всі вузли якої розпізнають колізію незалежно від того, в якій частині цієї галузі колізія виникла

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

Режими роботи VTP на комутаторах

VTP - VLAN Trunking Protocol, що дозволяє полегшити адміністрування комутаторів, а саме управління VLAN-ами на комутаторах Cisco. З VTP можна створювати, змінювати або видаляти VLAN на VTP сервері і всі ці зміни автоматично перенесуться на всі комутатори в одному домені VTP. Що позбавляє адміністратора конфігурування VLAN на кожному комутаторі.
Існує три режими роботи VTP (VTP mode):

1. VTP Server - серверний режим. У цьому режимі можна створювати, видаляти та змінювати VLAN-и, а також задавати різні параметри, такі як версію протоколу (vtp version), vtp фільтрацію (vtp pruning) для всього VTP домену. VTP сервер повідомляє про свою конфігурацію VLAN-ів інші комутатори, що знаходяться в тому ж VTP домені, і синхронізує їх конфігурацію VLAN. Також VTP сервер може синхронізувати свою конфігурацію з конфігурацією VTP клієнта, якщо у клієнта вище номер ревізії конфігурації. Обмін VTP інформацією відбувається через ТРАНКОВІ порти.

2. VTP Client – ​​режим клієнта. На комутаторі з режимом VTP Client не можна створювати, видаляти або змінювати VLAN. Все настроювання VLAN-ів комутатор бере від VTP сервера.

3. VTP Transparent – ​​прозорий режим. У цьому режимі комутатор не застосовує конфігурацію VLAN від VTP сервера і не сповіщає про свою конфігурацію інші комутатори, але дозволяє пропускати через свої транкові порти VTP сповіщення від інших комутаторів.

Квітування

В рамках з'єднання правильність передачі кожного сегмента має підтверджуватись квитанцією одержувача. Квітування- це один із традиційних методів забезпечення надійного зв'язку. Ідея квитування полягає в наступному.

Для того, щоб можна було організувати повторну передачу спотворених даних відправник нумерує одиниці, що відправляються даних (далі для простоти звані кадрами). Для кожного кадру відправник очікує від приймача так звану позитивну квитанцію - службове повідомлення, що повідомляє, що вихідний кадр був отриманий і дані в ньому виявилися коректними. Час цього очікування обмежений - при відправленні кожного кадру передавач запускає таймер, і якщо після його закінчення позитивна квитанція на отримана, то кадр вважається втраченим. У деяких протоколах приймач, у разі отримання кадру зі спотвореними даними, повинен відправити негативну квитанцію - явна вказівка ​​того, що цей кадр потрібно передати повторно.


Функції транспортного рівня

  • забезпечує логічне з'єднання між додатками;
  • реалізує надійну передачу даних;
  • забезпечує контроль швидкості передачі.

Сокети

Сокет(Socket, гніздо) - це структура даних, що ідентифікує мережне з'єднання.

Навіщо потрібні сокети? Сервер (програма) може одночасно підтримувати кілька TCP-з'єднань з іншими комп'ютерами, використовуючи той самий стандартний номерпорту. Як це реалізувати? Можна покласти це завдання на програміста. Нехай він вибирає з буфера прийому мережного рівня пакети, дивиться від кого вони надіслані та відповідає відповідним чином. Але можна зробити це зручніше.

З кожним з'єднанням повинен бути пов'язаний свій потік, в який можна писати інформацію і з якого її можна зчитувати. Кожному потоку відповідає свій IP-адреса віддаленого комп'ютера та свій порт віддаленого комп'ютера. Будемо назвати структуру даних, що відповідає кожному такому потоку, сокетом (розеткою). Таким чином сервер можна порівняти з розгалужувачем з купою розеток, до яких підключені клієнти.

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

Сокети були розроблені в університеті Каліфорнії в місті Berkeley, стали стандартом де-факто на противагу OSI TLI (Transport Layer Interface).

Історична довідка. Розкол UNIX

З 1978 починає свою історію BSD UNIX, створений в університеті Берклі. Автором BSD був Білл Джой. На початку 1980-х компанія AT&T, якій належали Bell Labs, усвідомила цінність UNIX та розпочала створення комерційної версії UNIX. Важливою причиною розколу UNIX стала реалізація 1980 р. стеку протоколів TCP/IP. До цього міжмашинна взаємодія в UNIX перебувала в зародковому стані - найбільш істотним способом зв'язку був UUCP (засіб копіювання файлів з однієї UNIX-системи в іншу, що спочатку працювало за телефонним мережамза допомогою модемів).

Ці дві операційні системи реалізували два різні інтерфейси програмування мережевих додатків: Berkley sockets (TCP/IP) та інтерфейс транспортного рівня TLI (OSI ISO) (англ. Transport Layer Interface). Інтерфейс Berkley sockets був розроблений в університеті Берклі і використав стек протоколів TCP/IP, розроблений там же. TLI був створений AT&T відповідно до визначення транспортного рівня моделі OSI. Спочатку у ній був реалізації TCP/IP чи інших мережевих протоколів, але такі реалізації надавалися сторонніми фірмами. Це, як і інші міркування (здебільшого, ринкові), викликало остаточне розмежування між двома гілками UNIX – BSD (університету Берклі) та System V (комерційна версія від AT&T). Згодом багато компаній, ліцензувавши System V у AT&T, розробили власні комерційні різновиди UNIX, такі як AIX, HP-UX, IRIX, Solaris.

Примітиви сокетів

SOCKET створити новий (порожній) сокет
BIND сервер пов'язує свою локальну адресу (порт) із сокетом
LISTEN сервер виділяє пам'ять під чергу підключень клієнтів (TCP)
ACCEPT сервер очікує на підключення клієнта або приймає перше підключення з черги (TCP). Щоб заблокувати очікування вхідних з'єднань, сервер виконує примітив ACCEPT. Отримавши запит з'єднання, транспортний модуль ОС створює новий сокет з тими самими властивостями, що у вихідного сокету, і повертає описувач файлу для нього. Після цього сервер може розгалужити процес або потік, щоб обробити з'єднання для нового сокету та паралельно чекати наступного з'єднання для оригінального сокету
CONNECT клієнт запитує з'єднання (TCP)
SEND/SEND_TO надіслати дані (TCP/UDP)
RECEIVE / RECEIVE_FROM отримати дані (TCP/UDP)
DISCONNECT запит роз'єднання (TCP)

Мультиплексування та демультиплексування

Мультиплексування- збирання повідомлень від сокетів усіх додатків та додавання заголовків.

Демультиплексування- розподіл даних по сокетах.

Для UDP потрібний сокет визначається номером порту одержувача, для TCP - номером порту одержувача, IP-адресою та номером порту відправника.

Протоколи транспортного рівня

На транспортному рівні діє два протоколи: TCP (надійний) та UDP (ненадійний).

Протокол UDP

UDP (User Datagram Protocol) виконує мінімум дій, дозволяючи програмі майже безпосередньо працювати з мережевим рівнем. Працює набагато швидше за TCP, тому що не потрібно встановлювати з'єднання і очікувати підтвердження доставки. Можливі втрати сегментів. Здійснює контроль коректності даних, що передаються (контрольна сума).

Структура UDP-сегменту

Заголовок всього 8 байт.

Принципи надійної передачі даних

Спроектуємо протокол myTCP, послідовно його ускладнюючи.

  • Стан протоколу myTCP 1.0. Передача абсолютно надійним каналом
  • Стан протоколу myTCP 1.0 (відправник) (передача абсолютно надійним каналом).JPG

    Відправник

    Стан протоколу myTCP 1.0 (одержувач) (передача абсолютно надійним каналом).JPG

    Одержувач

  • Стан протоколу myTCP 2.0. Передача каналом, що допускає спотворення бітів. Втрати пакетів неможливі
  • Стан протоколу myTCP 2.0 (відправник) (передача каналом, що допускає спотворення бітів. Втрати пакетів неможливі).JPG

    Відправник

    Стан протоколу myTCP 2.0 (одержувач) (передача каналом, що допускає спотворення бітів. Втрати пакетів неможливі).JPG

    Одержувач

Але квитанції також можуть губитися. Якщо квитанція спотворена відправник знову надсилає пакет. Одержувач повинен думати, як обробляти повторні пакети (потрібно ввести новий стан - передали минулий пакет додатку чи ні).

Роль ідентифікаторів «повторний» і «новий» у TCP/IP відіграють номери пакетів (бо пакети ще можуть губитися).

  • Стан протоколу myTCP 2.1. Передача каналом, що допускає спотворення бітів. Втрати пакетів неможливі
  • Стан протоколу myTCP 2.1 (відправник) (передача каналом, що допускає спотворення бітів. Втрати пакетів неможливі).JPG

    Відправник

    Стан протоколу myTCP 2.1 (одержувач) (передача каналом, що допускає спотворення бітів. Втрати пакетів неможливі).JPG

    Одержувач

Головна різниця між станами одержувача полягає в тому, як обробляються повторні пакети. У стані «Минулий пакет передано додатку» ми викидаємо повторні пакети, а в стані «Минулий пакет не був переданий додатком» ми їх приймаємо та передаємо додатку.

Тепер настав час згадати, що пакети можуть губитися.

  • Потрібно вміти визначати факт втрати пакета, наприклад, засікати час після того, як надіслано пакет.
  • Потрібно нумерувати пакети.
  • У квитанціях потрібно вказувати номер пакета, на який вона надіслана.

Таким чином, ми приходимо до потреби таймера. У випадку, якщо пройшов певний час і підтвердження не прийшло, то здійснюється повторне відправлення повідомлення. Інтервал часу – невеликий т.к. Можливість втрати приймається близькою до 1 (це дійсно так навіть для хорошого WiFi-з'єднання).

Недоліки протоколів з очікуванням на підтвердження

Розглянемо приклад. Нехай є 1Гб-канал Ростов – Москва. Порахуємо час відправлення 1000 байт (або 8000 біт):

8000 біт/1 Гб/с = 8 мкс

Час розповсюдження сигналу:

1000 км/300 000 км/с = 3333 мкс

Разом: наступні 1000 байт будуть надіслані більш ніж через 6674 мкс.

Висновок: 99,9% часу канал не використовує.

Шлях вирішення – збільшити розмір пакету. Але якщо хоча б 1 біт спотвориться, то весь пакет викинуть. Що тоді?

Протоколи ковзного вікна

Вирішення проблеми: дозволити відправнику надсилати не один кадр, а кілька перш ніж зупинитися і перейти в режим очікування підтверджень (квитанцій). Така техніка називається конвеєрною обробкою.

На малюнку зеленим позначені ті квитанції, які вже отримані, жовтим – відправлені, але не отримані, блакитні підготовлені до відправки, а білі не можна відправляти, доки отримаємо квитанції на жовті. Вікно: жовті та блакитні – це пакети, які можуть бути передані без очікування квитанцій. Перший білий пакет може бути відправлений лише після того, як отримали підтвердження першого жовтого. Тоді вікно рухається на 1 праворуч.

Може виникнути питання: навіщо обмежувати розмір вікна, давайте всі пакети передамо, а потім чекатимемо на підтвердження. Але так робити не можна: легко отримати навантаження в мережі.

Є два способи вирішити проблеми виникнення помилок під час конвеєризації кадрів:

  • GBN (Go Back N – повернення на N пакетів назад);
  • SR (Selective Repeat – вибіркове повторення).
GBN

Одержувач надсилає лише позитивні квитанції і лише про отримання тих пакетів, для яких виконується умова: всі пакети з меншими номерами вже отримані. Таким чином, тут використовується групове квитування: одержання відправником квитанції з номером i означає, що всі пакети до i включно доставлені успішно. Якщо через деякий час відправник не отримує квитанції, він повторює відправлення всіх N пакетів починаючи з наступного за останнім квитованим.

Метод GBN неефективний при великому вікні та довгому поширенні пакетів по мережі, в якій трапляються втрати. Приклад: відправили 1000 пакетів, другий не прийшов, доводиться повторювати надсилання всіх, починаючи з другого. Ми засмічуємо мережу марним трафіком.

SR

Цей підхід передбачає надсилання квитанції на кожен пакет. Одержувач зберігає у своєму буфері всі правильні кадри, прийняті після невірного чи втраченого. При цьому неправильний кадр відкидається. Якщо час очікування квитанції на якийсь кадр спливає, відправник знову відправляє цей кадр, не повторюючи відправлення всіх наступних. Якщо друга спроба буде успішною, пакети, що накопичилися в одержувача, можуть бути передані на мережевий рівень, після чого буде надіслано підтвердження отримання кадру з найбільшим номером.

Часто вибірковий метод комбінують з відправкою одержувачем "негативного підтвердження" (NAK - Negative Acknowledgement) при виявленні помилки (наприклад, при неправильній контрольній сумі). У цьому ефективність роботи підвищується.

При великому вікні підхід SR може потребувати значного розміру буфера.

Протокол TCP

Формат TCP-сегменту

TCP-сегмент складається з поля даних та кількох полів заголовка. Поле данихмістить фрагмент даних, що передаються між процесами. Розмір поля даних обмежується величиною MSS(Максимальний розмір сегмента). Коли протокол здійснює передачу великого файлу, він зазвичай розбиває дані на фрагменти розміром MSS (крім останнього фрагмента, який зазвичай має менший розмір). Інтерактивні програми, навпаки, часто обмінюються даними, обсяг яких значно менший за MSS. Наприклад, програми віддаленого доступу до мережі, подібні до Telnet, можуть передати транспортному рівню 1 байт даних. Оскільки зазвичай довжина заголовка ТСР-сегмента становить 20 байт (що на 12 байт більше, ніж у UDP), повний розмір сегмента в цьому випадку дорівнює 21 байт.

Як і в протоколі UDP, заголовок включає номери портів відправника та одержувача, призначені для процедур мультиплексування та демультиплексування даних, а також поле контрольної суми. Крім того, до складу TCP-сегменту входять деякі поля.

  • 32-розрядні поля порядкового номера та номери підтвердження. Потрібні для надійної передачі даних.
  • 4-розрядне поле довжини заголовка визначає довжину TCP-заголовка у 32-розрядних словах. Мінімальний розмір становить 5 слів, а максимальний – 15, що становить 20 та 60 байт відповідно. TCP-заголовок може мати змінну довжину завдяки полю параметрів, описаному нижче (зазвичай поле параметрів є порожнім; це означає, що довжина заголовка становить 20 байт).
  • Поле прапори складається з 6 біт. Біт підтвердження (АБК) вказує на те, що значення, що міститься в квитанції, є коректним. Біти RST, SYN та FIN використовуються для встановлення та завершення з'єднання. Встановлений біт PSH інструктує одержувача проштовхнути дані, що накопичилися в приймальному буфері, додаток користувача. Біт URG показує, що в сегменті знаходяться дані, розміщені верхнім рівнем як термінові. Розташування останнього байта термінових даних вказано у 16-розрядному полі покажчика термінових даних. На стороні, що приймає, протокол TCP повинен повідомити верхній рівень про наявність термінових даних в сегменті і передати йому покажчик на кінець цих даних. На практиці прапори PSH, URG та поле покажчика термінових даних не використовуються. Ми згадали їх лише повноти описи.
  • 16-розрядне вікно прийому використовується управління потоком даних. Воно містить кількість байтів, яке здатна прийняти сторона, що приймає.
  • Вказівник важливості - 16-бітове значення позитивного усунення від порядкового номера в даному сегменті. Це поле вказує порядковий номер октету, яким закінчуються важливі (urgent) дані. Поле береться до уваги лише для пакетів із встановленим прапором URG.
  • Необов'язкове поле параметрів використовується у випадках, коли сторона, що передає і приймає, «домовляються» про максимальний розмір сегмента, або для масштабування вікна у високошвидкісних мережах. Також у цьому полі визначається параметр тимчасових міток. Додаткову інформацію можна знайти у документах RFC 854 та RFC 1323 .
Порядкові номери та номери підтвердження

Порядковий номер сегмента- Це номер першого байта цього сегмента.

Номер підтвердження- Це порядковий номер наступного очікуваного байта.

Поля порядкового номера та номера підтвердження є найважливішими у заголовку TCP-сегмента, оскільки відіграють ключову роль у функціонуванні служби надійної передачі даних. Однак перед тим, як розглядати роль цих полів у механізмі надійної передачі, звернемося до величин, які протокол TCP містить у ці поля.

Протокол TCP розглядає дані як неструктурований впорядкований потік байтів. Такий підхід проявляється в тому, що TCP призначає порядкові номери не сегментам, а кожному байту, що передається. Виходячи з цього, порядковий номер сегмента визначається як порядковий номер першого байта цього сегмента. Розглянемо наступний приклад. Нехай хост А хоче переслати потік даних хосту через TCP-з'єднання. Протокол TCP на передавальній стороні неявно нумерує кожен байт потоку. Нехай розмір файлу, що передається, становить 500 000 байт, величина MSS дорівнює 1000 байт, і перший байт потоку має порядковий номер 0. TCP розбиває потік даних на 500 сегментів. Першому сегменту надається порядковий номер 0, другому сегменту - номер 1000, третьому сегменту - номер 2000, і т. д. Порядкові номери заносяться в поля порядкових номерів кожного TCP-сегмента.

Тепер розглянемо номери підтвердження. Згадаймо про те, що протокол TCP забезпечує дуплексну передачу даних, тобто через єдине TCP-з'єднання дані між хостами А і можуть передаватися одночасно в обидві сторони. Кожен сегмент, що виходить з хоста, містить порядковий номер даних, що передаються від хоста В до хоста А. Номер підтвердження, який хост А поміщає в свій сегмент, - це порядковий номер наступного байта, очікуваного хостом А від хоста В. Розглянемо наступний приклад. Припустимо, що хост А отримав всі байти з номерами від 0 до 535, надіслані хостом, і формує сегмент для передачі хосту В. 536 у полі номера підтвердження свого сегмента.

Розглянемо іншу ситуацію. Нехай хост А отримав від хоста У два сегменти, у першому з яких містяться байти з номерами від 0 до 535, а в другому - байти з номерами від 900 до 1000. Це означає, що з будь-якої причини байти з номерами від 536 до 899 не були отримані хостом А. У цьому випадку хост А очікує появи відсутніх байтів і в полі номера підтвердження свого сегмента поміщає число 536. Оскільки TCP квитує прийняті дані до першого відсутнього байта, кажуть, що він підтримує загальне квитування.

Останній приклад показує дуже важливий аспект функціонування протоколу TCP. Третій сегмент (що містить байти 900-1000) прийнятий хостом А раніше, ніж другий (що містить байти 536-899), тобто з порушенням порядку слідування даних. Виникає питання: як протокол TCP реагує порушення порядку? Якщо отриманий сегмент містить номер послідовності більший, ніж очікуваний, дані з сегмента буферизується, але номер підтвердженої послідовності не змінюється. Якщо згодом буде прийнятий сегмент, що відноситься до очікуваного номеру послідовності, то порядок даних буде автоматично відновлено, виходячи з номерів послідовностей у сегментах. Таким чином TCP відноситься до протоколів SR, але в нього використовується загальне квитування як GBN. Хоча SR – не зовсім чисте. Якщо сторона отримує кілька (3) негативних квитанцій на один і той же сегмент x, то вона здогадується, що відбулося навантаження мережі і сегменти x +1, x +2, x +3, ... теж не були доставлені. Тоді надсилається вся серія з x – як у протоколах GBN.

Проблеми з максимальним розміром сегмента

TCP вимагає явної вказівки максимального розміру сегмента у разі, якщо віртуальне з'єднання здійснюється через сегмент мережі, де максимальний розмір блоку (MTU) менш ніж стандартний MTU Ethernet (1500 байт). У протоколах тунелювання, таких як GRE, IPIP, а також PPPoE MTU тунелю менше стандартного, тому сегмент TCP максимального розміру має довжину пакета більше, ніж MTU. Оскільки фрагментація в переважній більшості випадків заборонена, такі пакети відкидаються.

Прояв цієї проблеми виглядає як «зависання» з'єднань. При цьому «зависання» може відбуватися в довільні моменти часу, а саме тоді, коли відправник використовував сегменти довші за допустимий розмір. Для вирішення цієї проблеми на маршрутизаторах застосовуються правила Firewall-а, що додають параметр MSS у всі пакети, що ініціюють з'єднання, щоб відправник використовував сегменти допустимого розміру. MSS може також управлятися параметрами операційної системи.

Потрійне рукостискання

Щоб встановити з'єднання, хост 2 пасивно очікує вхідного з'єднання, виконуючи примітив ACCEPT.

Хост 2 виконує примітив CONNECT, вказуючи IP-адресу і порт, з яким він хоче встановити з'єднання, максимальний розмір TCP-сегменту і т.п. АСК=0 і чекає на відповідь. Таким чином, хост повідомляє порядковий номер x послідовності бітів від хоста 1 до 2.

Хост 2 посилає у відповідь підтвердження "Connection accepted" (функція accept). Послідовність TCP-сегментів, що посилаються в нормальному випадку, показана на рис: SYN=1 ASK=1, хост 2 повідомляє порядковий номер x послідовності бітів від хоста 2 до 1 і повідомляє, що він очікує продовження даних, починаючи з байта № x+1.

Хост 1 (Connect) надсилає підтвердження про отримання згоди на встановлення з'єднання.

Боротьба з перевантаженням у TCP

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

Теоретично з перевантаженням можна боротися за допомогою принципу, запозиченого з фізики, - закону збереження пакетів. Ідея полягає в тому, щоб не передавати в мережу нові пакети, доки її не покинуть (тобто не будуть доставлені) старі. Протокол TCP намагається досягти цієї мети за допомогою динамічного керування розміром вікна.

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

В даний час втрати пакетів при передачі трапляються відносно рідко, так як більшість міжміських ліній зв'язку є оптоволоконними (хоча в бездротових мережахвідсоток пакетів, що втрачаються через перешкоди, досить високий). Відповідно, більшість втрачених пакетів в Інтернеті викликані заторами. Всі TCP-алгоритми Інтернету припускають, що втрати пакетів викликаються навантаженням мережі, і стежать за тайм-аутами як за провісниками проблем.

Перш ніж перейти до обговорення того, як TCP реагує на навантаження, опишемо спочатку способи запобігання, що застосовуються протоколом. При виявленні навантаження має бути обраний відповідний розмір вікна. Одержувач може вказати розмір вікна, виходячи із кількості вільного місцяу буфері. Якщо відправник матиме на увазі розмір відведеного вікна, переповнення буфера в одержувача не зможе стати причиною проблеми, однак вона все одно може виникнути через перевантаження на будь-якій ділянці мережі між відправником і одержувачем.

Боротьба з перевантаженням у TCP

Проілюструємо цю проблему з прикладу водопроводу. На малюнку ми бачимо товсту трубу, що веде до одержувача з невеликою ємністю. Доки відправник не посилає води більше, ніж може поміститися у відро, вода не буде проливатися, на малюнку б обмежувальним фактором є не ємність відра, а пропускна здатність мережі. Якщо з крана у вирву вода литиметься дуже швидко, то рівень води у вирві почне підніматися і, зрештою, частина води може перелитися через край вирви.

Рішення, що застосовується в Інтернеті, полягає у визнанні існування двох потенційних проблем: низької пропускної спроможності мережі та низької ємності одержувача – та у роздільному вирішенні обох проблем. Для цього кожен відправник має два вікна: вікно, надане одержувачем, і вікно перевантаження. Розмір кожного їх відповідає кількості байтів, яке відправник має право передати. Відправник керується мінімальним із цих двох значень. Наприклад, одержувач каже: «Посилайте 8 Кбайт», але відправник знає, що якщо він надішле більше 4 Кбайт, то в мережі утворюється затор, тому він посилає все-таки 4 Кбайт. Якщо ж відправник знає, що мережа здатна пропустити та Велика кількістьданих, наприклад 32 Кбайт, він передасть стільки, скільки просить одержувач (тобто 8 Кбайт).

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

Цей процес експоненціального зростання триває доти, доки не буде досягнуто розміру вікна одержувача або не буде вироблено ознаку тайм-ауту, що сигналізує про перевантаження в мережі. Наприклад, якщо пакети розміром 1024, 2048 та 4096 байт дійшли до одержувача успішно, а у відповідь на передачу пакета розміром 8192 байта підтвердження не надійшло у встановлений термін, вікно навантаження встановлюється рівним 4096 байтам. Поки розмір вікна навантаження залишається рівним 4096 байтам, більш довгі пакети не надсилаються, незалежно від розміру вікна, що надається одержувачем. Цей алгоритм називається затяжним пуском, або повільним пуском. Однак він не такий вже й повільний (Jacobson, 1988). Він експонентний. Усі реалізації протоколу TCP повинні його підтримувати.

Розглянемо тепер механізм боротьби з навантаженням, який використовується в Інтернеті. Крім вікон одержувача та перевантаження, як третій параметр у ньому використовується граничне значення, яке спочатку встановлюється рівним 64 Кбайт. Коли виникає ситуація тайм-ауту (підтвердження не повертається в строк), нове значення порога встановлюється рівним половині поточного розміру вікна навантаження, а вікно навантаження зменшується до розміру одного максимального сегмента. Потім, як і у попередньому випадку, використовується алгоритм затяжного пуску, що дозволяє швидко виявити межу пропускної спроможності мережі. Однак цього разу експонентне зростання розміру вікна зупиняється після досягнення ним порогового значення, після чого вікно збільшується лінійно, на один сегмент для кожної наступної передачі. По суті, передбачається, що можна спокійно урізати вдвічі розмір вікна навантаження, після чого поступово нарощувати його.

Механізми надійної передачі. Узагальнення

Контрольна сума Виявлення спотворень бітів у прийнятому пакеті
Таймер Відлік інтервалу очікування та вказівку на його закінчення. Останнє означає, що з високим ступенем ймовірності пакет або квитанція втрачені при передачі. У разі, якщо пакет доставляється із затримкою, але не втрачається (передчасне закінчення інтервалу очікування), або відбувається втрата квитанції, повторна передача призводить до дублювання пакета на стороні, що приймає.
Порядкові номери Послідовна нумерація пакетів, що надсилаються передавальною стороною. «Пробіли» в номерах пакетів, що отримуються, дозволяють зробити висновок про втрати даних. Одинакові порядкові номери пакетів означають, що пакети дублюють один одного
«+» та «-» квитанції Генерується стороною, що приймає, і вказує передавальної стороні на те, що відповідний пакет або група пакетів були або не були прийняті. Зазвичай, підтвердження містить порядкові номери успішно прийнятих пакетів. Залежно від протоколу розрізняють індивідуальні та групові підтвердження
Вікно/конвеєр Обмежують діапазон порядкових номерів, які можна використовувати передачі пакетів. Групова передача та квитування дозволяють значно збільшити пропускну здатністьпротоколів у порівнянні з режимом очікування підтвердження. Розмір вікна може бути розрахований на основі можливостей прийому та буферизації приймаючої сторони, а також рівня завантаження мережі

Особливості програмування

  1. Потокове з'єднання TCP
    • ситуація а) можлива за поганого зв'язкуякщо проміжок часу між приходами груп дейтаграм мережного рівня великий:
      • комп'ютер1 один раз використовує функцію send;
      • комп'ютер2 не отримує всю інформацію за один виклик recv (потрібно кілька викликів).
    • ситуація б) можлива, якщо інтервал часу між викликами функції send малий та розмір даних малий:
      • комп'ютер1 використовує функцію send кілька разів;
      • комп'ютер2 отримує всю інформацію за один виклик recv.
  2. За протоколом UDP
    • ситуація а) - неможлива
      • комп'ютер1 один раз використовує функцію send, на мережному рівні UDP-сегмент розбивається на кілька пакетів;
      • комп'ютер2 отримує сегмент завжди одним викликом recv і тільки якщо прийшли всі IP-дейтаграми.
    • ситуація б) - неможлива
      • різні виклики функції sendto на комп'ютері1 відповідають різним UDP-датаграмам і різним викликам recvfrom на комп'ютері2.
  3. Якщо буфер у функціях recv та recvfrom менший, ніж розмір надісланих даних, то у випадку UDP частина даних втрачається, а у випадку TCP – залишок зберігається для наступного виклику recv.
  4. UDP-сервер має 1 сокет, а TCP-сервер має багато різних сокетів (за кількістю одночасно підключених клієнтів) і кожному передається своя інформація.

© 2022 androidas.ru - Все про Android