Michael J. Donahoo "TCP/IP Sockets in C, 2nd Edition" / Michael J. Donahoo "Программирование TCP/IP сокетов в Си, 2-ое издание": 2.2 IPv4 TCP сервер

Оглавление
← →
Английский оригинал Перевод на русский

Сожалеем, но текст оригинала доступен только зарегистрированным пользователям.

IPv4 TCP Сервер.

BioForceGun 22.05.12 в 04:33

0

Теперь мы обратим внимание на создание TCP сервера. Задача сервера заключается в создании точки для подключения и последующего ожидания подключения к ней клиента.

BioForceGun 22.05.12 в 04:39

0

Выделим четыре основных шага для простой коммуникации с TCP сервером:

BioForceGun 22.05.12 в 04:41

0

1. Создание TCP сокета с помощью socket().

BioForceGun 22.05.12 в 04:42

0

2. Назначить номер порта сокету с помощью bind().

BioForceGun 22.05.12 в 04:42

0

3. Сообщим системе разрешить подключения к порту используя listen().

BioForceGun 22.05.12 в 04:44

0

4. Повторно выполнять:

BioForceGun 22.05.12 в 04:45

0

4. Повторно (в цикле) выполнять следующее:

victor8632 04.06.12 в 11:57

0

• Вызвать accept() для получения нового сокета, для каждого клиентского соединения.

BioForceGun 22.05.12 в 04:47

0

• Связаться с клиентом посредством нового сокета используя send() и recv().

BioForceGun 22.05.12 в 04:48

0

• Закрыть клиентское соединение с помощью close().

BioForceGun 22.05.12 в 04:49

0

Создание сокета, отправление, получение и закрытие происходят так же как и у клиента.

BioForceGun 22.05.12 в 04:50

0

Создание сокета, отправление, получение данных и закрытие соединения происходят так же, как и у клиента.

victor8632 04.06.12 в 12:00

0

Отличие в использовании сервером сокетов заключается в том что он связывает адрес с сокетом и потом использует сокет как способ получения других сокетов которые обеспечивают подключения клиентов. (Мы будем подробно останавливатся на этом в процессе комментирования последующего программного кода.)

victor8632 04.07.12 в 13:57

0

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

victor8632 06.06.12 в 12:44

0

TCPEchoServer4.c

prefer211 19.05.12 в 07:23

0

1 #include
2 #include
3 #include
4 #include types.h>
5 #include socket.h>
6 #include in.h>
7 #include inet.h>
8 #include "Practical.h"
9
10 static const int MAXPENDING = 5; // Максимум исходящих запросов на соединение
11
12 int main(int argc, char *argv[]) {
18 Chapter 2: Basic TCP Sockets
13
14 if (argc != 2) // Проверка правильности количества аргументов
15 DieWithUserMessage("Parameter(s)", "");
16
17 in_port_t servPort = atoi(argv[1]); // Первый аргумент: локальный порт
18
19 // Создаем сокет для входящих подключений
20 int servSock; // Дескриптор сокета для сервера
21 if ((servSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
22 DieWithSystemMessage("socket() failed");
23
24 // Создаем структуру локального адреса
25 struct sockaddr_in servAddr; // Локальный адрес
26 memset(&servAddr, 0, sizeof(servAddr)); // Обнуляем структуру
27 servAddr.sin_family = AF_INET; // семейство адресов IPv4
28 servAddr.sin_addr.s_addr = htonl(INADDR_ANY); // Любой входящий интерфейс
29 servAddr.sin_port = htons(servPort); // Локальный порт
30
31 // Связываем с локальным адресом
32 if (bind(servSock, (struct sockaddr*) &servAddr, sizeof(servAddr)) < 0)
33 DieWithSystemMessage("bind() failed");
34
35 // Маркируем сокет таким образом, чтобы он ожидал входящие соединения
36 if (listen(servSock, MAXPENDING) < 0)
37 DieWithSystemMessage("listen() failed");
38
39 for (;;) { // Бесконечный цикл
40 struct sockaddr_in clntAddr; // Адрес клиента
41 // Устанавливаем величину структуры адреса клиента (in-out параметр)
42 socklen_t clntAddrLen = sizeof(clntAddr);
43
44 // Ждем соединения с клиентом
45 int clntSock = accept(servSock, (struct sockaddr *) &clntAddr, &clntAddrLen);
46 if (clntSock < 0)
47 DieWithSystemMessage("accept() failed");
48
49 // clntSock: соединение с клиентом достигнуто!
50
51 char clntName[INET_ADDRSTRLEN]; // Строка, содержащая адрес клиента
52 if (inet_ntop(AF_INET, &clntAddr.sin_addr.s_addr, clntName,
53 sizeof(clntName)) != NULL)
54 printf("Handling client %s/%d\n", clntName, ntohs(clntAddr.sin_port));
55 else
56 puts("Unable to get client address");
57
58 HandleTCPClient(clntSock);
59 }
60 // NOT REACHED
61 }

victor8632 04.07.12 в 14:18

0

TCPEchoServer4.c

BioForceGun 22.05.12 в 05:07

0

1. Настройка программы и расшифровка параметров: строки 1-17

victor8632 06.06.12 в 12:45

0

Мы конвертировали номер порта из строки в числовое значение с помощью функции atoi(); если первый аргумент не число, atoi() вернет 0, который приведет к ошибке позже, когда мы вызовем bind().

BioForceGun 22.05.12 в 05:07

0

2. Создание сокета и настройка: строки 19-37

BioForceGun 22.05.12 в 05:04

0

Создание TCP сокета: строки 20-22

BioForceGun 22.05.12 в 05:05

0

Мы создаем потоковый сокет так же, как мы делали это для клиента.

victor8632 04.06.12 в 12:02

0

Ввод желаемого адреса конечной точки: строки 25-29

victor8632 06.06.12 в 12:49

0

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

victor8632 06.06.12 в 12:56

0

Так как мы пишем для IPv4, мы используем для этого структуру sockaddr_in. Так как нам, вобщем, все равно, какой адрес является действующим (любой адрес работающий адрес сервера нам подойдет), то доверим его выбор системе, указав шаблон INADDR_ANY как желаемый интернет адрес.

victor8632 06.06.12 в 13:04

0

(Как правило, это работает для любого сервера и избавляет сервер от необходимости определять реальный интернет адрес.)

victor8632 06.06.12 в 13:07

0

Перед установкой адреса и номера порта в структуре sockaddr_in мы конвертируем их к сетевому байтовому представлению при помощи функций htonl() и htons(). (Смотрите пункт 5.1.2 для получения полного представления.)

victor8632 07.06.12 в 05:34

0

Привязка сокета к установленному адресу и порту: строки 32-33

victor8632 07.06.12 в 05:35

0

Как отмечалось выше, сокет сервера должен быть связан с локальным адресом и портом; функцией, которая выполняется это связывание, является bind().

victor8632 07.06.12 в 05:38

0

Обратите внимание, что в то время как клиент должен указать адрес сервера в функции connect(), сервер должен указать свой собственный адрес в функции bind(). Именно эта часть информации (т.е. адрес и порт сервера), нужна для установки соединения; ни одному из них (ни серверу, ни клиенту) нет необходимости знать адрес клиента.

victor8632 07.06.12 в 05:45

0

Имейте в виду, что функция bind() может не выполниться по различным причинам; одной из самых важных таких причин является то, что некоторый другой сокет уже связан с указанным портом (см. пункт 7.5).

victor8632 07.06.12 в 05:48

0

Так что в некоторых системах нужны специальные права, чтобы привязать некоторый порт (обычно, с номером меньшим 1024).

victor8632 07.06.12 в 05:53

0

Установка сокета на прослушивание: строки 36-37

victor8632 07.06.12 в 05:54

0

Вызов функции listen() уведомляет реализацию TCP о том, что можно разрешить входящие соединения от клиентов. Перед вызовом функции listen(), любые входящие запросы на соединение с адресом сокета будут по умолчанию отвергнуты - так что функция connect() на клиенте не будет выполнена.

victor8632 07.06.12 в 06:10

0

3. Итеративная обработка входящих соединений: строки 39-59

victor8632 07.06.12 в 06:11

0

Прием входящих подключений: строки 40-47

victor8632 07.06.12 в 06:12

0

Как уже говорилось выше, TCP сокет, для которого вызвана функция listen(), используется иначе, чем это было в клиентском приложении.

victor8632 07.06.12 в 06:32

0

Вместо отсылки и получения информации на сокете, серверное приложение вызывает функцию accept(), которая блокируется до тех пор пока для прослушиваемого номера порта сокета не будет установлено входящее соединение.

victor8632 07.06.12 в 06:46

0

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

victor8632 07.06.12 в 06:49

0

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

victor8632 07.06.12 в 07:34

0

Обратите внимание, что сокет, на который ссылается возвращенный дескриптор, уже подключен; среди всего прочего это означает, что он готов к передаче и приему данных. (Для получения дополнительной информации о том, что происходит в конкретной реализации смотри пункт 7.4.1 в главе 7.)

victor8632 07.06.12 в 11:17

0

Сообщение о подключенном клиенте: строки 51-56

victor8632 07.06.12 в 11:18

0

В этом месте текста clntAddr содержит адрес и номер порта присоединившегося клиента; мы предоставляем "номер звонившего" функции проверки и потом выводим информацию о клиенте.

victor8632 04.07.12 в 07:25

0

Как и следовало ожидать, функция inet_ntop() является обратной функции inet_pton(), которую мы использовали на клиенте.

victor8632 04.07.12 в 07:34

0

Она берет бинарное представление адреса клиента и преобразует его в четырехкомпонентную строку.

victor8632 04.07.12 в 07:36

0

Поскольку операции с портами и адресами производятся в так называемом сетевом порядке байтов (пункт 5.1.2) мы должны преобразовать номер порта перед передачей его функции printf() (функция inet_pton() заботится об этом для адресов).

victor8632 04.07.12 в 07:41

0

Обработчик эхо ответа для клиента: строка 58

victor8632 04.07.12 в 13:59

0

HandleTCPClient() берет на себя "протокол приложения". Мы обсудим эту функцию ниже. Таким образом мы убираем из текущей функции часть функциональности сервера, связанную с выдачей "эха".

victor8632 04.07.12 в 07:58

0

У нас есть отдельная функция, которая реализует "эхо" часть нашего сервера.

victor8632 04.07.12 в 07:59

0

Хотя реализация этого протокола приложения содержит всего несколько строк, это хороший стиль программирования, отделить данную часть от остального серверного кода. Это способствует повторному использованию кода.

victor8632 04.07.12 в 08:02

0

HandleTCPClient() получает данные с указанного сокета и посылает их обратно на тот же сокет, эта операция повторяется до тех пор, пока функция recv() возвращает положительное значение (положительное значение сигнализирует о том, что что-то было получено).

victor8632 04.07.12 в 08:17

0
Минутку...

Следующая страница →