UNIX: разработка сетевых приложений
9 err_quit("usage: udpcli <Ipaddress>");10 sockfd = Socket(AF_INET, SOCK_DGRAM, 0);11 bzero(&servaddr, sizeof(servaddr));12 servaddr.sin_family = AF_INET;13 servaddr.sin_port = htons(SERV_PORT);14 Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);15 Connect(sockfd, (SA*)&servaddr, sizeof(servaddr));16 len = sizeof(cliaddr);17 Getsockname(sockfd, (SA*)&cliaddr, &len);18 printf("local address %s\n", Sock_ntop((SA*)&cliaddr, len));19 exit(0);20 }Если мы запустим программу на узле
с несколькими сетевыми интерфейсами, то получим следующий вывод:freebsdfreebsd % <b>udpcli09 206.168.112.96</b>local address 12.106.32.254:52329freebsd % <b>udpcli09 192.168.42.2</b>local address 192.168.42.1:52330freebsd % <b>udpcli09 127.0.0.1</b>local address 127.0.0.1:52331По рис. 1.7 видно, что когда мы запускаем программу первые два раза, аргументом командной строки является IP-адрес в разных сетях Ethernet. Ядро присваивает локальный IP-адрес первичному адресу интерфейса в соответствующей сети Ethernet. При вызове функции
на сокете UDP ничего не отправляется на этот узел — это полностью локальная операция, которая сохраняет IP-адрес и порт собеседника. Мы также видим, что вызов функции connect на неприсоединенном сокете UDP также присваивает сокету динамически назначаемый порт.connectПРИМЕЧАНИЕК сожалению, эта технология действует не во всех реализациях, что особенно касается ядер, происходящих от SVR4. Например, это не работает в Solaris 2.5, но работает в AIX, Digital Unix, Linux, MacOS X и Solaris 2.6.
8.15. Эхо-сервер TCP и UDP, использующий функцию select
Теперь мы объединим наш параллельный эхо-сервер TCP из главы 5 и наш последовательный эхо-сервер UDP из данной главы в один сервер, использующий функцию
для мультиплексирования сокетов TCP и UDP. В листинге 8.14 представлена первая часть этого сервера.selectЛистинг 8.14. Первая часть эхо-сервера, обрабатывающего сокеты TCP и UDP при помощи функции select
//udpcliserv/udpservselect01.c1 #include "unp.h"2 int3 main(int argc, char **argv)4 {5 int listenfd, connfd, udpfd, nready, maxfdp1;6 char mesg[MAXLINE];7 pid_t childpid;8 fd_set rset;9 ssize_t n;10 socklen_t len;11 const int on = 1;12 struct sockaddr_in cliaddr, servaddr;13 void sig_chld(int);14 /* создание прослушиваемого сокета TCP */15 listenfd = Socket(AF_INET, SOCK_STREAM, 0);16 bzero(&servaddr, sizeof(servaddr));17 servaddr.sin_family = AF_INET;18 servaddr.sin_addr.s_addr = htonl(INADDR_ANY);19 servaddr.sin_port = htons(SERV_PORT);20 Setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));21 Bind(listenfd, (SA*)&servaddr, sizeof(servaddr));22 Listen(listenfd, LISTENQ);23 /* создание сокета UDP */24 udpfd = Socket(AF_INET, SOCK_DGRAM, 0);25 bzero(&servaddr, sizeof(servaddr));26 servaddr.sin_family = AF_INET;27 servaddr.sin_addr.s_addr = htonl(INADDR_ANY);28 servaddr.sin_port = htons(SERV_PORT);29 Bind(udpfd, (SA*)&servaddr, sizeof(servaddr));Создание прослушиваемого сокета TCPСоздается прослушиваемый сокет TCP, который связывается с заранее известным портом сервера. Мы устанавливаем параметр сокета14-22в случае, если на этом порте существуют соединения.SO_REUSEADDRСоздание сокета UDPТакже создается сокет UDP и связывается с тем же портом. Даже если один и тот же порт используется для сокетов TCP и UDP, нет необходимости устанавливать параметр сокета23-29перед этим вызовом функцииSO_REUSEADDR, поскольку порты TCP не зависят от портов UDP.bindВ листинге 8.15 показана вторая часть нашего сервера.
Листинг 8.15. Вторая половина эхо-сервера, обрабатывающего TCP и UDP при помощи функции select
udpcliserv/udpservselect01.c30 Signal(SIGCHLD, sig_chld); /* требуется вызвать waitpid() */31 FD_ZERO(&rset);32 maxfdp1 = max(listenfd, udpfd) + 1;33 for (;;) {34 FD_SET(listenfd, &rset);35 FD_SET(udpfd, &rset);36 if ((nready = select(maxfdp1, &rset, NULL, NULL, NULL)) < 0) {37 if (errno == EINTR)