# killall -HUP inetd
Существует много параметров, которые можно применять для управления поведением соединений на базе сокетов — слишком много для подробного описания в этой главе. Для манипулирования параметрами используют функцию setsockopt
:
#include
int setsockopt(int socket, int level, int option_name,
const void *option value, size_t option len);
Задавать параметры можно на разных уровнях иерархии протоколов. Для установки параметров на уровне сокета вы должны задать level
равным SOL_SOCKET
. Для задания параметров на более низком уровне протоколов (TCP, UDP и т.д.) приравняйте параметр level номеру протокола (полученному либо из заголовочного файла netinet/in.h, либо из функции getprotobyname
).
В аргументе option_name
указывается имя задаваемого параметра, аргумент option_value
содержит произвольное значение длиной option_len
байтов, передаваемое без изменений обработчику низкоуровневого протокола.
Параметры уровня сокета определены в заголовочном файле sys/socket.h и включают приведенные в табл. 15.4 значения.
Таблица 15.5
Параметр |
Описание |
SO_DEBUG |
Включает отладочную информацию |
SO_KEEPALIVE |
Сохраняет активными соединения при периодических передачах |
SO_LINGER |
Завершает передачу перед закрытием |
Параметры SO_DEBUG
и SO_KEEPALIVE
принимают целое значение option_value
для установки или включения (1) и сброса или выключения (0). Для параметра SO_LINGER
нужна структура типа linger
, определенная в файле sys/socket.h и задающая состояние параметра и величину интервала задержки.
Функция setsockopt
возвращает 0 в случае успеха и -1 в противном случае. На страницах интерактивного справочного руководства описаны дополнительные параметры и ошибки.
До сих пор в этой главе вы видели, как применяются сокеты для реализации клиент-серверных систем, как локальных, так действующих, в сети. После установки соединения на базе сокетов они ведут себя как низкоуровневые открытые файловые дескрипторы и во многом как двунаправленные каналы.
Теперь необходимо рассмотреть случай множественных клиентов, одновременно подключающихся к серверу. Вы видели, что, когда серверная программа принимает от клиента запрос на соединение, создается новый сокет, а исходный сокет, ожидающий запросы на соединение, остается доступен для последующих запросов. Если сервер не сможет немедленно принять поступившие позже запросы на соединения, они сохранятся в очереди ожидания.
Тот факт, что исходный сокет все еще доступен, и что сокеты ведут себя как файловые дескрипторы, дает нам метод одновременного обслуживания многих клиентов. Если сервер вызовет функцию fork
для создания своей второй копии, открытый сокет будет унаследован новым дочерним процессом. Далее он сможет обмениваться данными с подключившимся клиентом, в то время как основной сервер продолжит прием последующих запросов на соединение. В действительности в вашу программу сервера нужно внести очень простое изменение, показанное в упражнении 15.7.
Поскольку вы создаете дочерние процессы, но не ждете их завершения, следует сделать так, чтобы сервер игнорировал сигналы SIGCHLD
, препятствуя возникновению процессов-зомби.
Упражнение 15.7. Сервер для многочисленных клиентов
1. Программа server4.c начинается так же, как последний рассмотренный сервер с важным добавлением директивы include
для заголовочного файла signal.h. Переменные и процедуры создания и именования сокета остались прежними:
#include
#include
#include
#include
#include
#include
#include
int main() {
int server_sockfd, client_sockfd;
int server_len, client_len;
struct sockaddr_in server_address;
struct sockaddr_in client_address;
server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = htonl(INADDR_ANY);
server_address.sin_port = htons(9734);
server_len = sizeof(server_address);
bind(server_sockfd, (struct sockaddr *)&server_address, server_len);
2. Создайте очередь соединений, игнорируйте подробности завершения дочернего процесса и ждите запросов клиентов:
listen(server_sockfd, 5);
signal(SIGCHLD, SIG_IGN);
while(1) {
char ch;
printf("server waiting\n");
3. Примите запрос на соединение:
client_len = sizeof(client_address);
Читать дальше