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

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

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

2.1 IPv4 TCP клиент

prefer211 16.05.12 в 17:14

0

Между клиентом и сервером существует важное отличие - на определенных этапах процесса передачи информации они по-разному используют интерфейс сокетов. Сначала мы рассмотрим клиент.

Ostrolist 22.05.12 в 17:27

0

Его работа состоит в том, чтобы установить соединение с пассивно ожидающим контакта сервером.

Ostrolist 22.05.12 в 17:30

0

Связь типичного TCP клиента, включает в себя четыре основных этапа:

IamSadist 16.05.12 в 18:29

0

1. Создание сокета TCP, используя socket().

prefer211 17.05.12 в 07:26

0

2. Установление соединения с сервером, используя connect().

prefer211 17.05.12 в 07:27

0

3. Прием и передача, используя send и recv().

prefer211 17.05.12 в 07:28

0

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

prefer211 17.05.12 в 07:28

0

TCPEchoClient4.c является реализацией TCP эхо клиента для IPv4.
TCPEchoClient4.c
1 #include
2 #include
3 #include
4 #include
5 #include types.h>
6 #include socket.h>
7 #include in.h>
8 #include inet.h>
9 #include "Practical.h"
10
11 int main(int argc, char *argv[]) {
12
13 if (argc<3||argc > 4) // Проверка корректности количества аргументов
14 DieWithUserMessage("Parameter(s)",
15 " []");
16
17 char *servIP = argv[1]; // Первый аргумент: IP адрес сервера (строка с четырьмя числами через точку)
18 char *echoString = argv[2]; // Второй аргумент: строка для эхо ответа
19
20 // Третий агумент (необязательный): порт сервера (числовое значение). 7 - хорошо известный эхо порт
21 in_port_t servPort = (argc == 4) ? atoi(argv[3]) : 7;
22
23 // Создаем достоверный, потоковый сокет, используя ТСР
24 int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
25 if (sock < 0)
26 DieWithSystemMessage("socket() failed");
27
28 // Конструируем структуру адреса сервера
29 struct sockaddr_in servAddr; // Адрес сервера
30 memset(&servAddr, 0, sizeof(servAddr)); // Обнуляем структуру
31 servAddr.sin_family = AF_INET; // адрес IPv4 семейства
32 // Конвертируем адрес
2.1 IPv4 TCP Client 13
33 int rtnVal = inet_pton(AF_INET, servIP, &servAddr.sin_addr.s_addr);
34 if (rtnVal == 0)
35 DieWithUserMessage("inet_pton() failed", "invalid address string");
36 else if (rtnVal < 0)
37 DieWithSystemMessage("inet_pton() failed");
38 servAddr.sin_port = htons(servPort); // Порт сервера
39
40 // Устанавливаем соединение с эхо сервером
41 if (connect(sock, (struct sockaddr *) &servAddr, sizeof(servAddr)) < 0)
42 DieWithSystemMessage("connect() failed");
43
44 size_t echoStringLen = strlen(echoString); // Определяем длину ввода
45
46 // Посылаем строку на сервер
47 ssize_t numBytes = send(sock, echoString, echoStringLen, 0);
48 if (numBytes < 0)
49 DieWithSystemMessage("send() failed");
50 else if (numBytes != echoStringLen)
51 DieWithUserMessage("send()", "sent unexpected number of bytes");
52
53 // Получаем строку обратно от сервера
54 unsigned int totalBytesRcvd = 0; // Количество полученных байт всего
55 fputs("Received: ", stdout); // Устанавливаем вывод (печать) для эхо строки
56 while (totalBytesRcvd < echoStringLen) {
57 char buffer[BUFSIZE]; // I/O buffer
58 /* Получаем размер буфера (минус 1, чтобы оставить место для символа окончания строки) в байтах от отправителя */
60 numBytes = recv(sock, buffer, BUFSIZE - 1, 0);
61 if (numBytes < 0)
62 DieWithSystemMessage("recv() failed");
63 else if (numBytes == 0)
64 DieWithUserMessage("recv()", "connection closed prematurely");
65 totalBytesRcvd += numBytes; // Сохраняем результат расчета общего количества байт
66 buffer[numBytes] = '\0'; // Символ окончания строки!
67 fputs(buffer, stdout); // Выводим эхо буфер
68 }
69
70 fputc('\n', stdout); // Выводим завершающий перевод строки
71
72 close(sock);
73 exit(0);
74 }

victor8632 31.05.12 в 08:53

0

TCPEchoClient4.c

prefer211 16.05.12 в 17:14

0

TCPEchoClient4.c делает следующее:

prefer211 16.05.12 в 17:14

0

1. Настройка приложения и разбор параметров: строки 1-21

Ostrolist 22.05.12 в 17:33

0

· Включение файлов: строки 1-9

Ostrolist 22.05.12 в 17:36

0

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

Ostrolist 22.05.12 в 17:43

0

Мы используем наш собственный заголовочный файл Practical.h с прототипами созданных нами функций, которые мы опишем ниже.

Ostrolist 22.05.12 в 17:45

0

· Разбор символьных параметров и проверка на здравый смысл: строки 13-21

Ostrolist 22.05.12 в 17:49

0

IPv4 адрес и текстовая строка для отправки эхо-сервису передаются в качестве первых двух параметров.

Ostrolist 22.05.12 в 17:51

0

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

Ostrolist 22.05.12 в 17:52

0

Если порт не указывается, клиент использует общепринятый номер порта для эхо-сервиса - 7.

Ostrolist 22.05.12 в 17:57

0

2. Создание TCP сокета: строки 23-26

Ostrolist 23.05.12 в 02:09

0

Мы создаем сокет с помощью функции socket().

Ostrolist 23.05.12 в 02:10

0

В качестве параметров при создании мы указываем, что создаем сокет для IPv4 (AF_INET), использующий потоковый протокол (SOCK_STREAM), называемый TCP (IPPROTO_TCP). В случае успешного выполнения функция socket() возвращает целочисленный дескриптор или "хэндл" для созданного сокета.

Ostrolist 23.05.12 в 02:59

0

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

Ostrolist 23.05.12 в 03:02

0

3. Подготовка адреса и установка соединения: строки 28-42

victor8632 31.05.12 в 07:48

0

Подготовка структуры sockaddr_in для сохранения адреса сервера: строки 29-30

victor8632 31.05.12 в 07:50

0

Для подключения к сокету, мы должны определить адрес и порт, к которому хотим подключиться.

victor8632 31.05.12 в 07:51

0

Определенная нами структура sockaddr_in будет "контейнером" для этой информации.

victor8632 31.05.12 в 07:53

0

Вызов функции memset() гарантирует, что ни в одной части структуры мы явно не установили нулевое значение.

victor8632 31.05.12 в 08:29

0

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

Ostrolist 01.06.12 в 07:27

1

Заполнение структуры sockaddr_in: строки 31-38

victor8632 31.05.12 в 08:29

0

Мы должны установить адрес сети (AF_INET), интернет адрес, и номер порта.

victor8632 31.05.12 в 08:31

0

Функция inet_pton() преобразует строковое представление интернет адреса сервера (передается как аргумент командной строки в формате четырехкомпонентной системы записи) в 32-битное бинарное представление.

victor8632 31.05.12 в 08:35

0

Номер порта сервера был конвертирован из командной строки в бинарное представление ранее; вызов функции htons() (“host to network short”) гарантирует, что двоичное представление будет отформатировано в соответствии с требованиями API. (Необходимость этого описана в Главе 5.)

victor8632 31.05.12 в 08:41

0

Подключение: строки 40-42

victor8632 31.05.12 в 08:36

0

Функция connect() устанавливает соединение между нашим сокетом и сокетом, определённым с помощью адреса и порта внутри структуры sockaddr_in.

Ostrolist 01.06.12 в 07:31

0

Поскольку Sockets API является общим, указатель на структуру адреса sockaddr_in (которая является специфичной для IPv4) должен быть преобразован к общему типу (sockaddr *). Кроме того, функции должен быть предоставлен фактический размер структуры с данными адреса.

Ostrolist 01.06.12 в 07:41

0

4. Посылаем эхо строку на сервер: строки 44-51

victor8632 31.05.12 в 10:35

0

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

victor8632 31.05.12 в 10:37

0

Указатель на эхо строку подставляется в вызов функции send(); сама строка была сохранена где-то (как аргумент командной строки) при запуске приложения.

victor8632 31.05.12 в 10:49

0

Нам совершенно все равно, где находится строка, нужен только знать адрес первого байта строки и количество байт, которые нужно отправить. (Нужно отметить, что мы не будем посылать символ окончания строки ('\0'), который стоит в конце строки-аргумента, как и в конце любой другой строки в Си).

victor8632 31.05.12 в 10:54

0

send() возвращает количество переданных байт, если функция отработала успешно, в противном случае будет возвращено -1.

victor8632 31.05.12 в 10:56

0

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

victor8632 01.06.12 в 07:23

0

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

victor8632 01.06.12 в 07:25

0

Тем не менее, это хорошая идея включать в код проверку на ошибки, так как они могут возникнуть в некоторых контекстах.

victor8632 01.06.12 в 07:32

0

5. Получение эхо ответа от сервера: строки 53-70

victor8632 01.06.12 в 07:32

0

ТСР - это протокол потока байтов. Одним из подразумеваемых положений данного типа протокола является то, что границы функции send() являются размытыми.

victor8632 01.06.12 в 07:38

0

Другими словами: байты посланные через вызов функции send() на одном конце соединения могут быть не все возвращены посредством одного вызова функции recv() на его другом конце. (Мы обсудим этот вопрос более подробно в главе 7.).

victor8632 01.06.12 в 07:42

0

Так что мы должны получать байты в цикле до тех пор, пока не получим столько байт, сколько послали.

victor8632 01.06.12 в 07:45

0

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

victor8632 01.06.12 в 07:52

0

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

victor8632 01.06.12 в 09:00

0

Получаем блок байт: строки 57-65

victor8632 31.05.12 в 10:44

0
Минутку...

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