В этом пункте мы напишем две программы — сервер и клиент. Программа-сервер после запуска сразу же перейдет в режим ожидания («прослушивания») новых клиентов. Максимальное количество клиентов —3. Как только подключится клиент, сервер отправит ему сообщение «What is your name?», в ответ на которое клиент передаст свое имя — «Denis». Сервер прочитает переданную клиентом информацию и выведет ее на консоль. Клиент, в свою очередь, выведет на консоль запрос сервера.
С целью упрощения исходного кода как сервера, так и клиента, обработку ошибок производить не будем, поэтому будьте готовы к тому, что ваш клиент выдаст сообщение Segmentation fault в ответ на неверно заданные параметры. Я рекомендую в качестве имени сервера использовать localhost
и обе программы запускать на одном компьютере — это же только демонстрация.
Вот исходный код программы-сервера:
Листинг 27.3. Программа-сервер
#include <sys/types.h>
#include <netdb.h>
#include <memory.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#define SERVER_PORT 1234
#define BUF_SIZE 64
#define MSG_TO_SEND 'What is your name?
'
int main() {
int sock1, sock2;
int ans_len, total=0;
char buffer[BUF_SIZE];
struct sockaddr_in sin, client;
sock1 = socket(AF_INET, SOCK_STREAM, 0);
memset((char *)&sin, ' ', sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = SERVER_PORT;
bind(sock1, (struct sockaddr *)&sin, sizeof(sin));
printf('Server running...
');
listen(sock1, 3);
while (1) {
ans_len = sizeof(client);
sock2 = accept(sock1, &client, &ans_len);
write(sock2, MSG_TO_SEND, sizeof(MSG_TO_SEND));
total+=1;
ans_len = read(sock2, buffer, BUF_SIZE);
write(1, buffer, ans_len);
printf('Client no %d
', total);
shutdown(sock2, 0);
close(sock2);
}
return 0;
}
Теперь разберемся, что есть что. Сначала мы определяем некоторые макросы: номер порта, который будет прослушивать сервер, размер буфера передаваемых данных и текст запроса клиенту.
Стандартные номера портов определены в файле netinet/in.h
:
enum {
IPPORT_ECHO = 7, /* Echo service. */
IPPORT_DISCARD = 9, /* Discard transmissions service. */
IPPORT_SYSTAT = 11, /* System status service. */
IPPORT_DAYTIME = 13, /* Time of day service. */
IPPORT_NETSTAT = 15, /* Network status service. */
IPPORT_FTP = 21, /* File Transfer Protocol. */
IPPORT_TELNET =23, /* Telnet protocol. */
IPPORT_SMTP = 25, /* Simple Mail Transfer Protocol. */
IPPORT_TIMESERVER = 37, /* Timeserver service. */
IPPORT_NAMESERVER = 42, /* Domain Name Service. */
IPPORT_WHOIS = 43, /* Internet Whois service. */
IPPORT_MTP = 57,
IPPORT_TFTP = 69, /* Trivial File Transfer Protocol. */
IPPORT_RJE = 77,
IPPORT_FINGER = 79, /* Finger service. */
IPPORT_TTYLINK = 87,
IPPORT_SUPDUP = 95, /* SUPDUP protocol. */
IPPORT_EXECSERVER = 512, /* execd service. */
IPPORT_LOGINSERVER = 513, /* rlogind service. */
IPPORT_CMDSERVER = 514,
IPPORT_EFSSERVER = 520,
/* UDP ports. */
IPPORT_BIFFUDP = 512,
IPPORT_WHOSERVER = 513,
IPPORT_ROUTESERVER = 520,
/* Ports less than this value are reserved
for privileged processes. */
IPPORT_RESERVED = 1024,
/* Ports greater this value are reserved
for (non-privileged) servers. */
IPPORT_USERRESERVED = 5000
}
;
Нам понадобятся сразу два сокета: первый — это сокет сервера, а через второй сокет мы будем производить обмен данными с клиентом.
int sock1, sock2;
Следующие переменные: ans_len
используется для хранения размера передаваемой клиентом информации — фактического размера структуры struct sockaddr_in, а total
— это счетчик числа клиентов, используемый для вывода порядкового номера клиента.
Переменная buffer
размера BUF_SIZE — это наш буфер для обмена информацией. Нам нужны две структуры типа sockaddr_in
— одна для сервера (sin) и одна для клиента (client).