Рейтинговые книги
Читем онлайн Linux: Полное руководство - Денис Колисниченко

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 146 147 148 149 150 151 152 153 154 ... 158

 IPPORT_USERRESERVED = 5000

};

Нам понадобятся сразу два сокета: первый — это сокет сервера, а через второй сокет мы будем производить обмен данными с клиентом.

int sock1, sock2;

Следующие переменные: ans_len используется для хранения размера передаваемой клиентом информации — фактического размера структуры struct sockaddr_in, а total — это счетчик числа клиентов, используемый для вывода порядкового номера клиента.

Переменная buffer размера BUF_SIZE — это наш буфер для обмена информацией. Нам нужны две структуры типа sockaddr_in — одна для сервера (sin) и одна для клиента (client).

В строке

sock1 = socket(AF_INET, SOСK_STREAM, 0);

мы создаем наш, «серверный», сокет: набор протоколов — TCP/IP, режим — с установлением соединения.

Затем мы инициализируем структуру sin:

memset((char *)&sin, '', sizeof(sin));

sin.sin_family = AF_INET; // TCP/IP

sin.sin_addr.s_addr = INADDR_ANY; // можем работать на

                                  // любом адресе

sin.sin_port = SERVER_PORT; // указываем порт (1234)

После создания сокета и инициализации структуры sin, нужно связать наш сокет с адресом и портом сервера:

bind(sock1, (struct sockaddr *)&sin, sizeof(sin));

Оператор listen(sock1, 3) означает, что мы будем прослушивать сокет sock1 (порт 1234) и максимальное число клиентов не должно превышать 3.

Как и любой нормальный сервер, мы должны работать в бесконечном цикле, постоянно обрабатывая запросы клиентов. В бесконечном цикле мы:

1. получаем размер структуры client

ans_len = sizeof(client);

2. создаем сокет sock2, через который будем обмениваться данными с клиентом. Если в очереди listen нет клиентов, мы переходим в состояние ожидания

sock2 = accept(sock1, &client, &ans_len);

3. как только подключится клиент, мы отправим ему сообщение MSG_TO_SEND

write(sock2, MSG_TO_SEND, sizeof(MSG_TO_SEND));

4. увеличиваем счетчик клиентов

total+=1;

5. получаем размер прочитанных данных, сами данные записываются в буфер buffer

ans_len = read(sock2, buffer, BUF_SIZE);

6. выводим прочитанные данные на стандартный вывод

write(1, buffer, ans_len);

7. завершаем сеанс связи

shutdown(sock2, 0);

8. закрываем сокет

close(sock2);

Конечно, любой нормальный сервер при поступлении определенных сигналов, например, SIG_HUP, должен корректно перезапуститься или вообще завершить работу. Наш сервер этого не делает — обработку сигналов, я надеюсь, вы можете добавить сами.

Теперь мы можем откомпилировать нашу программу:

$ gcc -о server server.c

Запускаем:

./server

Программа перешла в состояние ожидания новых клиентов.

27.3.8. Программа-клиент

Программа-клиент несколько проще, чем сервер. Вот ее листинг:

Листинг 27.4. Программа-клиент

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <netdb.h>

#include <memory.h>

#include <stdio.h>

#define SERVER_HOST "localhost"

#define SERVER_PORT 1234

#define CLIENT_PORT 1235

#define MSG "Denisn"

main() {

 int sock;

 int ans_len;

 int BUF_SIZE = 64;

 char buffer[BUF_SIZE];

 struct hostent *h;

 struct sockaddr_in client, server;

 sock = socket(AF_INET, SOCK_STREAM, 0);

 memset((char *)&client, '', sizeof(client));

 client.sin_family = AF_INET;

 client.sin_addr.s_addr = INADDR_ANY;

 client.sin_port = CLIENT_PORT;

 bind(sock, (struct sockaddr *)&client, sizeof(client));

 memset((char *)&client, '', sizeof(server));

 h = gethostbyname(SERVER_HOST);

 server.sin_family = AF_INET;

 memcpy((char *)&server.sin_addr.h->h_addr, h->h_length);

 server.sin_port = SERVER_PORT;

 connect(sock, &server, sizeof(server));

 ans_len = recv(sock, buffer, BUF_SIZE, 0);

 write(1, buffer, ans_len);

 send(sock, MSG, sizeof(MSG), 0);

 close(sock);

 exit(0);

}

