UNIX: разработка сетевых приложений
Листинг Д.2. Вывод размера приемного буфера сокета и MSS до и после установления соединения
//sockopt/rcvbuf.c1 #include "urp.h"2 #include <netinet/tcp.h> /* для TCP_MAXSEG */3 int4 main(int argc, char **argv)5 {6 int sockfd, rcvbuf, mss;7 socklen_t len;8 struct sockaddr_in servaddr;9 if (argc != 2)10 err_quit("usage: rcvbuf <Ipaddress>");11 sockfd = Socket(AF_INET, SOCK_STREAM, 0);12 len = sizeof(rcvbuf);13 Getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, &len);14 len = sizeof(mss);15 Getsockopt(sockfd, IPPROTO_TCP, TCP_MAXSEG, &mss, &len);16 printf("defaults: SO_RCVBUF = %d. MSS = %d\n", rcvbuf, mss);17 bzero(&servaddr, sizeof(servaddr));18 servaddr.sin_family = AF_INET;19 servaddr.sin_port = htons(13); /* сервер времени и даты */20 Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);21 Connect(sockfd, (SA*)&servaddr, sizeof(servaddr));22 len = sizeof(rcvbuf);23 Getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, &len);24 len = sizeof(mss);25 Getsockopt(sockfd, IPPROTO_TCP, TCP_MAXSEG, &mss, &len);26 printf("after connect: SO_RCVBUF = %d, MSS = %d\n", rcvbuf, mss);27 exit(0);28 }He существует какого-то одного «правильного» вывода для данной программы. Результаты зависят от системы. Некоторые системы (в особенности Solaris 2.5.1 и более ранние версии) всегда возвращают нулевой размер буфера сокета, не давая нам возможности увидеть, что происходит с этим значением в процессе соединения.
До вызова функции
выводится значение MSS по умолчанию (часто 536 или 512), а значение, выводимое после вызова функции connect, зависит от возможных параметров MSS, полученных от собеседника. Например, в локальной сети Ethernet после выполнения функции connect MSS может иметь значение 1460. Однако после соединения (connect) с сервером в удаленной сети значение MSS может быть равно значению по умолчанию, если только ваша система не поддерживает обнаружение транспортной MTU. Если это возможно, запустите во время работы вашей программы программуconnectили подобную ей (см. раздел В.5), чтобы увидеть фактическое значение параметра MSS в сегменте SYN, полученном от собеседника.tcpdumpМногие реализации после установления соединения округляют размер приемного буфера сокета в большую сторону, чтобы он было кратным MSS. Чтобы узнать размер приемного буфера сокета после установления соединения, можно исследовать пакеты с помощью программы типа
и посмотреть, каков размер объявленного окна TCP.tcpdump7.3. Разместите в памяти структуру
по имениlingerи проинициализируйте ее следующим образом:lingstr_cli(stdin, sockfd);ling.l_onoff = 1;ling.l_linger = 0;Setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling));exit(0);Это заставит TCP на стороне клиента прекратить работу путем отправки сегмента RST вместо нормального обмена четырьмя сегментами. Дочерний процесс сервера вызывает функцию
, возвращает ошибкуreadlineи выводит следующее сообщение:ECONNRESETreadline error: Connection reset by peerКлиентский сокет не должен проходить через состояние ожидания TIME_WAIT, даже если клиент выполняет активное закрытие.
7.4. Первый клиент вызывает функции
,setsockoptиbind. Но если второй клиент вызовет функциюconnectмежду вызовами функцийbindиbindпервого клиента, возвращается ошибкаconnect. Но как только первый клиент установит соединение с собеседником, вызов функцииEADDRINUSEвторого клиента будет работать, поскольку сокет первого клиента уже присоединен. В случае возвращения ошибкиbindвторому клиенту следует вызыватьEADDRINUSEнесколько раз, а не останавливаться при появлении первой ошибки — это единственный способ справиться с данной ситуацией.bind7.5. Запускаем программу на узле без поддержки многоадресной передачи (MacOS X 10.2.6).
macosx % <b>sock -s 9999 &</b> <i>запускаем первый сервер с универсальным адресом</i>[1] 29697macosx % <b>sock -s 172.24.37.78 9999</b> <i>пробуем второй сервер, но без -А</i>can't bind local address: Address already in usemacosx % <b>sock -s -A 172.24.37.78 9999 &</b> <i>пробуем опять с -A: работает</i>[2] 29699macosx % <b>sock -s -A 127.0.0.1 9999 &</b> <i>третий сервер с -A; работает</i>[3] 29700macosx % <b>netstat -na | grep 9999</b>tcp4 0 0 127.0.0.1.9999 *.* LISTENtcp4 0 0 206.62.226.37.9999 *.* LISTENtcp4 0 0 *.9999 *.* LISTEN7.6. Теперь попробуем проделать то же на узле с поддержкой многоадресной передачи, но без поддержки параметра
(Solaris 9).SO_REUSEADDR