5.5.5. Примеры программ, работающих с локальными сокетами

5.5.5. Примеры программ, работающих с локальными сокетами

Работу с локальными сокетами мы проиллюстрируем двумя программами. Первая (листинг 5.10) — это сервер. Он создает локальный сокет и переходит в режим ожидания запросов на подключение. Приняв запрос, сервер читает сообщения из сокета и отображает на на экране, пока соединение не будет закрыто. Если поступает сообщение "quit", сервер удаляет сокет и завершает свою работу. Программа socket-server ожидает путевое имя сокета в командной строке.

Листинг 5.10. (socket-server.c) Сервер локального сокета

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <sys/socket.h>

#include <sys/un.h>

#include <unistd.h>

/* Чтение сообщений из сокета и вывод их на экран. Функция

   продолжает работу до тех пор, пока сокет не будет закрыт.

   Функция возвращает 0, если клиент послал сообщение "quit",

   в противном случае возвращается ненулевое значение. */

int server(int client_socket) {

 while (1) {

  int length;

  char* text;

  /* Сначала читаем строку, в которой записана длина сообщения.

     Если возвращается 0, клиент закрыл соединение. */

  if (read(client_socket, &length, sizeof(length)) == 0)

   return 0;

  /* Выделение буфера для хранения текста. */

  text = (char*)malloc(length);

  /* Чтение самого сообщения и вывод его на экран. */

  read(client_socket, text, length);

  printf("%s ", text);

  /* Очистка буфера. */

  free(text);

  /* Если клиент послал сообщение "quit.", работа сервера

     завершается. */

  if (!strcmp(text, "quit"))

   return 1;

 }

}

int main(int argc, char* const argv[]) {

 const char* const socket_name = argv[1];

 int socket_fd;

 struct sockaddr_un name;

 int client_sent_quit_message;

 /* Создание локального сокета. */

 socket_fd = socket(PF_LOCAL, SOCK_STREAM, 0);

 /* Переход в режим сервера. */

 name.sun_family = AF_LOCAL;

 strcpy(name.sun_path, socket_name);

 bind(socket_fd, SUN_LEN(&name));

 /* Ожидание запросов. */

 listen(socket_fd, 5);

 /* Непрерывный прием запросов на подключение. Для каждого

    клиента вызывается функция server(). Цикл продолжается,

    пока не будет получено сообщение "quit". */

 do {

  struct sockaddr_un client_name;

  socklen_t client_name_len;

  int client_socket_fd;

  /* Прием запроса. */

  client_socket_fd =

   accept(socket_fd, &client_name, &client_name_len);

  /* Обработка запроса. */

  client_sent_quit_message = server(client_socket_fd);

  /* Закрытие серверной стороны соединения. */

  close(client_socket_fd);

 } while(!client_sent_quit_message);

 /* Удаление файла локального сокета. */

 close(socket_fd);

 unlink(socket_name);

 return 0;

}

Клиентская программа, показанная в листинге 5.11, подключается к локальному сокету и посылает сообщение. Путевое имя сокета и текст сообщения задаются в командной строке.

Листинг 5.11. (socket-client.c) Клиент локального сокета

#include <stdio.h>

#include <string.h>

#include <sys/socket.h>

#include <sys/un.h>

#include <unistd.h>

/* Запись строки TEXT в сокет, заданный

   дескриптором SOCKET_FD. */

void write_text(int socket_fd, const char* text) {

 /* Сначала указывается число байтов в строке, включая

    завершающий символ NULL. */

 int length = strlen(text) + 1;

 write(socket_fd, &length, sizeof(length));

 /* Запись строки. */

 write(socket_fd, text, length);

}

int main(int argc, char* const argv[]) {

 const char* const socket_name = argv[1];

 const char* const message = argv[2];

 int socket_fd;

 struct sockaddr_un name;

 /* Создание сокета. */

 socket_fd = socket(PF_LOCAL, SOCK_STREAM. 0);

 /* Сохранение имени сервера в адресной структуре. */

 name.sun_family = AF_LOCAL;

 strcpy(name.sun_path, socket_name);

 /* Подключение к серверному сокету. */

 connect(socket_fd, &name, SUN_LEN(&name));

 /* передача сообщения, заданного в командной строке. */

 write_text(socket_fd, message);

 close(socket_fd);

 return 0;

}

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

Чтобы проверить этот пример, запустите в одном терминальном окне серверную программу, указав путь к сокету, например:

% ./socket-server /tmp/socket

В другом окне запустите несколько раз клиентскую программу, задав тот же путь к сокету плюс требуемое сообщение:

% ./socket-client /tmp/socket "Hello, world."

% ./socket-client /tmp/socket "This is a test."

Сервер получит и отобразит эти сообщения. Чтобы закрыть сервер, пошлите ему сообщение "quit":

% ./socket-client /tmp/socket "quit"