3. Запрашивать
4. Создавать очередь, которая сможет хранить сведения о пяти клиентах. Оставшиеся шаги повторяются многократно:
5. Ожидать запросов от клиентов. Когда появляется клиент, создавать для него новый TCB на основе копии главного TCB и записи в него адреса socket клиента и других параметров.
6. Создавать дочерний процесс для обслуживания клиента. Дочерний процесс будет наследовать новый TCB и обрабатывать все дальнейшие операции по связи с клиентом
(ожидать сообщений от клиента, записывать их и завершать работу).
Каждый шаг в программе объясняется в следующем разделе.
/* tcpserv.c
* Для запуска программ ввести 'tcpserv'. */
/* Сначала включить набор стандартных заголовочных файлов. */
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>
main() {
int sockMain, sockClient, length, child;
struct sockaddr_in servAddr;
/* 1. Создать главный блок управления пересылкой. */
if ((sockMain = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror('Сервер не может открыть главный socket.');
exit(1);
}
/* 2. Создать структуру данных для хранения локальных IP-адресов
* и портов, которые будут использованы. Предполагается прием
* клиентских соединений от любых локальных IP-адресов
* (INADDR_ANY). Поскольку данный сервер не применяет
* общеизвестный порт, установить port = 0. Это позволит
* связать вызов с присвоением порта серверу и записать
* порт в TCB. */
bzero((char *)&servAddr, sizeof(servAddr));
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
servAddr.sin_port = 0;
/* 3. Связать запрос, выбор номера порта и
* запись его в TCB. */
if (bind(sockMain, &servAddr, sizeof(servAddr))) {
perror('Связывание сервера неудачно.');
exit(1);
}
/* Чтобы увидеть номер порта, следует использовать
* функцию getsockname(), чтобы скопировать порт в servAddr. */
length = sizeof(servAddr);
if (getsockname(sockMain, &servAddr, &length)) {
perror('Вызов getsockname неудачен.');
exit(1);
}
printf('СЕРВЕР: номер порта - %d
', ntohs(servAddr.sin_port));
/* 4. Создать очередь для хранения пяти клиентов. */
listen(sockMain, 5);
/* 5. Ожидать клиента. При разрешении возвратить новый
* дескриптор socket, который должен использоваться клиентом. */
for(;;) {
if ((sockClient = accept(sockMain, 0, 0)) < 0) {
perror ('Неверный socket для клиента.');
exit(1);
}
/* 6. Создать дочерний процесс для обслуживания клиента. */
if ((child = fork()) < 0)
{
perror('Ошибка создания дочернего процесса.');
exit(1);
}
else if (child == 0) /* Это код для исполнения дочернего процесса. */
{
close(sockMain); /* Дочерний процесс неинтересен для sockMain.*/
childWork(sockClient);
close(sockClient);
exit(0);
}
/* 7. Это родительский процесс. Его более не интересует
* socket клиента, поскольку его обслуживание передано
* дочернему процессу. Родительский процесс закрывает свой элемент для
* socket клиента и переходит на цикл приема новых accept(). */
close(sockClient);
}
}
/* Дочерний процесс читает один поступивший буфер, распечатывает
* сообщение и завершается. */
#define BUFLEN 81
int childWork(sockClient)
int sockClient;
{
char buf[BUFLEN];
int msgLength;