В операционной системе Windows межпроцессное взаимодействие (IPC) может осуществляться с помощью различных механизмов. Ниже приведены некоторые распространенные методы взаимодействия.
CreateFileMapping
иOpenFileMapping
функция,Процесс может создать область общей памяти.,Другие процессы открывают этот отображенный в памяти объект с тем же именем.,Для выполнения операций чтения и записи в одной и той же памяти,достичь цели обмена данными.При обсуждении взаимодействия между процессами в сети необходим способ уникальной идентификации процессов, участвующих в обмене данными, и стек протоколов TCP/IP обеспечивает решение этой проблемы.
Что касается реализации прикладного уровня, то интерфейс программирования сокетов является одним из наиболее широко используемых механизмов в настоящее время. Он берет свое начало из системы UNIX BSD и стал стандартом кросс-платформенного сетевого программирования.
Можно сказать,“Всё есть розетка”
В этой статье будет создан простой TCP-эхо-сервер и клиент на основе программирования сокетов под Windows, а также дано подробное объяснение некоторых кодов и принципов TCP.
Socket,Часто переводится как «розетка» на китайском языке.,Это очень важная концепция в компьютерных сетях.,это сетевое общениеиз Одна из основ。Socket Предоставляет механизм межсетевого взаимодействия, позволяющий приложениям на двух разных компьютерах обмениваться данными по сети. На более конкретном уровне Socket Его можно рассматривать как интерфейс для двух программ в сети для связи через двустороннюю связь. Некоторые люди также рассматривают сокет как специальный файл, и с ним работают некоторые функции сокета (чтение/запись ввода-вывода, открытие, закрытие).
Принцип работы Socket основан на модели CS, где одна сторона играет роль клиента, а другая — роль сервера. Общий процесс в Windows выглядит следующим образом:
первый,Необходимо инициализировать сетевую библиотеку,нравитьсясуществоватьWindowsиспользуется в системеWSAStartupфункцияинициализацияWinsockБиблиотека,Явная инициализация обычно не требуется в системах Unix/Linux.
#include <windows.h>
#include <iostream>
#pragma comment(lib,"ws2_32.lib")
int main()
{
// 0. Инициализируйте сетевое окружение
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
printf("Инициализация Winsock не удалась\n");
return -1;
}
printf("Winsock успешно инициализирован\n");
// Разместите здесь код сетевой связи...
// Очистка ресурсов Winsock
WSACleanup();
printf("Ресурсы очищены\n");
return 0;
}
Когда вы вызываете функцию сокета для создания сокета, возвращаемый ею дескриптор сокета однозначно идентифицирует сокет. Этот дескриптор сокета концептуально аналогичен дескриптору файла. Он используется в качестве параметра, и через него выполняются некоторые операции передачи данных.
Точно так же, как вы можете передавать в fopen разные значения параметров для открытия разных файлов. При создании сокета вы также можете указать разные параметры для создания разных дескрипторов сокета.
SOCKET WSAAPI socket(
[in] int af,
[in] int type,
[in] int protocol
);
af:Прямо сейчаспротоколдомен,также известный какпротоколклан(family)。Обычно используетсяизпротоколкланиметь,AF_INET означает IPv4. AF_INET6 означает IPv6 и так далее.
type:обозначениеsocketтип。Обычно используетсяизsocketтипиметь,SOCK_STREAM представляет TCP-соединение.,SOCK_DGRAM означает UDP и т. д.
protocol:Итак, имя означает,то естьобозначениепротокол。Обычно используетсяизпротоколиметь,IPPROTO_TCP, IPPTOTO_UDP и т. д.,Они соответствуют соответственноTCPпередача инфекциипротокол、UDPпередача инфекциипротокол
Служить端и Все клиентские программы будут вызыватьsocketфункция СоздайтеSocket。Нужен в это времяобозначениекоммуникацияизпротоколдомен、типиобозначениепротокол(обозначениепротокол Обычно заполняется0,Пусть система выбереттиппереписыватьсяизпо умолчаниюпротокол)。
// 1. Создать дескриптор сервера (сокет)
// AF_INET ipv4 AF_INET6 ipv6
// SOCK_STREAM --> TCP SOCK_DREAM --> UDP
SOCKET sockServer = socket(AF_INET, SOCK_STREAM, 0);
if (INVALID_SOCKET == sockServer)
{
printf("Не удалось создать дескриптор сервера\n");
WSACleanup();
return -1;
}
printf("1. Сервер успешно создан\n");
Когда функция сокета() вызывается для создания сокета, сокету не назначается конкретный сетевой адрес (IP-адрес и номер порта). Чтобы назначить сокету адрес (в основном IP-адрес и номер порта), следующим шагом является функцияbind().
Служить端想существоватьего созданиеизSocketПривяжите одинIP-адресиномер порта,Нужно позвонитьbind()функция,И передайте параметр, содержащий информацию об адресе (например, структуру SOCKADDR_IN). Этот шаг связывает определенный сетевой адрес с сокетом.,Позволяет сокету начать прослушивание соединений с этого адреса (для сервера) или в качестве исходного адреса для последующих вызовов Connect() (для клиента).
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockfd:Это до прохожденияsocket()функцияобратный звонокиздескриптор сокета。это целое число,Представляет сокет, к которому должен быть привязан адрес. Этот параметр позволяет операционной системе узнать, какому сокету вы хотите назначить информацию об адресе.
addr:Это указательsockaddrструктураизуказатель
А параметр addr типа struct sockaddr* необходимо указать в соответствии с доменом протокола, указанным при создании сокета.
Для IPv4 используется структура struct sockaddr_in, которая содержит:
sin_family: Член семейства адресов, обычно установленный в AF_INET для IPv4.
sin_port: Номер порта, выраженный в сетевом порядке байтов.
sin_addr: Структура, содержащая адрес IPv4. Ее член s_addr хранит 32-битный адрес IPv4, также в сетевом порядке байтов.
Аббревиатура IPv6
addrlen:этоsocklen_tтипизценить,Указывает размер структуры адреса, на которую указывает адрес.
// 2. Привязать номер порта и IP-адрес
SOCKADDR_IN addr = {};
addr.sin_family = AF_INET;
addr.sin_port = htons(9870);// номер порта host to net short
//addr.sin_addr.S_un.S_addr должен быть установлен на IP-адрес, на котором расположен сервер
//addr.sin_addr.S_un.S_addr = INADDR_ANY; // IP-адрес Все IP в порядке
//addr.sin_addr.S_un.S_addr = inet_addr("192.168.1.4"); // идентификацияIP-адрес
addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //Локальный адрес
if (bind(sockServer, (sockaddr*)&addr, sizeof(SOCKADDR_IN)) == SOCKET_ERROR)
{
printf("привязочный номер неудачно\n");
closesocket(sockServer);
WSACleanup();
return -1;
}
printf("2. обязательный номер портауспех\n");
После вызова функций socket() иbind() вам следует вызвать метод Listen() для мониторинга сокета. Если клиент вызывает метод Connect() для выдачи запроса на соединение, сервер получит запрос.
Вызов сервераlistenфункция
int listen(int sockfd, int backlog);
Начните прослушивать запросы на подключение к связанному порту.
sockfd:мониторизsocket
backlog:еще нетaccept()вызов принятьиззапрос на соединениеизмаксимальное количество,Включая соединения, которые завершили трехстороннее рукопожатие, но еще не были обработаны серверным процессом через метод Accept(). это означает,Если быстро поступает большое количество запросов на соединение,Запросы, превышающие это значение, могут быть отклонены.
// 3. мониторномер порт(сообщить операционной системе,и Текущая программа устанавливает логическую связь)
if (listen(sockServer, 5) == SOCKET_ERROR)
{
printf("мониторномер неудачно\n");
closesocket(sockServer);
WSACleanup();
return -1;
}
printf("3. мониторномер портауспех\n");
TCP-сервер настраивает и начинает прослушивать запросы на подключение для указанного IP-адреса и порта, последовательно вызывая функции Socket(), Bind() и Listen(). Конкретно:
Socket() создает несвязанный сокет.
bind() привязывает сокет к определенному IP-адресу и номеру порта.
Listen() переводит сокет в режим прослушивания и устанавливает максимальную длину очереди ожидания соединений.
Далее вам следует использовать функцию Connect(), чтобы попытаться установить соединение с определенным IP-адресом и портом сервера. Это действие включает в себя трехэтапный процесс установления связи TCP для установления надежного соединения.
Сервер вызываетaccept
функцияпринимает сообщение от клиентаиззапрос на соединение,Это выделит новый дескриптор сокета (сокет) специально для связи с этим клиентом. Исходный сокет продолжает прослушивать другие новые запросы на подключение.
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
sockfd:Это вызывается перед серверной частью черезsocket()функциясоздаватьиздескриптор сокета,Он представляет сокет, который прослушивает сервер. (Обычно сервер имеет только один дескриптор прослушиваемого сокета.,Он существует на протяжении всего срока службы сервера. )
addr:Это указательstruct sockaddr * указатель, используемый для получения информации об адресе клиента. Когда метод Accept() завершает работу успешно, эта структура будет заполнена адресом клиента и информацией о порте, чтобы сервер знал, какой клиент инициировал соединение.
addrlen:указывает наsocklen_tтипизуказатель,Используется для указания размера структуры, на которую указывает адрес. Перед вызовом Accept(),Это значение необходимо сначала инициализировать,сообщить ядру размер структуры,После звонка,Обычно обновляется, чтобы отразить фактический размер заполненной структуры адреса.
while (true)
{
// 4. Получить клиентское соединение Будет создан новый сокет (он же тег клиента)
printf("4. Приготовьтесь дождаться прибытия клиента\n");
SOCKADDR_IN clientAddr = {};
int nAddrLen = sizeof(SOCKADDR_IN);
SOCKET sockClient = accept(sockServer, (sockaddr*)&clientAddr, &nAddrLen);
if (INVALID_SOCKET == sockClient)
{
printf("Получить клиентское соединениенеудача\n");
continue; // После обработки ошибок продолжайте ждать следующего клиента.
}
printf("4. Получить клиентское соединениеуспех\n");
// Цикл для общения с клиентом
// Закрыть клиентский сокет
closesocket(sockClient);
printf("Текущий клиент отключился, ожидание следующего клиента...\n");
}
После установления соединения обе стороны могут отправлять и получать данные, реализуя таким образом связь между различными процессами в сети!
Чтение данных:Обычно это делается с помощьюrecv()илиread()функцияот соединенияизв розетке Чтение данные. Эти функции позволяют программам читать данные, отправленные клиентом или сервером.
Отправить данные:Так же,Они могут отправлять сообщения друг другу с помощью функций send() или write(). Эти функции записывают данные в сокет,и затем передается другой стороне.
Для TCP-соединений передача данных основана на потоках, что обеспечивает порядок и надежность данных, тогда как для UDP данные передаются в виде дейтаграмм, и порядок не гарантируется и может быть ненадежным.
дляWindowsПлатформа, которую я обычно используюrecvиsendфункцияруководитьI/Oдействовать
int recv(
SOCKET s,
char FAR *buf,
int len,
int flags
);
s:дескриптор сокета,Возвращается функцией сокета(), которая ранее создала сокет. Он идентифицирует конечную точку связи для чтения данных.
buf:указатель на буферизуказатель,Этот буфер используется для приема данных. Данные будут считываться в этот буфер.
len:буфериздлина,В байтах. Этот параметр указывает максимальный объем данных, которые можно получить из сокета.
flags:контрольный приемдействоватьизлоготип。общийизлоготипиметьMSG_PEEK(Предварительный просмотр данных, не удаляя их из очереди приема)ждать。нравиться Если вы не используете специальные функции,Обычно можно установить на 0.
recv
функцияиз返回ценитьиметь Несколько типичных ситуаций,Каждый представляет собой разное значение:
значение больше 0:выражатьуспех Полученные данные,Возвращаемое значение — это фактическое количество полученных байтов. Это означает, что данные были успешно прочитаны из буфера сокета в предоставленный буфер.
значение, равное 0:Обычно это означает, что соединение было закрыто другой стороной.。существоватьTCPПодключение,Когда партнер выполняет обычный процесс завершения работы (отправляет пакет FIN),и все оставшиеся данные получены,Recv может вернуть 0. Это означает нормальное завершение передачи данных.
значение меньше 0:Это означает, что произошла ошибка。существоватьWindowsв системе,Значение ошибки обычно равно SOCKET_ERROR (обычно определяется как -1). в это время,Вам нужно вызвать WSAGetLastError(), чтобы получить конкретный код ошибки.,Для дальнейшего анализа причины ошибки,Сравниватьнравиться Сеть недоступна、Соединение прерванождатьвопрос。
Если для сокета установлен неблокирующий режим, функция Recv также может немедленно вернуться, если нет данных для чтения. В это время возвращаемым значением может быть код ошибки WSAEWOULDBLOCK, указывающий, что вызов следует повторить позже и не следует. расценивать как ошибку. Кроме того, в некоторых случаях Recv может также возвращать -1, если операция приема прерывается сигналом, а errno (в системах POSIX) или WSAGetLastError() (в Windows) может быть установлено в EINTR, указывая, что операция была прервана. Нужно попробовать еще раз.
int send(
SOCKET s,
const char FAR *buf,
int len,
int flags
);
s:То же самоедескриптор сокета,идентифицированный Отправить Конечная точка связи для данных.
buf:Укажите на желание Отправить указатель данных на буфер. Данные будут считаны из этого буфера и отправлены подключенному узлу.
len:отправитьизданныеиздлина,В байтах.
flags:иrecvсерединаизflagsпохожий,Флаг, используемый для управления операциями отправки. Общие из них включают MSG_OOB (отправка внеполосных данных) и т. д. Обычно,Если не требуется никаких специальных операций,Можно установить на 0.
Возвращаемое значение функции send также имеет несколько возможных ситуаций, каждая из которых имеет определенное значение:
значение больше 0:выражатьуспех发送了данные,Возвращаемое значение — это количество фактически отправленных байтов. Это может быть меньше общего количества байтов, которые вы пытаетесь отправить.,Особенно если установлен флаг MSG_PARTIAL или операция прерывается сигналом,Но обычно оно должно быть равно количеству байтов, которые вы запрашиваете для отправки.,пока не Произошла ошибкаили В неблокирующем режимеизособые обстоятельства。
значение, равное 0:эта ситуациясуществоватьTCP编程середина是不общийиз,Обычно означает, что данные не отправляются,Это может быть связано с тем, что сокет был закрыт или что-то серьезное пошло не так.
значение меньше 0:выражать发送действоватьнеудача。существоватьWindowsв системе,Обычно это SOCKET_ERROR (значение -1). в это время,Вам нужно вызвать WSAGetLastError(), чтобы получить подробный код ошибки.,примернравиться Сеть недоступна、Соединение прервано、Буфер заполнен и так далее.
В частности, когда сокет установлен в неблокирующий режим, если буфер отправки заполнен или дополнительные данные не могут быть отправлены временно по другим причинам, send может немедленно вернуть SOCKET_ERROR, а WSAGetLastError() возвращает WSAEWOULDBLOCK, указывая, что данные не могут быть отправлены немедленно. , надо попробовать еще раз позже. Кроме того, если операция отправки прерывается сигналом, в некоторых системах возвращаемое значение также может быть -1, а индикацией кода ошибки является EINTR, который также необходимо обработать и операцию отправки можно повторить.
// Цикл для общения с клиентом
while (true)
{
char szData[1024] = {};
int ret = recv(sockClient, szData, sizeof(szData) - 1, 0);
if (ret > 0)
{
szData[ret] = '\0'; // Добавить терминатор строки
printf("5. Данные клиента успешно получены [%s]\n", szData);
// Отправить эхо-данные
ret = send(sockClient, szData, ret, 0);
if (ret == SOCKET_ERROR)
{
printf("Отправить данныенеудача\n");
break; // Не удалось отправить, отключитесь от клиента
}
}
else if (ret == 0) // Клиент закрывает соединение
{
printf("Клиент активно отключился.\n");
break; // Выйдите из цикла обычным образом и подготовьтесь к обработке следующего клиента.
}
else // Произошла ошибка
{
printf("Не удалось получить данные клиента\n");
break; // Отключение после обработки ошибки
}
}
#include <windows.h>
#include <iostream>
#pragma comment(lib,"ws2_32.lib")
int main()
{
// 0. Инициализируйте сетевое окружение
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
printf("Инициализация Winsock не удалась\n");
return -1;
}
printf("Winsock успешно инициализирован\n");
// 1. Создать дескриптор сервера (сокет)
// AF_INET ipv4 AF_INET6 ipv6
// SOCK_STREAM --> TCP SOCK_DREAM --> UDP
SOCKET sockServer = socket(AF_INET, SOCK_STREAM, 0);
if (INVALID_SOCKET == sockServer)
{
printf("Не удалось создать дескриптор сервера\n");
WSACleanup();
return -1;
}
printf("1. Сервер успешно создан\n");
// 2. Привязать номер порта и IP-адрес
SOCKADDR_IN addr = {};
addr.sin_family = AF_INET;
addr.sin_port = htons(9870);// номер порта host to net short
//addr.sin_addr.S_un.S_addr указывает расположение хоста сервера.
//addr.sin_addr.S_un.S_addr должен быть установлен на IP-адрес, на котором расположен сервер
//addr.sin_addr.S_un.S_addr = INADDR_ANY; // IP-адрес Все IP в порядке
//addr.sin_addr.S_un.S_addr = inet_addr("192.168.1.4"); // идентификацияIP-адрес
addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //Локальный адрес
if (bind(sockServer, (sockaddr*)&addr, sizeof(SOCKADDR_IN)) == SOCKET_ERROR)
{
printf("привязочный номер неудачно\n");
closesocket(sockServer);
WSACleanup();
return -1;
}
printf("2. обязательный номер портауспех\n");
// 3. мониторномер порт(сообщить операционной системе,и Текущая программа устанавливает логическую связь)
if (listen(sockServer, 5) == SOCKET_ERROR)
{
printf("мониторномер неудачно\n");
closesocket(sockServer);
WSACleanup();
return -1;
}
printf("3. мониторномер портауспех\n");
while (true)
{
// 4. Получить клиентское соединение Будет создан новый сокет (он же тег клиента)
printf("4. Приготовьтесь дождаться прибытия клиента\n");
SOCKADDR_IN clientAddr = {};
int nAddrLen = sizeof(SOCKADDR_IN);
SOCKET sockClient = accept(sockServer, (sockaddr*)&clientAddr, &nAddrLen);
if (INVALID_SOCKET == sockClient)
{
printf("Получить клиентское соединениенеудача\n");
continue; // После обработки ошибок продолжайте ждать следующего клиента.
}
printf("4. Получить клиентское соединениеуспех\n");
// Цикл для общения с клиентом
while (true)
{
char szData[1024] = {};
int ret = recv(sockClient, szData, sizeof(szData) - 1, 0);
if (ret > 0)
{
szData[ret] = '\0'; // Добавить терминатор строки
printf("5. Данные клиента успешно получены [%s]\n", szData);
// Отправить эхо-данные
ret = send(sockClient, szData, ret, 0);
if (ret == SOCKET_ERROR)
{
printf("Отправить данныенеудача\n");
break; // Не удалось отправить, отключитесь от клиента
}
}
else if (ret == 0) // Клиент закрывает соединение
{
printf("Клиент активно отключился.\n");
break; // Выйдите из цикла обычным образом и подготовьтесь к обработке следующего клиента.
}
else // Произошла ошибка
{
printf("Не удалось получить данные клиента\n");
break; // Отключение после обработки ошибки
}
}
// Закрыть клиентский сокет
closesocket(sockClient);
printf("Текущий клиент отключился, ожидание следующего клиента...\n");
}
// После завершения основного цикла закройте сокет сервера.
closesocket(sockServer);
// Очистка ресурсов Winsock
WSACleanup();
printf("Ресурсы очищены\n");
return 0;
}
За исключением того, что привязка и прослушивание отсутствуют, процесс установления сокета аналогичен серверному, поэтому он опускается.
Просто поговорим о Connect()
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockfd:Это до прохождениявызовsocket()функциясоздаватьиздескриптор сокета。
addr:Это указательstruct Указатель на структуру sockaddr. Краткое введение в серверную часть sockaddr
addrlen:это указатель,Указатель на переменную, в которой хранится размер структуры адреса.
После вызова функции Connect() она попытается установить соответствующее соединение с сервером по указанному адресу. В случае успеха функция немедленно возвращает 0. Если соединение не может быть установлено немедленно (например, из-за того, что сеть недоступна или сервер не отвечает), функция блокируется до тех пор, пока соединение не будет установлено или не произойдет тайм-аут/ошибка, после чего возвращается -1, а подробности можно получить с помощью кода ошибки errno или WSAGetLastError() (под Windows).
SOCKADDR_IN addr = {};
addr.sin_family = AF_INET;
addr.sin_port = htons(9870);
addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); // IP-адрес
int ret = connect(sockClient, (sockaddr*)&addr, sizeof(SOCKADDR_IN));
if (SOCKET_ERROR == ret)
{
printf("Подключиться к сбой сервера\n");
return -1;
}
while (1)
#include <windows.h>
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#pragma comment(lib,"ws2_32.lib")
int main()
{
// 0. Инициализируйте сетевое окружение
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
printf("Инициализация Winsock не удалась\n");
return -1;
}
printf("Winsock успешно инициализирован\n");
// 1. Создать дескриптор клиента (сокет)
// AF_INET ipv4
// SOCK_STREAM --> TCP SOCK_DREAM --> UDP
SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);
if (INVALID_SOCKET == sockClient)
{
printf("Не удалось создать дескриптор клиента\n");
return -1;
}
printf("1. Дескриптор клиента успешно создан\n");
// 2. Подключиться к серверуномер портаиIP-адрес
SOCKADDR_IN addr = {};
addr.sin_family = AF_INET;
addr.sin_port = htons(9870);
addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); // IP-адрес
int ret = connect(sockClient, (sockaddr*)&addr, sizeof(SOCKADDR_IN));
if (SOCKET_ERROR == ret)
{
printf("Подключиться к сбой сервера\n");
return -1;
}
while (1)
{
char buf[1024] = { 0 };
printf("Пожалуйста, введите символы:");
//Читаем по одной строке из консоли
gets_s(buf);
// 3. Генерация данных на сервере.
int retSend = send(sockClient, buf, strlen(buf), 0);
// retSend !=13 Отправка не удалась
// 4. Примите данные сервера
char szRecv[4096] = {};
int retRecv = recv(sockClient, szRecv, 4096, 0);
if (retRecv <= 0)
{
printf("Не удалось принять данные сервера\n");
return -1;
}
printf("Полученные данные сервера: %s\n", szRecv);
}
// 5. Закройте дескриптор клиента.
closesocket(sockClient);
return 0;
}
TCP обеспечивает надежную, ориентированную на соединение службу транспортного уровня с байтовым потоком, которая использует трехстороннее подтверждение для установления соединения. Используйте четыре волны
чтобы закрыть соединение.
Цель трехстороннего рукопожатия — убедиться, что две стороны установили связь друг с другом.
Трехстороннее рукопожатие происходит при подключении клиента. При вызове метода Connect() нижний уровень выполняет трехстороннее рукопожатие через протокол TCP.
Общий процесс выглядит следующим образом
Первое рукопожатие:
1. Клиент устанавливает флаг SYN в 1.
2. Сгенерируйте случайный 32-битный серийный номер seq=J. Этот серийный номер может содержать после себя данные (размер данных).
Второе рукопожатие:
1. Сервер получает соединение клиента: ACK=1.
2. Сервер отправит обратно порядковый номер подтверждения: ack = порядковый номер клиента + длина данных + SYN/FIN (рассчитывается в одном байте)
3. Сервер инициирует запрос на соединение с клиентом: SYN=1.
4. Сервер сгенерирует случайный порядковый номер: seq = K.
Третье рукопожатие:
1. Клиент отвечает на запрос подключения сервера: ACK=1.
2. Клиент отвечает, что получил данные от сервера: ack = серийный номер сервера + длина данных + SYN/FIN (рассчитывается как один байт)
Четыре волны возникают при разрыве соединения. Когда в программе вызывается функция close(), протокол TCP будет использоваться для передачи волны четыре раза.
И клиент, и сервер могут активно инициировать отключение. Тот, кто первым вызовет метод close(), инициирует его.
Поскольку во время TCP-соединения соединение, установленное с помощью трехстороннего рукопожатия, является двунаправленным, и при отключении его необходимо отключать в обоих направлениях.
Общий процесс выглядит следующим образом
1. Процесс приложения сначала вызывает close, чтобы активно закрыть соединение. В это время TCP отправляет FIN M;
2. После того, как другой конец получает FIN M, он выполняет пассивное закрытие и подтверждает FIN. Его прием также передается процессу приложения как символ конца файла, поскольку прием FIN означает, что процесс приложения больше не может получать дополнительные данные по соответствующему соединению;
3. Через некоторое время процесс приложения, который получает символ конца файла, вызывает close, чтобы закрыть свой сокет. Это заставляет его TCP также отправлять FIN N;
4. TCP отправителя отправителя, который получает этот FIN, подтверждает его.
В настоящее время echoServer (эхо-сервер) может обрабатывать только одно клиентское соединение.,Что делать, если клиентов несколько? Подключиться к серверу? Можно ли разделить полученную клиентскую строку для идентификации,И переслать клиенту сообщение, соответствующее строке? Как передать информацию клиентам, отличным от клиента, отправившего информацию? (реализация простого чата)
Справочная статья:
Программирование сокетов Linux (не ограничиваясь Linux)
Основы сокетной связи