21.6 Серверная программа TCP

We use cookies. Read the Privacy and Cookie Policy

21.6 Серверная программа TCP

Рассмотрим подробно пример серверной программы. Сервер предназначен для непрерывной работы. Он будет выполнять следующие действия:

1. Запрашивать у socket создание главного TCB и возвращать значение дескриптора socket, который будет идентифицировать этот TCB в последующих вызовах.

2. Вводить локальный адрес сервера socket в структуру данных программы.

3. Запрашивать связывание, при котором в TCB копируется локальный адрес socket.

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;

 /* 8. Опустошить буфер. Затем вывести recv для получения сообщения от клиента. */

 bzero(buf, BUFLEN);

 if ((msgLength = recv(sockClient, buf, BUFLEN, 0)) < 0) {

  perror("Плохое получение дочерним процессом.");

  exit(1);

 }

 printf ("SERVER: Socket для клиента - %d ", sockClient);

 printf ("SERVER: Длина сообщения - %d ", msgLength);

 printf ("SERVER: Сообщение: %s ", buf);

}