52: while ((conn = accept(sock, (struct sockaddr *) &address,
53: &addrLength)) >=0) {
54: printf("----получение данных\n");
55: copyData(conn, 1);
56: printf("----готово\n");
57: close(conn);
58: }
59:
60: if (conn < 0)
61: die("accept");
62:
63: close(sock);
64: return 0;
65: }
Обратите
внимание на то, что IP-адрес, привязанный к сокету, указывает номер порта 4321, но не IP-адрес. Это предоставляет ядру возможность при необходимости воспользоваться локальным IP-адресом.
Код в строках 41–42 требует дополнительного объяснения.
Linux-реализация TCP, как и в остальных системах Unix, вводит ограничения на то, насколько скоро можно повторно использовать кортеж (локальный хост, локальный порт) [136] . Этот код устанавливает опцию на сокет, которая обходит это ограничение и позволяет дважды запускать сервер за короткий период времени. По сходной причине сервер-пример сокета домена Unix удаляет любой существующий файл сокета, прежде чем вызывать
bind
.
136
Для TCP-портов данная комбинация не может использоваться в течение двух минут.
Функция
setsockopt
позволяет устанавливать множество специальных опций для сокета и протокола:
#include <sys/socket.h>
int setsockopt(int sock, int level, int option,
const void * valptr, int vallength);
Первый аргумент — это сокет, для которого определяется опция. Второй аргумент,
level
, указывает тип устанавливаемой опции. В нашем сервере используется
SOL_SOCKET
, что указывает на установку опции обобщенного сокета. Параметр
option
определяет опцию, которая подлежит изменению. Указатель на новое значение опции передается через
valptr
, а размер значения, на которое указывает valptr, передается как
vallength
. Для нашего сервера применяется указатель на ненулевое целое число, которое вводит в действие опцию
SO_REUSEADDR
.
17.5.8. Клиентские приложения TCP
Клиенты TCP подобны клиентам домена Unix. Как правило, сразу же после создания сокета, клиент подключается к серверу с помощью функции
connect
. Единственное различие состоит в способе передачи адреса в
connect
. Вместо того, чтобы использовать имя файла, большинство клиентов TCP отыскивают имя хоста через функцию
getaddrinfo
, которая предоставляет информацию для
connect
.
Ниже приводится несложный TCP-клиент, который взаимодействует с сервером, представленным в предыдущем разделе. Он принимает один аргумент: имя хоста, на котором работает сервер, или его IP-номер (в десятичном представлении с разделительными точками). Во всем остальном программа ведет себя также как клиент сокета домена Unix, показанный ранее в этой главе.
1: /* tclient.с */
2:
3: /* Подключиться к серверу, чье имя хоста или IP-адрес переданы в качестве
4:
аргумента, на порте 4321. После соединения скопировать все содержимое
5: stdin в сокет, затем завершить работу. */
6:
7: #include <arpa/inet.h>
8: #include <netdb.h>
9: #include <netinet/in.h>
10: #include <stdio.h>
11: #include <stdlib.h>
12: #include <string.h>
13: #include <sys/socket.h>
14: #include <unistd.h>
15:
16: #include "sockutil.h" /* некоторые служебные функции */
17:
18: int main(int argc, const char ** argv) {
19: struct addrinfo hints, *addr;
20: struct sockaddr_in * addrinfo;
21: int rc;
22: int sock;
23:
24: if (argc !=2) {
25: fprintf(stderr, "поддерживается только одиночный аргумент\n");
26: return 1;
27: }
28:
29: memset(&hints, 0, sizeof(hints));
30:
31: hints.ai_socktype = SOCK_STREAM;
32: hints.ai_flags = AI_ADDRCONFIG;
33: if ((rc = getaddrinfo(argv[1], NULL, &hints, &addr))) {
34: fprintf(stderr, "сбой поиска имени хоста: %s\n",
35: gai_strerror(rc));
36: return 1;
37: }
38:
39: /* это позволяет получить доступ к sin_family и sin_port
40: (которые расположены там же, где и sin6_family и sin6_port) */
43: if ((sock = socket(addrInfo->sin_family, addr->ai_socktype,
44: addr->ai_protocol)) < 0)
45: die("socket");
46:
47: addrInfo->sin_port = htons(4321);
48:
49: if (connect(sock, (struct sockaddr *) addrinfo,
50: addr->ai_addrlen))
51: die("connect");
52:
53: freeaddrinfo(addr);
54:
55: copyData(0, sock);
56:
57: close(sock);
58:
59: return 0;
60: }
17.6. Использование дейтаграмм UDP
Наряду с тем, что большинство приложений пользуются преимуществами потокового протокола TCP, некоторые предпочитают применять UDP. Давайте рассмотрим несколько причин, по которым дейтаграммная модель без установления соединений, предоставляемая UDP, может оказаться весьма полезной.