UNIX: разработка сетевых приложений - Страница 385
Листинг Д.2. Вывод размера приемного буфера сокета и MSS до и после установления соединения
//sockopt/rcvbuf.c 1 #include "urp.h" 2 #include <netinet/tcp.h> /* для TCP_MAXSEG */ 3 int 4 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 = %dn", 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 = %dn", rcvbuf, mss);27 exit(0);28 }He существует какого-то одного «правильного» вывода для данной программы. Результаты зависят от системы. Некоторые системы (в особенности Solaris 2.5.1 и более ранние версии) всегда возвращают нулевой размер буфера сокета, не давая нам возможности увидеть, что происходит с этим значением в процессе соединения.
До вызова функции
connectconnecttcpdumpМногие реализации после установления соединения округляют размер приемного буфера сокета в большую сторону, чтобы он было кратным MSS. Чтобы узнать размер приемного буфера сокета после установления соединения, можно исследовать пакеты с помощью программы типа
tcpdump7.3. Разместите в памяти структуру
lingerlingstr_cli(stdin, sockfd);ling.l_onoff = 1;ling.l_linger = 0;Setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling));exit(0);Это заставит TCP на стороне клиента прекратить работу путем отправки сегмента RST вместо нормального обмена четырьмя сегментами. Дочерний процесс сервера вызывает функцию
readlineECONNRESETreadline error: Connection reset by peerКлиентский сокет не должен проходить через состояние ожидания TIME_WAIT, даже если клиент выполняет активное закрытие.
7.4. Первый клиент вызывает функции
setsockoptbindconnectbindbindconnectEADDRINUSEbindEADDRINUSEbind7.5. Запускаем программу на узле без поддержки многоадресной передачи (MacOS X 10.2.6).
macosx % sock -s 9999 & запускаем первый сервер с универсальным адресом[1] 29697macosx % sock -s 172.24.37.78 9999 пробуем второй сервер, но без -Аcan't bind local address: Address already in usemacosx % sock -s -A 172.24.37.78 9999 & пробуем опять с -A: работает[2] 29699macosx % sock -s -A 127.0.0.1 9999 & третий сервер с -A; работает[3] 29700macosx % netstat -na | grep 9999tcp4 0 0 127.0.0.1.9999 *.* LISTENtcp4 0 0 206.62.226.37.9999 *.* LISTENtcp4 0 0 *.9999 *.* LISTEN7.6. Теперь попробуем проделать то же на узле с поддержкой многоадресной передачи, но без поддержки параметра
SO_REUSEADDR