Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Володин В.Е. TCP.docx
Скачиваний:
13
Добавлен:
11.03.2016
Размер:
401.02 Кб
Скачать

Флаги (управляющие биты)

Это поле содержит 6 битовых флагов:

  • URG — поле «Указатель важности» задействовано (англ. Urgent pointer field is significant)

  • ACK — поле «Номер подтверждения» задействовано (англ. Acknowledgement field is significant)

  • PSH — (англ. Push function) инструктирует получателя протолкнуть данные, накопившиеся в приемном буфере, в приложение пользователя

  • RST — оборвать соединения, сбросить буфер (очистка буфера) (англ. Reset the connection)

  • SYN — синхронизация номеров последовательности (англ. Synchronize sequence numbers)

  • FIN (англ. final, бит) — флаг, будучи установлен, указывает на завершение соединения (англ. FIN bit used for connection termination).

Размер окна

В этом поле содержится число, определяющее в байтах размер данных, которые отправитель может отправить без получения подтверждения.

Контрольная сумма

Поле контрольной суммы — это 16-битное дополнение к сумме всех 16-битных слов заголовка(включая псевдозаголовок) и данных. Если сегмент, по которому вычисляется контрольная сумма, имеет длину не кратную 16-ти битам, то длина сегмента увеличивается до кратной 16-ти, за счет дополнения к нему справа нулевых битов заполнения. Биты заполнения (0) не передаются в сообщении и служат только для расчёта контрольной суммы. При расчёте контрольной суммы значение самого поля контрольной суммы принимается равным 0.

Указатель важности

16-битовое значение положительного смещения от порядкового номера в данном сегменте. Это поле указывает порядковый номер октета, которым заканчиваются важные (urgent) данные. Поле принимается во внимание только для пакетов с установленным флагом URG.

Описание структуры псевдозаголовка.

TCP-заголовок не содержит информации об адресе отправителя и получателя, поэтому даже при совпадении порта получателя нельзя с точностью сказать, что сообщение пришло в нужное место. Поскольку назначением протокола TCP является надёжная доставка сообщений, то этот момент имеет принципиальное значение. Эту задачу можно было решить разными способами. Самый очевидный — добавить информацию об адресе назначения в заголовок TCP, однако это, во-первых, приводит к дублированию информации, что снижает долю полезной информации переносимой TCP-сегментом, а во-вторых, нарушает принцип инкапсуляции модели OSI. Поэтому разработчики протокола пошли другим путём и использовали дополнительный псевдозаголовок:

TCP-псевдозаголовок IPv4

Биты

0

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

0-31

IP-адрес отправителя (Source address)

32-63

IP-адрес получателя (Destination address)

64-95

0

0

0

0

0

0

0

0

Протокол (Protocol)

Длина TCP-сегмента (TCP length)

Диаграмма состояний TCP соединения.

Временная диаграмма установления TCP соединения.

Временная диаграмма завершения TCP соединения.

Временная диаграмма обмена данными.

Описание алгоритма подсчёта контрольной суммы TCP сегмента.

В общих чертах алгоритм расчета контрольной суммы очень прост:

  1. Соседние октеты информации, для которой создается контрольная сумма, объединяются в 16-битовые целые числа и для этих чисел формируется 16-битовое поразрядное дополнение до 1.

  2. При расчете контрольной суммы значение самого поля контрольной суммы принимается нулевым. Для 16-битовых поразрядных дополнений вычисляется сумма. Для полученного в результате 16-битового целого числа создается 16-битовое поразрядное дополнение до 1 и помещается в поле контрольной суммы.

  3. Для проверки контрольной суммы вычисляется сумма поразрядных дополнений до 1 для того же набора октетов, включая поле контрольной суммы. Если в результате получается значение, все биты которого равны 1 (-0 в арифметике дополнений до 1), это говорит о корректности контрольной суммы.

Временная диаграмма одновременного завершения TCP соединения.

Исходный код и результаты работы программы.

Main.cpp

#include "buffer.h"

#include "TcpSegment.h"

#include <conio.h>

void const GetTcpHeader(TcpSegment &head);

void GetTcpData(TcpSegment &head);

void SetTcpHeader(TcpSegment &head);

void ContBits(byte contbits);

void Reserved(uint16_t reserved);

void Comparison(TcpSegment &head);

void main()

{

cout << "Hex Data Vector:\n";

shared_ptr <vector<byte>> v(new vector < byte >({

/*0x45, 0x00, 0x01, 0x43, 0x00, 0x14, 0x40, 0x00, 0x80, 0x06,

0x8f, 0xdf, 0x0a, 0x00, 0x02, 0x69, 0xd5, 0xdb, 0x87, 0x7d,*/

0xc0, 0x06, 0x0f, 0x42, 0x43, 0x51, 0xbb, 0x09, 0x00, 0x00,

0xfa, 0x02, 0x50, 0x18, 0xfa, 0xf0, 0x32, 0x2b, 0x00, 0x00 }));

for (unsigned int i = 0; i < v->size(); i++)

cout << hex << "0x" << (int)*(v->data() + i) << " ";

buffer buf(v);

try

{

buffer buf(v);

}

catch (exception &e)

{

cout << "NULLPTR";

}

TcpSegment TcpHead(buf);

int ans = 0;

do {

cout << "\n\nWhat do you want to do?: \n";

cout << "1. Get Header;\n2. Set Header;\n3. Get Data;\n4. Wireshark vs. C++;\n0. Quit Programm.\n\n";

cout << "Your choice: ";

cin >> ans;

switch (ans)

{

case 1: GetTcpHeader(TcpHead); break;

case 2: SetTcpHeader(TcpHead); break;

case 3: GetTcpData(TcpHead); break;

case 4: Comparison(TcpHead); break;

case 0: break;

}

} while (ans != 0);

}

void Reserved(uint16_t reserved)

{

char name1[][10] = { "Not Set", "Set" };

char name2[][10] = { "Not Set", "Set" };

char name3[][10] = { "Not Set", "Set" };

char name4[][10] = { "Not Set", "Set" };

int bits = ((reserved >> 9) & 111); //0000111111000000 => 0000000000000111

cout << "Reserved: " << name1[bits] << endl;

bits = ((reserved >> 8) & 1); //0000111111000000 => 0000000000001111

cout << "Nonce: " << name2[bits] << endl;

bits = ((reserved >> 7) & 1);

cout << "CWR: " << name3[bits] << endl;

bits = ((reserved >> 6) & 1);

cout << "ECN-Echo: " << name4[bits] << endl;

}

void ContBits(byte contbits)

{

char name1[][10] = { "Not Set", "Set" };

char name2[][10] = { "Not Set", "Set" };

char name3[][10] = { "Not Set", "Set" };

char name4[][10] = { "Not Set", "Set" };

char name5[][10] = { "Not Set", "Set" };

char name6[][10] = { "Not Set", "Set" };

int bits = ((contbits >> 5) & 1); //00111111 => 00000001

cout << "URG: " << name1[bits] << endl;

bits = ((contbits >> 4) & 1); //00111111 => 00000011

cout << "ACK: " << name2[bits] << endl;

bits = ((contbits >> 3) & 1);

cout << "PSH: " << name3[bits] << endl;

bits = ((contbits >> 2) & 1);

cout << "RST: " << name4[bits] << endl;

bits = ((contbits >> 1) & 1);

cout << "SYN: " << name5[bits] << endl;

bits = (contbits & 1);

cout << "FIN: " << name6[bits] << endl;

}

void const GetTcpHeader(TcpSegment &head)

{

int ans = 0;

start:

cout << "\nWhat header field do you want to see?: \n";

cout << "1. Source port;\n2. Destination port;\n3. Sequence number;\n4. Acknowledgement number;\n5. Data offset;\n6. Reserved;\n7. Control bits;\n";

cout << "8. Window;\n9. Checksum;\n10. Urgent pointer;\n0. Return to menu\n ";

do {

cout << "\nYour choice : ";

cin >> ans;

switch (ans) {

case 1: cout << "Source port: " << endl << dec << (int)head.GetSourcePort() << endl;; break;

case 2: cout << "Destination port: " << endl << dec << (int)head.GetDestinationPort() << endl;; break;

case 3: cout << "Sequence number: " << endl << hex << (int)head.GetSequenceNumber() << endl; break;

case 4: cout << "Acknowledgement number: " << endl << hex << (int)head.GetAckNumber() << endl; break;

case 5: cout << "Data Offset: " << endl << dec << (int)head.GetOffset() << " bytes" << endl; break;

case 6: cout << "Reserved: \n"; Reserved(head.GetReserved()); break;

case 7: cout << "Control Bits: \n"; ContBits(head.GetContBits()); break;

case 8: cout << "Window: " << endl << dec << (int)head.GetWindow() << endl; break;

case 9: cout << "Checksum: " << endl << hex << "0x" << (int)head.GetChecksum() << endl; break;

case 10: cout << "Urgent pointer: " << endl << dec << (int)head.GetUrgPointer() << endl; break;

default: cout << "ERROR\n"; goto start;

case 0: break;

}

} while (ans != 0);

}

