Системное программное обеспечение - Учебное пособие (Терехин А.Н.)

4.14резюме: общая схема работы с сокетами.

Мы рассмотрели все основные функции работы с сокетами. Обобщая изложенное, можно изобразить общую схему работы с сокетами с установлением соединения в следующем виде:

Рис. 20 Схема работы с сокетами с установлением соединения

Общая схема работы с сокетами без предварительного установления соединения проще, она такова:

Рис. 21 Схема работы с сокетами без установления соединения

 Работа с локальными сокетами.

Рассмотрим небольшой пример, иллюстрирующий работу с сокетами в рамках локального домена (AF_UNIX). Ниже приведена небольшая программа, которая в зависимости от параметра командной строки исполняет роль клиента или сервера. Клиент и сервер устанавливают соединение с использованием датаграммных сокетов. Клиент читает строку со стандартного ввода и пересылает серверу; сервер посылает ответ в зависимости от того, какова была строка. При введении строки «quit» и клиент, и сервер завершаются.

#include <sys/types.h>

#include <sys/socket.h>

#include <sys/un.h>

#include <stdio.h>

#include <string.h>

 

#define SADDRESS "mysocket"

#define CADDRESS "clientsocket"

#define BUFLEN  40

int main(int argc, char **argv)

{

struct sockaddr_un party_addr, own_addr;

int sockfd;

int is_server;

char buf[BUFLEN];

int party_len;

int quitting;

 

if (argc != 2) {

printf("Usage: \%s client|server. ", argv[0]);

return 0;

}

quitting = 1;

 

/* определяем, кто мы: клиент или сервер*/

is_server = !strcmp(argv[1], "server");

memset(&own_addr, 0, sizeof(own_addr));

own_addr.sun_family = AF_UNIX;

strcpy(own_addr.sun_path, is_server ? SADDRESS : CADDRESS);

 

/* создаем сокет */

if ((sockfd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)

{

  printf("can't create socket ");

  return 0;

                                                                                                  }

 

/* связываем сокет */

unlink(own_addr.sun_path);

if (bind(sockfd, (struct sockaddr *) &own_addr, sizeof(own_addr.sun_family)+ strlen(own_addr.sun_path)) < 0)

{

      printf("can't bind socket!");

      return 0;

}

 

if (!is_server)

{

/* это – клиент */

memset(&party_addr, 0, sizeof(party_addr));

party_addr.sun_family = AF_UNIX;

strcpy(party_addr.sun_path, SADDRESS);

printf("type the string: ");

 

while (gets(buf)) {

     /* не пора ли выходить? */

quitting = (!strcmp(buf, "quit"));

 

/* считали строку и передаем ее серверу */

if (sendto(sockfd, buf, strlen(buf) + 1, 0, (struct sockaddr *) &party_addr, sizeof(party_addr.sun_family) +

strlen(SADDRESS)) != strlen(buf) + 1)

     {

printf("client: error writing socket! ");

          return 0;

     }

 

/*получаем ответ и выводим его на печать*/

if (recvfrom(sockfd, buf, BUFLEN, 0, NULL, 0) < 0)

     {

printf("client: error reading socket! ");

return 0;

     }  

printf("client: server answered: \%s ", buf);

     if (quitting) break;

     printf("type the string: ");

} // while    

close(sockfd);

return 0;

} // if (!is_server)

 

/* это – сервер */

while (1)

{

/* получаем строку от клиента и выводим на печать */

     party_len = sizeof(party_addr);

if (recvfrom(sockfd, buf, BUFLEN, 0,(struct sockaddr *) &party_addr, &party_len) < 0)

{

printf("server: error reading socket!");

          return 0;

     }

 

printf("server: received from client: \%s ", buf);

/* не пора ли выходить? */

          quitting = (!strcmp(buf, "quit"));

          if (quitting)

strcpy(buf, "quitting now!");

          else

if (!strcmp(buf, "ping!"))

strcpy(buf, "pong!");

                   else

strcpy(buf, "wrong string!");

          /* посылаем ответ */

if (sendto(sockfd, buf, strlen(buf) + 1, 0, (struct sockaddr *) &party_addr,                 party_len) != strlen(buf)+1)

          {

printf("server: error writing socket! ");

          return 0;

          }

          if (quitting) break;

} // while

close(sockfd);

return 0;     

}

 Пример работы с сокетами в рамках сети.

В качестве примера  работы с сокетами в домене AF_INET напишем простенький web-сервер, который будет понимать только одну команду :

GET /<имя файла>

Сервер запрашивает у системы сокет, связывает его с адресом, считающимся известным, и начинает принимать клиентские запросы. Для обработки каждого запроса порождается отдельный потомок, в то время как родительский процесс продолжает прослушивать сокет. Потомок разбирает текст запроса и отсылает клиенту либо содержимое требуемого файла, либо диагностику (“плохой запрос” или “файл не найден”).

#include <sys/types.h>

#include <sys/socket.h>

#include <sys/stat.h>

#include <netinet/in.h>

#include <stdio.h>

#include <string.h>

#include <fcntl.h>

#include <unistd.h>

 

#define PORTNUM 8080

#define BACKLOG 5

#define BUFLEN 80

 

#define FNFSTR "404 Error File Not Found "

#define BRSTR "Bad Request "

 

int main(int argc, char **argv)

{

struct sockaddr_in own_addr, party_addr;

int sockfd, newsockfd, filefd;

          int party_len;

          char buf[BUFLEN];

int len;

          int i;

/* создаем сокет */

if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)

{

                   printf("can't create socket ");

                   return 0;

          }

          /* связываем сокет */

          memset(&own_addr, 0, sizeof(own_addr));

          own_addr.sin_family = AF_INET;

          own_addr.sin_addr.s_addr = INADDR_ANY;

          own_addr.sin_port = htons(PORTNUM);

          if (bind(sockfd, (struct sockaddr *) &own_addr,

          sizeof(own_addr)) < 0)

          {

                   printf("can't bind socket!");

                   return 0;

          }

 

          /* начинаем обработку запросов на соединение */

          if (listen(sockfd, BACKLOG) < 0)

          {

                   printf("can't listen socket!");

                   return 0;

          }

         

          while (1) {

memset(&party_addr, 0, sizeof(party_addr));

                   party_len = sizeof(party_addr);

          /* создаем соединение */

if ((newsockfd = accept(sockfd, (struct sockaddr *)&party_addr, &party_len)) < 0)

                   {

printf("error accepting connection!");

                   return 0;

                   }

 

                   if (!fork())

                   {

/*это – сын, он обрабатывает запрос и посылает ответ*/

close(sockfd);   /*  этот сокет сыну не нужен */

if ((len = recv(newsockfd, &buf, BUFLEN, 0)) < 0)

                             {

                             printf("error reading socket!");

                             return 0;

                             }       

                   /* разбираем текст запроса */

                   printf("received: \%s ", buf); 

                   if (strncmp(buf, "GET /", 5))

                   { /*плохой запрос!*/

if (send(newsockfd, BRSTR, strlen(BRSTR) + 1, 0) != strlen(BRSTR) + 1)

                             {

printf("error writing socket!");

                                      return 0;

                             }

 

                             shutdown(newsockfd, 1);

                             close(newsockfd);

                             return 0;

                   }

 

for (i=5; buf[i] && (buf[i] > ' '); i++);

                   buf[i] = 0;

                   /* открываем файл */

if ((filefd = open(buf+5, O_RDONLY)) < 0)

{

                             /* нет файла! */

if (send(newsockfd, FNFSTR, strlen(FNFSTR) + 1, 0) != strlen(FNFSTR) + 1)

{

printf("error writing socket!");

                                      return 0;

                             }

                             shutdown(newsockfd, 1);

                             close(newsockfd);

                             return 0;

                   }

                  

/* читаем из файла порции данных и посылаем их клиенту */

while (len = read(filefd, &buf, BUFLEN))

if (send(newsockfd, buf, len, 0) < 0) {

                             printf("error writing socket!");

                             return 0;

                   }

                   close(filefd);

                   shutdown(newsockfd, 1);

                   close(newsockfd);

                   return 0;     

          }

         

     /* процесс – отец. Он закрывает новый сокет и

     продолжает прослушивать старый */

     close(newsockfd);

}

}