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 |
|
|
Теперь мы обратим внимание на создание TCP сервера. Задача сервера заключается в создании точки для подключения и последующего ожидания подключения к ней клиента. BioForceGun 22.05.12 в 04:39 |
||
|
Выделим четыре основных шага для простой коммуникации с TCP сервером: BioForceGun 22.05.12 в 04:41 |
||
|
1. Создание TCP сокета с помощью socket(). BioForceGun 22.05.12 в 04:42 |
||
|
2. Назначить номер порта сокету с помощью bind(). BioForceGun 22.05.12 в 04:42 |
||
|
3. Сообщим системе разрешить подключения к порту используя listen(). BioForceGun 22.05.12 в 04:44 |
||
|
4. Повторно выполнять: BioForceGun 22.05.12 в 04:45 4. Повторно (в цикле) выполнять следующее: victor8632 04.06.12 в 11:57 |
||
|
• Вызвать accept() для получения нового сокета, для каждого клиентского соединения. BioForceGun 22.05.12 в 04:47 |
||
|
• Связаться с клиентом посредством нового сокета используя send() и recv(). BioForceGun 22.05.12 в 04:48 |
||
|
• Закрыть клиентское соединение с помощью close(). BioForceGun 22.05.12 в 04:49 |
||
|
Создание сокета, отправление, получение и закрытие происходят так же как и у клиента. BioForceGun 22.05.12 в 04:50 Создание сокета, отправление, получение данных и закрытие соединения происходят так же, как и у клиента. victor8632 04.06.12 в 12:00 |
||
|
Отличие в использовании сервером сокетов заключается в том что он связывает адрес с сокетом и потом использует сокет как способ получения других сокетов которые обеспечивают подключения клиентов. (Мы будем подробно останавливатся на этом в процессе комментирования последующего программного кода.) victor8632 04.07.12 в 13:57 |
||
|
Связь сервера с каждым клиентом настолько проста, насколько это вообще возможно: он просто получает данные от клиентского соединения и посылает их обратно клиенту; это повторяется до тех пор, пока клиент не закроет свой конец соединения. victor8632 06.06.12 в 12:44 |
||
|
TCPEchoServer4.c prefer211 19.05.12 в 07:23 |
||
|
1 #include victor8632 04.07.12 в 14:18 |
||
|
TCPEchoServer4.c BioForceGun 22.05.12 в 05:07 |
||
|
1. Настройка программы и расшифровка параметров: строки 1-17 victor8632 06.06.12 в 12:45 |
||
|
Мы конвертировали номер порта из строки в числовое значение с помощью функции atoi(); если первый аргумент не число, atoi() вернет 0, который приведет к ошибке позже, когда мы вызовем bind(). BioForceGun 22.05.12 в 05:07 |
||
|
2. Создание сокета и настройка: строки 19-37 BioForceGun 22.05.12 в 05:04 |
||
|
Создание TCP сокета: строки 20-22 BioForceGun 22.05.12 в 05:05 |
||
|
Мы создаем потоковый сокет так же, как мы делали это для клиента. victor8632 04.06.12 в 12:02 |
||
|
Ввод желаемого адреса конечной точки: строки 25-29 victor8632 06.06.12 в 12:49 |
||
|
На сервере мы должны связать сокет сервера с адресом и номером порта, чтобы клиентские соединения могли попасть в нужное место. victor8632 06.06.12 в 12:56 |
||
|
Так как мы пишем для IPv4, мы используем для этого структуру sockaddr_in. Так как нам, вобщем, все равно, какой адрес является действующим (любой адрес работающий адрес сервера нам подойдет), то доверим его выбор системе, указав шаблон INADDR_ANY как желаемый интернет адрес. victor8632 06.06.12 в 13:04 |
||
|
(Как правило, это работает для любого сервера и избавляет сервер от необходимости определять реальный интернет адрес.) victor8632 06.06.12 в 13:07 |
||
|
Перед установкой адреса и номера порта в структуре sockaddr_in мы конвертируем их к сетевому байтовому представлению при помощи функций htonl() и htons(). (Смотрите пункт 5.1.2 для получения полного представления.) victor8632 07.06.12 в 05:34 |
||
|
Привязка сокета к установленному адресу и порту: строки 32-33 victor8632 07.06.12 в 05:35 |
||
|
Как отмечалось выше, сокет сервера должен быть связан с локальным адресом и портом; функцией, которая выполняется это связывание, является bind(). victor8632 07.06.12 в 05:38 |
||
|
Обратите внимание, что в то время как клиент должен указать адрес сервера в функции connect(), сервер должен указать свой собственный адрес в функции bind(). Именно эта часть информации (т.е. адрес и порт сервера), нужна для установки соединения; ни одному из них (ни серверу, ни клиенту) нет необходимости знать адрес клиента. victor8632 07.06.12 в 05:45 |
||
|
Имейте в виду, что функция bind() может не выполниться по различным причинам; одной из самых важных таких причин является то, что некоторый другой сокет уже связан с указанным портом (см. пункт 7.5). victor8632 07.06.12 в 05:48 |
||
|
Так что в некоторых системах нужны специальные права, чтобы привязать некоторый порт (обычно, с номером меньшим 1024). victor8632 07.06.12 в 05:53 |
||
|
Установка сокета на прослушивание: строки 36-37 victor8632 07.06.12 в 05:54 |
||
|
Вызов функции listen() уведомляет реализацию TCP о том, что можно разрешить входящие соединения от клиентов. Перед вызовом функции listen(), любые входящие запросы на соединение с адресом сокета будут по умолчанию отвергнуты - так что функция connect() на клиенте не будет выполнена. victor8632 07.06.12 в 06:10 |
||
|
3. Итеративная обработка входящих соединений: строки 39-59 victor8632 07.06.12 в 06:11 |
||
|
Прием входящих подключений: строки 40-47 victor8632 07.06.12 в 06:12 |
||
|
Как уже говорилось выше, TCP сокет, для которого вызвана функция listen(), используется иначе, чем это было в клиентском приложении. victor8632 07.06.12 в 06:32 |
||
|
Вместо отсылки и получения информации на сокете, серверное приложение вызывает функцию accept(), которая блокируется до тех пор пока для прослушиваемого номера порта сокета не будет установлено входящее соединение. victor8632 07.06.12 в 06:46 |
||
|
В этот момент функция accept() возвращает указатель на новый сокет, который уже подключен к уже активному удаленному сокету. victor8632 07.06.12 в 06:49 |
||
|
Вторым аргументом функции является указатель на структуру sockaddr_in и третий аргумент является указателем на длину этой структуры. В случае успеха выполнения функции структура sockaddr_in содержит интернет адрес и порт клиента, к которому подключен возвращенный сокет; длина адреса будет записана в указатель на целое число, являющийся третьим параметром. victor8632 07.06.12 в 07:34 |
||
|
Обратите внимание, что сокет, на который ссылается возвращенный дескриптор, уже подключен; среди всего прочего это означает, что он готов к передаче и приему данных. (Для получения дополнительной информации о том, что происходит в конкретной реализации смотри пункт 7.4.1 в главе 7.) victor8632 07.06.12 в 11:17 |
||
|
Сообщение о подключенном клиенте: строки 51-56 victor8632 07.06.12 в 11:18 |
||
|
В этом месте текста clntAddr содержит адрес и номер порта присоединившегося клиента; мы предоставляем "номер звонившего" функции проверки и потом выводим информацию о клиенте. victor8632 04.07.12 в 07:25 |
||
|
Как и следовало ожидать, функция inet_ntop() является обратной функции inet_pton(), которую мы использовали на клиенте. victor8632 04.07.12 в 07:34 |
||
|
Она берет бинарное представление адреса клиента и преобразует его в четырехкомпонентную строку. victor8632 04.07.12 в 07:36 |
||
|
Поскольку операции с портами и адресами производятся в так называемом сетевом порядке байтов (пункт 5.1.2) мы должны преобразовать номер порта перед передачей его функции printf() (функция inet_pton() заботится об этом для адресов). victor8632 04.07.12 в 07:41 |
||
|
Обработчик эхо ответа для клиента: строка 58 victor8632 04.07.12 в 13:59 |
||
|
HandleTCPClient() берет на себя "протокол приложения". Мы обсудим эту функцию ниже. Таким образом мы убираем из текущей функции часть функциональности сервера, связанную с выдачей "эха". victor8632 04.07.12 в 07:58 |
||
|
У нас есть отдельная функция, которая реализует "эхо" часть нашего сервера. victor8632 04.07.12 в 07:59 |
||
|
Хотя реализация этого протокола приложения содержит всего несколько строк, это хороший стиль программирования, отделить данную часть от остального серверного кода. Это способствует повторному использованию кода. victor8632 04.07.12 в 08:02 |
||
|
HandleTCPClient() получает данные с указанного сокета и посылает их обратно на тот же сокет, эта операция повторяется до тех пор, пока функция recv() возвращает положительное значение (положительное значение сигнализирует о том, что что-то было получено). victor8632 04.07.12 в 08:17 |