void SetTcpHeader(TcpSegment &head)

{

int ans = 0;

start:

cout << "\nWhat header field do you want to set?: \n";

cout << "1. Source port;\n2. Destination port;\n3. Sequence number;\n4. Acknowledgement number;\n5. Data offset;\n6. Reserved;\n7. Control bits;\n";

cout << "8. Window;\n9. Checksum;\n10. Urgent pointer;\n0. Return to menu\n ";

do {

cout << "\nYour choice : ";

cin >> ans;

switch (ans) {

case 1:

{

int i = 0;

cout << endl << "Source port - header's bytes 1 & 2: enter the number [1..65 535] : ";

do {

cin >> i;

if (i <= 0 || i > 65535)

cout << "\nERROR: Enter the number [1..65 535] : ";

} while (i <= 0 || i > 15);

head.SetSourcePort(i);

break;

}

case 2:

{

int i = 0;

cout << endl << "Destination porth - header's bytes 3 & 4: enter the number [1..65 535] : ";

do {

cin >> i;

if (i < 0 || i > 65535)

cout << "\nERROR: Enter the number [1..65 535] : ";

} while (i < 0 || i > 15);

head.SetDestinationPort(i);

break;

}

case 3:

{

uint16_t i = 0;

cout << endl << "Sequence number - header's bytes 5, 6, 7, 8: enter the number [0..65 535] : ";

do {

cin >> i;

if (i < 0 || i > 65535)

cout << "\nERROR: Enter the number [0..65 535] : ";

} while (i < 0 || i > 255);

head.SetSequenceNumber(i);

break;

}

case 4:

{

uint16_t i = 0;

cout << endl << "Acknowledgement number - header's bytes 9, 10, 11, 12: enter the number [0..65 535] : ";

do {

cin >> i;

if (i < 20 || i > 65535)

cout << "\nERROR: Enter the number [0..65 535] : ";

} while (i < 20 || i > 65535);

head.SetAckNumber(i);

break;

}

case 5:

{

uint16_t i = 0;

cout << endl << "Header's offset - 4 bits: enter the number [1..15] : ";

do {

cin >> i;

if (i <= 0 || i > 15)

cout << "\nERROR: Enter the number [1..15] : ";

} while (i <= 0 || i > 15);

head.SetOffset(i);

break;

}

case 6:

{

int i = 0;

head.SetReserved(i);

break;

}

case 7:

{

uint16_t i = 0;

cout << endl << "Control bits - 6 bits: enter the number [15..63] : "; //63 = 00111111

do {

cin >> i;

if (i < 15 || i > 63)

cout << "\nERROR: Enter the number [15..63] : ";

} while (i < 15 || i > 63);

head.SetContBits(i);

break;

}

case 8:

{

int i = 0;

cout << endl << "Window size value - header's bytes 15 & 16: enter the number [0..65 535] : ";

do {

cin >> i;

if (i < 0 || i > 65535)

cout << "\nERROR: Enter the number [0..65 535] : ";

} while (i < 0 || i > 65535);

head.SetWindow(i);

break;

}

case 9:

{

int i = 0;

cout << endl << "Checksum - header's bytes 17 & 18: enter the number [0..65 535] : ";

do {

cin >> i;

if (i < 0 || i > 65535)

cout << "\nERROR: Enter the number [0..65 535] : ";

} while (i < 0 || i > 65535);

head.SetChecksum(i);

break;

}

case 10:

{

uint16_t i = 0;

cout << endl << "Urgent pointer - header's bytes 19 & 20: enter the number [0..65 535] : ";

do {

cin >> i;

if (i < 0 || i > 65535)

cout << "\nERROR: Enter the number [0..65 535] : ";

} while (i < 0 || i > 65535);

head.SetUrgPointer(i);

break;

}

default: cout << endl << "ERROR\n"; goto start;

case 0: break;

}

} while (ans != 0);

}

void GetTcpData(TcpSegment &head)

{

cout << "\nTCP Segment's Data:\n";

buffer data = buffer(head.GetData(head), 20, head.GetData(head).GetLength());

for (int i = 0; i < head.GetData(head).GetLength(); i++)

cout << hex << (int)data[i];

cout << "\n\n";

}

void Comparison(TcpSegment &head)

{

cout << "\t\t\t\tWireshark \tvs. \tC++";

cout << "\nSource port: \t\t\t49158" << dec << "\t\t\t" << (int)head.GetSourcePort() << endl;;

cout << "Destination port: \t\t3906" << dec << "\t\t\t" << (int)head.GetDestinationPort() << endl;;

cout << "Sequence number: \t\t1129429769" << dec << "\t\t" << (int)head.GetSequenceNumber() << endl;;

cout << "Acknowledgement number: \t64002" << dec << "\t\t\t" << (int)head.GetAckNumber() << endl;

cout << "Data offset: \t\t\t20 bytes" << dec << "\t\t\t" << (int)head.GetOffset() << " bytes" << endl;

cout << "Reserved: \t\t\t0" << dec << "\t\t\t" << (int)head.GetReserved() << endl;;

cout << "Control bits: \t\t\t18" << hex << "\t\t\t" << (int)head.GetContBits() << endl;

cout << "Window: \t\t\t64240" << dec << "\t\t\t" << (int)head.GetWindow() << endl;

cout << "Checksum: \t\t\t332b" << hex << "\t\t\t" << (int)head.GetChecksum() << endl;

cout << "Urgent pointer: \t\t0" << dec << "\t\t\t" << (int)head.GetUrgPointer() << endl;

}

Buffer.h

#include <memory>

#include <iostream>

#include <vector>

#include <conio.h>

#include <stdlib.h>

#include <exception>

using namespace std;

typedef uint8_t byte;

/*Класс "Буфер", содержит индекс начала буфера внутри вектора,

длину буфера, умный указатель на вектор.*/

class buffer {

uint32_t m_start;

uint32_t m_length;

shared_ptr <vector<byte>> m_data;

public:

buffer();

buffer(shared_ptr <vector<byte>> data);

buffer(shared_ptr <vector<byte>> data, uint32_t start, uint32_t length);

buffer(buffer b, uint32_t start, uint32_t length);

byte& operator[](uint32_t index);

int const GetLength();

buffer operator=(buffer& b);

};

Buffer.cpp

#include "buffer.h";

#include <exception>

using namespace std;

buffer::buffer()

{

m_start = 0;

m_length = 0;

m_data = 0;

}

buffer::buffer(shared_ptr <vector<byte>> data)

{

if (data == nullptr) throw exception("Error");

m_data = data;

m_start = 0;

m_length = data->size();

}

buffer::buffer(shared_ptr <vector<byte>> data, uint32_t start, uint32_t length)

{

m_data = data;

m_start = start;

m_length = length;

}

buffer::buffer(buffer b, uint32_t start, uint32_t length)

{

m_data = b.m_data;

m_start = start;

m_length = length;

}

byte& buffer::operator[](uint32_t index) { return *(m_data->data() + index + m_start); }

int const buffer::GetLength() { return m_length; }

buffer buffer::operator=(buffer& b)

{

m_data = b.m_data;

m_start = b.m_start;

m_length = b.m_length;

return *this;

}

TCPSegment.h

#include <memory>

#include <iostream>

#include <vector>

#include <conio.h>

#include <stdlib.h>

using namespace std;

typedef uint8_t byte;

/*Класс дэйтаграммы, элементы - 3 разных буфера,

указывающие на один вектор, но на разные его части.*/

class TcpSegment {

buffer data;

buffer header;

buffer option;

public:

TcpSegment(buffer a);

uint16_t const GetSourcePort();

uint16_t const GetDestinationPort();

uint32_t const GetSequenceNumber();

uint32_t const GetAckNumber();

byte const GetOffset();

uint16_t const GetReserved();

byte const GetContBits();

uint16_t const GetWindow();

uint16_t const GetChecksum();

uint16_t const GetUrgPointer();

void SetSourcePort(int i);

void SetDestinationPort(int i);

void SetSequenceNumber(uint16_t i);

void SetAckNumber(uint16_t i);

void SetOffset(uint16_t i);

void SetReserved(int i);

void SetContBits(uint16_t i);

void SetWindow(int i);

void SetChecksum(int i);

void SetUrgPointer(uint16_t i);

buffer TcpSegment::GetData(TcpSegment &head);

};

