sockets/is_echo_cl.c
#include "inet_sockets.h"
#include "tlpi_hdr.h"
#define BUF_SIZE 100
int
main(int argc, char *argv[])
{
int sfd;
ssize_t numRead;
char buf[BUF_SIZE];
if (argc!= 2 || strcmp(argv[1], "-help") == 0)
usageErr("%s host\n", argv[0]);
sfd = inetConnect(argv[1], "echo", SOCK_STREAM);
if (sfd == -1)
errExit("inetConnect");
switch (fork()) {
case -1:
errExit("fork");
case 0:
/* Потомок считывает ответ сервера и направляет его в стандартный вывод */
for (;;) {
numRead = read(sfd, buf, BUF_SIZE);
if (numRead <= 0)
/* Выходим при завершении файла или ошибке */
break;
printf("%.*s", (int) numRead, buf);
}
exit(EXIT_SUCCESS);
default: /* Родитель записывает в сокет стандартный ввод */
for (;;) {
numRead = read(STDIN_FILENO, buf, BUF_SIZE);
if (numRead <= 0)
/* Выходим из цикла при завершении файла или ошибке */
break;
if (write(sfd, buf, numRead)!= numRead)
fatal("write() failed");
}
/* Закрываем записывающий канал, чтобы сервер увидел конец файла */
if (shutdown(sfd, SHUT_WR) == -1)
errExit("shutdown");
exit(EXIT_SUCCESS);
}
}
sockets/is_echo_cl.c
Системные вызовы recv() и send() выполняют операции ввода/вывода с подключенными сокетами. Они предоставляют специальные возможности, недоступные в традиционных вызовах read() и write().
#include
ssize_t recv(int
Возвращает количество полученных байтов, 0, если обнаружился конец файла, или -1 при ошибке
ssize_t send(int
Возвращает количество отправленных байтов или -1 при ошибке
Возвращаемые значения и три первых аргумента вызовов recv() и send() такие же, как у read() и write(). Последний аргумент, flags, представляет собой битовую маску, влияющую на выполнение ввода/вывода. Вызов recv() поддерживает следующие флаги, к которым можно применять побитовое ИЛИ.
• MSG_DONTWAIT — выполняет неблокирующее чтение. При отсутствии доступных данных немедленно возвращает ошибку EAGAIN. Того же можно добиться с помощью вызова fcntl(), если перевести сокет в неблокирующий режим (O_NONBLOCK); разница лишь в том, что флаг MSG_DONTWAIT позволяет делать неблокирующими отдельные вызовы.
• MSG_OOB — принимает из сокета внеканальные данные. Эта возможность кратко описывается в подразделе 57.13.1.
• MSG_PEEK — получает из буфера сокета копию запрашиваемых байтов, не удаляя их оттуда. Позже данные можно опять прочитать, используя вызов recv() или read().
• MSG_WAITALL — обычно вызов recv() возвращает меньше байтов, чем было запрошено (length) и чем доступно в сокете. При указании флага MSG_WAITALL вызов заблокируется, пока не будут получены length байтов. Но даже в этом случае можно получить меньше байтов, чем указано в запросе, если:
• был перехвачен сигнал;
• удаленное приложение на другом конце сокета разорвало соединение;
• были обнаружены внеканальные данные (см. подраздел 57.13.1);
• сообщение, полученное из датаграммного сокета, занимает меньше length байтов;
• в сокете произошла ошибка.
Флаг MSG_WAITALL может заменить собой функцию readn(), представленную в листинге 57.1, с той лишь разницей, что не перезагружается в случае прерывания обработчиком сигнала.
Все флаги, перечисленные выше, входят в стандарт SUSv3. Исключение составляет флаг MSG_DONTWAIT, который, тем не менее, доступен в ряде других реализаций UNIX. Флаг MSG_WAITALL появился в программном интерфейсе сокетов позже остальных, поэтому в отдельных старых системах не представлен.
Вызов send() поддерживает следующие флаги, к которым можно применять побитовое ИЛИ.
• MSG_DONTWAIT — выполняет неблокирующую запись. Если данные нельзя передать немедленно (поскольку исходящий буфер сокета заполнен), то операция не блокируется, а сразу же возвращает ошибку EAGAIN. Как и в случае с recv(), того же результата можно достичь, установив сокету флаг O_NONBLOCK.
• MSG_MORE (начиная с Linux 2.4.4) — этот флаг используется в TCP-соединениях и является эквивалентом параметра TCP_CORK, передаваемого сокету (см. раздел 57.4); разница лишь в том, что он обеспечивает закупоривание данных для каждого отдельного вызова. Начиная с версии ядра 2.6 MSG_MORE можно применять и для датаграммных сокетов, но в таком случае он будет иметь другое значение. Если указать его в последовательных вызовах send() или sendto(), то данные будут упакованы в единую датаграмму; она будет передана только при выполнении следующего вызова, в котором этот флаг не указан. (Linux также поддерживает аналогичный параметр для сокета — UDP_CORK; он позволяет группировать данные из вызовов send() или sendto() в единую датаграмму и отсылает их, только если его отключить.) Флаг MSG_MORE никак не влияет на работу сокетов домена UNIX.