Константа MSG — это сообщение, которое будет передано серверу. Как и в случае с сервером, нам понадобятся две структуры типа sockaddr_in:

struct hostent *h;

struct sockaddr_in client, server;

Структура типа hostent нам нужна для получения адреса сервера.

Создаем сокет, заполняем информацию о клиенте и связываем сокет:

sock = socket(AF_INET, SOCK_STREAM, 0);

memset((char *)&client, '' , sizeof(client));

client.sin_family = AF_INET;

client.sin_addr.s_addr = INADDR_ANY;

client.sin_port = CLIENT_PORT;

bind(sock, (struct sockaddr *)&client, sizeof(client));

Перед подключением к серверу нужно определить его IP-адрес:

h = gethostbyname(SERVER_HOST);

Подключаемся к серверу:

server.sin_family = AF_INET; // набор протоколов

memcpy((char *)&server.sin_addr, h->h_addr, h->h_length);

// задаем адрес сервера

server.sin_port = SERVER_PORT; // указываем порт сервера

connect(sock, &server, sizeof(server));

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

ans_len = recv(sock, buffer, BUF_SIZE, 0);

write(1, buffer, ans_len);

send(sock, MSG, sizeof(MSG), 0);

close(sock);

27.3.9. Установка опций сокета

Поскольку мы используем набор протоколов AF_INET, то в этом пункте будем рассматривать только те опции сокетов, которые относятся к этому набору. Для работы с опциями сокета используются две функции:

♦ getsockopt() — получение опций сокета;

♦ setsockopt() — установка опций сокета.

Прототипы этих функций выглядят так:

#include <sys/socket.h>

int getsockopt(int sd, int level, int option_name,

 void *restrict option_value, socklen_t *restrict option_len);

int setsockopt(int sd, int level, int option_name,

 const void *option_value, socklen_t option_len);

Первый параметр, sd, — это дескриптор сокета. Второй параметр — уровень доступа (существует только один уровень — SOL_SOCKET). Следующий параметр, option_name, — это название опции, значение которой вы хотите изменить (см. таблицу 27.10). Последние два параметра — это значение опции и его размер.

Наиболее часто используемые опции сокетов Таблица 27.10

Название опции Описание SO_DEBUG Включить/выключить (1/0) запись отладочной информации для сокета SO_BROADCAST Включить/выключить (1/0) отправку широковещательных сообщений SO_REUSEADDR Опция разрешает/запрещает использование локальных адресов SO_KEEPALIVE Сохраняет неактивные соединения "в живых" путем посылки сообщений. Если данный сокет не отвечает на сообщения, соединение будет разорвано, а процессу, который осуществлял запись в сокет, будет послан сигнал SIGPIPE. Для включения KEEPALIVE нужно установить значение 1, для выключения — 0 SO_SNDBUF Устанавливает размер буфера отправки, значение целого типа SO_RCVBUF Устанавливает размер буфера приема, значение целого типа SO_SNDTIMEO Установка таймаута для отправки сообщений. По умолчанию таймаут равен 0, то есть его вообще нет. Нужно передать значение типа struct timeval SO_RCVTIMEO Установка таймаута для приема сообщений. По умолчанию таймаут равен 0, то есть его вообще нет. Нужно передать значение типа struct timeval TCP_NODELAY Отключить (1) механизм буферизации сообщений, то есть они будут отправляться сразу, без задержки. Для включения механизма буферизации нужно указать значение 0 TCP_MAXSEG Установить максимальный сегмент данных. Значение целого типа TCP_NOPUSH Не использовать проталкивание (1) TCP_NOOPT Не использовать опции TCP (1). Для использования опций передайте значение 0

В случае успешной установки параметра функция setsockopt() возвращает 0; в случае ошибки возвращается -1, а переменная errno устанавливается следующим образом:

♦ EBADF — неверный дескриптор сокета:

♦ ENOTSOCK — указанный дескриптор является файлом, а не сокетом;

♦ EFAULT — нет доступа к адресу, на который указывает указатель optval, то есть данный адрес находится за пределами видимости приложения.

Функция getsockopt() возвращает значение параметра. Кроме вышеперечисленных параметров, функция getsockopt() может использовать следующие параметры:

1 ... 146 147 148 149 150 151 152 153 154 ... 158
На этой странице вы можете бесплатно читать книгу Linux: Полное руководство - Денис Колисниченко бесплатно.
Похожие на Linux: Полное руководство - Денис Колисниченко книги

Оставить комментарий