1. Применение нашей функции inetListen() для создания слушающего сокета, listenFd, привязанного к универсальному IP-адресу и порту, чей номер указан в виде единственного аргумента командной строки (его можно задать в виде числа или имени службы). Аргумент len возвращает размер структуры с адресом для сокета текущего домена. Позже это значение передается в вызов malloc(), где на его основе выделяется буфер для хранения адреса сокета, полученного из вызовов getsockname() и getpeername().
2. Использование функции inetConnect() для создания второго сокета, connFd, с помощью которого сокету, созданному в предыдущем шаге, отправляется запрос на соединение.
3. Вызов accept() для слушающего сокета. В результате создается третий сокет, acceptFd, соединенный с сокетом, созданным на шаге 2.
4. Применение вызовов getsockname() и getpeername() для получения локального и удаленного адресов двух соединенных сокетов, connFd и acceptFd. После каждого из этих вызовов программа обращается к функции inetAddressStr() для преобразования адреса в формат, пригодный для печати.
5. Приостановка работы на несколько секунд, чтобы запустить утилиту netstat (описана в разделе 57.7) и подтвердить информацию об адресе сокета.
Пример выполнения этой программы показан в следующей сессии командной строки:
$ ./socknames 55555 &
getsockname(connFd): (localhost, 32835)
getsockname(acceptFd): (localhost, 55555)
getpeername(connFd): (localhost, 55555)
getpeername(acceptFd): (localhost, 32835)
[1] 8171
$ netstat — a | egrep '(Address|55555)'
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 *:55555 *:* LISTEN
tcp 0 0 localhost:32835 localhost:55555 ESTABLISHED
tcp 0 0 localhost:55555 localhost:32835 ESTABLISHED
Как видите, подключенный сокет (connFd) был привязан к динамическому порту под номером 32835. Команда netstat выводит сведения обо всех трех сокетах, созданных нашей программой, и позволяет проверить информацию о номерах портов двух соединенных сокетов, которые находятся в состоянии ESTABLISHED (описанном в подразделе 57.6.3).
Листинг 57.3. Использование вызовов getsockname() и getpeername()
sockets/socknames.c
#include "inet_sockets.h" /* Определяет наши функции для работы с сокетами */
#include "tlpi_hdr.h"
int
main(int argc, char *argv[])
{
int listenFd, acceptFd, connFd;
socklen_t len; /* Размер буфера с адресом сокета */
void *addr; /* Буфер для адреса сокета */
char addrStr[IS_ADDR_STR_LEN];
if (argc!= 2 || strcmp(argv[1], "-help") == 0)
usageErr("%s service\n", argv[0]);
listenFd = inetListen(argv[1], 5, &len);
if (listenFd == -1)
errExit("inetListen");
connFd = inetConnect(NULL, argv[1], SOCK_STREAM);
if (connFd == -1)
errExit("inetConnect");
acceptFd = accept(listenFd, NULL, NULL);
if (acceptFd == -1)
errExit("accept");
addr = malloc(len);
if (addr == NULL)
errExit("malloc");
if (getsockname(connFd, addr, &len) == -1)
errExit("getsockname");
printf("getsockname(connFd): %s\n",
inetAddressStr(addr, len, addrStr, IS_ADDR_STR_LEN));
if (getsockname(acceptFd, addr, &len) == -1)
errExit("getsockname");
printf("getsockname(acceptFd): %s\n",
inetAddressStr(addr, len, addrStr, IS_ADDR_STR_LEN));
if (getpeername(connFd, addr, &len) == -1)
errExit("getpeername");
printf("getpeername(connFd): %s\n",
inetAddressStr(addr, len, addrStr, IS_ADDR_STR_LEN));
if (getpeername(acceptFd, addr, &len) == -1)
errExit("getpeername");
printf("getpeername(acceptFd): %s\n",
inetAddressStr(addr, len, addrStr, IS_ADDR_STR_LEN));
sleep(30); /* Дает нам время для запуска netstat(8) */
exit(EXIT_SUCCESS);
}
sockets/socknames.c
Представление о некоторых особенностях работы протокола TCP поможет нам отлаживать приложения, основанные на потоковых сокетах, и в ряде случаев делать их более эффективными. В следующих разделах мы рассмотрим:
• формат TCP-сегментов;
• модель подтверждения в TCP;
• машину состояний протокола TCP;
• установку и разрыв TCP-соединений;
• состояние TIME_WAIT.
57.6.1. Формат TCP-сегментов
На рис. 57.2 показан формат TCP-сегмента, который передается между разными концами TCP-соединения. Данные поля означают следующее.
•