TCPSegment.cpp

#include "buffer.h"

#include "TcpSegment.h"

TcpSegment::TcpSegment(buffer a)

{

auto len = a[0] & 0x0F;

len *= 4;

auto optionLen = len - 20;

auto totalLen = (a[2] * 0x100) + a[3];

header = buffer(a, 0, 20);

option = buffer(a, 20, optionLen);

data = buffer(a, len, totalLen - len);

}

uint16_t const TcpSegment::GetSourcePort() { return (header[0] * 0x100) + header[1]; }

uint16_t const TcpSegment::GetDestinationPort() { return (header[2] * 0x100) + header[3]; }

uint32_t const TcpSegment::GetSequenceNumber() { return (((header[4] * 0x100) + header[5]) * 0x100 + header[6]) * 0x100 + header[7]; }

uint32_t const TcpSegment::GetAckNumber() { return (((header[8] * 0x100) + header[9]) * 0x100 + header[10]) * 0x100 + header[11]; }

byte const TcpSegment::GetOffset() { return (header[12] >> 4) * 4; } //размер в 4-х байтных словах

uint16_t const TcpSegment::GetReserved() { uint16_t temp = (header[12] * 0x100) + header[13]; return (temp & 0xfc0); } //fc0 = 0000111111000000

byte const TcpSegment::GetContBits() { return (header[13] & 0x3f); } //3f = 00111111 (берем 6 последних битов)

uint16_t const TcpSegment::GetWindow() { return (header[14] * 0x100) + header[15]; }

uint16_t const TcpSegment::GetChecksum() { return (header[16] * 0x100) + header[17]; }

uint16_t const TcpSegment::GetUrgPointer() { return (header[18]) + header[19]; }

void TcpSegment::SetSourcePort(int i)

{

header[0] = header[0] & 0;

header[0] = header[0] | (i >> 8);

header[1] = header[1] & 0;

header[1] = header[1] | (i & 255);

}

void TcpSegment::SetDestinationPort(int i)

{

header[2] = header[2] & 0;

header[2] = header[2] | (i >> 8);

header[3] = header[3] & 0;

header[3] = header[3] | (i & 255);

}

void TcpSegment::SetSequenceNumber(uint16_t i)

{

header[1] = i;

}

void TcpSegment::SetAckNumber(uint16_t i)

{

header[2] = header[2] & 0;

header[2] = header[2] | (i >> 8);

header[3] = header[3] & 0;

header[3] = header[3] | (i & 255);

}

void TcpSegment::SetOffset(uint16_t i)

{

header[12] = header[12] & 15;

header[12] = header[12] | (i << 4);

}

void TcpSegment::SetReserved(int i)

{

cout << "No need in setting Reserved field";

}

void TcpSegment::SetContBits(uint16_t i)

{

header[13] = header[13] & (63 << 2);

header[13] = header[13] | i;

}

void TcpSegment::SetWindow(int i)

{

header[14] = header[14] & 0;

header[14] = header[14] | (i >> 8);

header[15] = header[15] & 0;

header[15] = header[15] | (i & 255);

}

void TcpSegment::SetChecksum(int i)

{

header[16] = header[16] & 0;

header[16] = header[16] | (i >> 8);

header[17] = header[17] & 0;

header[17] = header[17] | (i & 255);

}

void TcpSegment::SetUrgPointer(uint16_t i)

{

header[18] = header[18] & 0;

header[18] = header[18] | (i >> 8);

header[19] = header[19] & 0;

header[19] = header[19] | (i & 255);

}

buffer TcpSegment::GetData(TcpSegment &head) { return data; }

Выводы.

Изучил принцип работы протокола TCP, структуру TCP-сегмента.

Контрольные вопросы

Назначение протокола TCP.

Механизм TCP предоставляет поток данных с предварительной установкой соединения, осуществляет повторный запрос данных в случае потери данных и устраняет дублирование при получении двух копий одного пакета, гарантируя тем самым, в отличие от UDP, целостность передаваемых данных и уведомление отправителя о результатах передачи.

Какова минимальная длина заголовка TCP сегмента в байтах?

16 байт

Какова максимальная длина TCP сегмента?

TCP требует явного указания максимального размера сегмента (MSS) в случае, если виртуальное соединение осуществляется через сегмент сети, где максимальный размер блока (MTU) менее, чем стандартный MTU Ethernet (1500 байт).

В протоколах туннелирования, таких как GRE, IPIP, а также PPPoE MTU туннель меньше, чем стандартный, поэтому сегмент TCP максимального размера имеет длину пакета больше, чем MTU. Это приводит к фрагментации и уменьшению скорости передачи полезных данных. Если на каком-либо узле фрагментация запрещена, то со стороны пользователя это выглядит как «зависание» соединений. При этом «зависание» может происходить в произвольные моменты времени, а именно тогда, когда отправитель использовал сегменты длиннее допустимого размера. Для решения этой проблемы на маршрутизаторах применяются правила Firewall-а, добавляющие параметр MSS во все пакеты, инициирующие соединения, чтобы отправитель использовал сегменты допустимого размера.

Для чего необходимо поле Sequence Number?

Для опознания октета данных

Кем, как и когда выбирается значение Sequence Number?

Программой при установлении соединения выбирается случайное число seq

Для чего необходимо поле Acknowledgment Number?

Для подтверждения посылки данных

Что показывает поле Window?

Количество октетов данных, начиная с октета, чей номер указан в поле подтверждения. Количество октетов, получения которых ждет отправитель настоящего сегмента.

Для чего нужен флаг SYN?

Для чего нужен флаг FIN?

Для чего нужен флаг PSH?

Для чего нужен флаг URG?

Для чего нужен флаг RST?

Что такое сокет?

Название программного интерфейса для обеспечения обмена данными между процессами

Какие таймеры существуют в TCP?

Для взаимного согласования операций в рамках TCP-протокола используется четыре таймера:

  1. Таймер повторных передач (retransmission; RTO) контролирует время прихода подтверждений (ACK). Таймер запускается в момент посылки сегмента. При получении отклика ACK до истечения времени таймера, он сбраcывается. Если же время таймера истекает до прихода ACK, сегмент посылается адресату повторно, а таймер перезапускается.

  2. Таймер запросов (persist timer), контролирующий размер окна даже в случае, когда приемное окно закрыто. При window=0 получатель при изменении ситуации посылает сегмент с ненулевым значением ширины окна, что позволит отправителю возобновить свою работу. Но если этот пакет будет потерян, возникнет тупик, тогда каждая из сторон ждет сигнала от партнера. Именно в этой ситуации и используется таймер запросов. По истечении времени этого таймера отправитель пошлет сегмент адресату. Отклик на этот сегмент будет содержать новое значение ширины окна. Таймер запускается каждый раз, когда получен сегмент с window=0.

  3. Таймер контроля работоспособности (keepalive), который регистрирует факты выхода из строя или перезагрузки ЭВМ-партнеров. Время по умолчанию равно 2 часам. Keepalive-таймер не является частью TCP-спецификации. Таймер полезен для выявления состояний сервера half-open при условии, что клиент отключился (например, пользователь выключил свою персональную ЭВМ, не выполнив LOGOUT). По истечении времени таймера клиенту посылается сегмент проверки состояния. Если в течение 75 секунд будет получен отклик, сервер повторяет запрос 10 раз с периодом 75 сек, после чего соединение разрывается. При получении любого сегмента от клиента таймер сбрасывается и запускается вновь.

  4. 2MSL-таймер (Maximum Segment Lifetime) контролирует время пребывания канала в состоянии TIME_WAIT. Выдержка таймера по умолчанию равно 2 мин (FIN_WAIT-таймер). См. рис. 4.4.3.4. и RFC-793. Таймер запускается при выполнении процедуры active close в момент посылки последнего ACK.

Что такое RTT (round-trip time)?

Время путешествия пакета до адресата и обратно. Контрольное время повторной посылки.

Что такое triple duplicate ACK?

В чем смысл алгоритма «Меделенный старт»?

Во время фазы экспоненциального роста, алгоритм медленного старта работает за счет увеличения окна TCP каждый раз когда получено подтверждение, то есть увеличивает размер окна в зависимости от количества подтвержденных сегментов. Это происходит до тех пор, пока для какого-то сегмента не будет получено подтверждение или будет достигнуто какое-то заданное пороговое значение. Если происходит потеря, TCP предполагает что это связано с перегрузкой сети и принимает меры по сокращению нагрузки на сеть. Как только порог достигнут, TCP входит в фазу линейного роста (предотвращения перегрузки). В эту фазу окно увеличивается на один сегмент для каждого RTT. Это происходит до тех пор, пока случаются потери.