W. R. Stevens UNIX : Network Programming Networking APIs Глава 1
Глава 1
Код для этой главы находится в архивном каталоге /intro .
Рассматривается простой клиент-серверный сценарий :
клиент устанавливает простое ТСР-соединение с сервером ,
а сервер в ответ посылает текущее время .
При чтении из сокета функцию read нужно вызывать циклически ,
и прерывать функцию тогда , когда она возвращает 0 либо ошибку .
Клиент :
int sockfd, n;
char recvline[MAXLINE + 1];
struct sockaddr_in servaddr;
if (argc != 2)
err_quit("usage: a.out ");
if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
err_sys("socket error");
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(13); /* daytime server */
if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)
err_quit("inet_pton error for %s", argv[1]);
if (connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) < 0)
err_sys("connect error");
while ( (n = read(sockfd, recvline, MAXLINE)) > 0) {
recvline[n] = 0; /* null terminate */
if (fputs(recvline, stdout) == EOF)
err_sys("fputs error");
}
if (n < 0)
err_sys("read error");
exit(0);
Функция socket создает потоковый сокет (SOCK_STREAM) и возвращает
целочисленный дескриптор.
Если ее вызов оказывается неудачным , выполнение программы прерывается
с помощью err_sys .
Структура sockaddr_inc - заполняется ip-адресом сервера и
номером порта сервера .
Ip-адрес и номер порта должны иметь специфический формат ,
который формируется с помощью
htons (для порта) и inet_pton .
Далее функция connect устанавливает соединение с сервером ,
который должен быть запущен раньше .
Чтение и отображение ответа сервера выполняется с помощью
стандартной функции fputs .
Сокеты - процесс потоковый и циклический , и для этого используется цикл ,
внутри которого работает функция read . И цикл этот может быть прерван двояко -
когда read возвращает 0 (соединение разорвано , поскольку передача закончена )
либо -1 (произошла ошибка) .
Стивенс использует т.н. функции-обертки , например функция-обертка для socket ,
которую можно найти в каталоге /lib/wrapsock.c :
int
Socket(int family, int type, int protocol)
{
int n;
if ( (n = socket(family, type, protocol)) < 0)
err_sys("socket error");
return(n);
}
Удобство в их использовании в том , что они сами обрабатывают ошибки и
возвращают нужные коды .
Если системная ("ядрёная") юниксовая функция вызывает ошибку ,
глобальной переменной errno
присваивается положительное значение . Эти значения определены в sys/errno.h .
Код сервера выглядит так :
int listenfd, connfd;
struct sockaddr_in servaddr;
char buff[MAXLINE];
time_t ticks;
listenfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(13); /* daytime server */
Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
Listen(listenfd, LISTENQ);
for ( ; ; ) {
connfd = Accept(listenfd, (SA *) NULL, NULL);
ticks = time(NULL);
snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));
Write(connfd, buff, strlen(buff));
Close(connfd);
}
Функция bind связывает порт сервера с клиентом .
Вызов функций socket , bind , listen - обычная последовательность
для любого tcp-сервера.
Функция accept полностью блокирует сервер до тех пор , пока он не соединится .
Соединение устанавливается с помощью т.н. 3-этажного рукопожатия ,
accept возвращает присоединенный дескриптор .
Соединение закрывается с помощью функции close .
Показанный сервер последовательный , т.е. нескольких клиентов он может обслужить
лишь по очереди . Для создания параллельного сервера нужно использовать
fork() либо threads .
На следующем рисунке представлена модель взаимодействия открытых систем - Open System Interconnection -
где она сравнивается со стеком протоколов интернета .
|
2 нижних уровня OSI соответствуют аппаратному обеспечению , программиста оно мало касается ,
отсюда нужно лишь знать MTU - максимальную единицу передачи .
Сетевой уровень - это IPV4 / IPV6 .
Транспортный уровень - это TCP/UDP .
3 верхних уровня соответствуют уровню приложения .
В этой книге описываются сокеты , которые являются интерфейсом между уровнем приложений
и транспортным уровнем .
В юниксе для получения системной сетевой информации есть несколько команд :
1 netstat -ni : дает информацию об интерфейсах
netstat -nl : показывает активные соединения
netstat -rn : показывает таблицу маршрутизации
2 ifconfig eth0 : получить информацию для интерфейса
3 ping
|