Чтение онлайн

ЖАНРЫ

UNIX: разработка сетевых приложений
Шрифт:

local address 127.0.0.1:52331

По рис. 1.7 видно, что когда мы запускаем программу первые два раза, аргументом командной строки является IP-адрес в разных сетях Ethernet. Ядро присваивает локальный IP-адрес первичному адресу интерфейса в соответствующей сети Ethernet. При вызове функции

connect
на сокете UDP ничего не отправляется на этот узел — это полностью локальная операция, которая сохраняет IP-адрес и порт собеседника. Мы также видим, что вызов функции connect на неприсоединенном сокете UDP также присваивает сокету динамически назначаемый порт.

ПРИМЕЧАНИЕ

К

сожалению, эта технология действует не во всех реализациях, что особенно касается ядер, происходящих от SVR4. Например, это не работает в Solaris 2.5, но работает в AIX, Digital Unix, Linux, MacOS X и Solaris 2.6.

8.15. Эхо-сервер TCP и UDP, использующий функцию select

Теперь мы объединим наш параллельный эхо-сервер TCP из главы 5 и наш последовательный эхо-сервер UDP из данной главы в один сервер, использующий функцию

select
для мультиплексирования сокетов TCP и UDP. В листинге 8.14 представлена первая часть этого сервера.

Листинг 8.14. Первая часть эхо-сервера, обрабатывающего сокеты TCP и UDP при помощи функции select

//udpcliserv/udpservselect01.c

1 #include "unp.h"

2 int

3 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

14-22
Создается прослушиваемый сокет TCP, который связывается с заранее известным портом сервера. Мы устанавливаем параметр сокета
SO_REUSEADDR
в случае, если на этом порте существуют соединения.

Создание сокета UDP

23-29
Также создается сокет UDP и связывается с тем же портом. Даже если один и тот же порт используется для сокетов TCP и UDP, нет необходимости устанавливать параметр сокета
SO_REUSEADDR
перед этим вызовом функции
bind
, поскольку порты TCP не зависят от портов UDP.

В листинге 8.15 показана вторая часть нашего сервера.

Листинг 8.15. Вторая половина эхо-сервера, обрабатывающего TCP и UDP при помощи функции select

udpcliserv/udpservselect01.c

30 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)

38 continue; /* назад в for */

39 else

40 err_sys("select error");

41 }

42 if (FD_ISSET(listenfd, &rset)) {

43 len = sizeof(cliaddr);

44 connfd = Accept(listenfd, (SA*)&cliaddr, &len);

45 if ((childpid = Fork) == 0) { /* дочерний процесс */

46 Close(listenfd); /* закрывается прослушиваемый сокет */

47 str_echo(connfd); /* обработка запроса */

48 exit(0);

49 }

50 Close(connfd); /* родитель закрывает присоединенный сокет */

51 }

52 if (FD_ISSET(udpfd, &rset)) {

53 len = sizeof(cliaddr);

54 n = Recvfrom(udpfd, mesg, MAXLINE, 0, (SA*)&cliaddr, &len);

55 Sendto(udpfd, mesg, n, 0, (SA*)&cliaddr, len);

Поделиться с друзьями: