Книга: TCP/IP Архитектура, протоколы, реализация (включая IP версии 6 и IP Security)

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("СЕРВЕР: номер порта - %dn", 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 для клиента - %dn", sockClient);
 printf ("SERVER: Длина сообщения - %dn", msgLength);
 printf ("SERVER: Сообщение: %snn", buf);
}

Оглавление книги

Оглавление статьи/книги

Генерация: 1.361. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз