Страница 45 из 51 5.1. Программа-сервер Текст программы-сервера на языке программирования СИ выглядит следующим образом 1 #include <sys/types.h> 2 #include <sys/socket.h> 3 #include <netinet/in.h> 4 #include <netdb.h> 5 #include <memory.h> 6 #define SRV_PORT 1234 7 #define BUF_SIZE 64 8 #define TXT_QUEST "Who are you?\n" 9 main () { 10 int s, s_new; 11 int from_len; 12 char buf[BUF_SIZE]; 13 struct sockaddr_in sin, from_sin; 14 s = socket (AF_INET, SOCK_STREAM, 0); 15 memset ((char *)&sin, '\0', sizeof(sin)); 16 sin.sin_family = AF_INET; 17 sin.sin_addr.s_addr = INADDR_ANY; 18 sin.sin_port = SRV_PORT; 19 bind (s, (struct sockaddr *)&sin, sizeof(sin)); 20 listen (s, 3); 21 while (1) { 22 from_len = sizeof(from_sin); 23 s_new = accept (s, &from_sin, &from_len); 24 write (s_new, TXT_QUEST, sizeof(TXT_QUEST)); 25 from_len = read (s_new, buf, BUF_SIZE); 26 write (1, buf, from_len); 27 shutdown (s_new, 0); 28 close (s_new); 29 }; 30 } - 1...5 описывают включаемые файлы, содержащие определения для всех необходимых структур данных и символических констант.
- Строка 6 приписывает целочисленной константе 1234 символическое имя SRV_PORT. В дальнейшем эта константа будет использована в качестве номера порта сервера. Значение этой константы должно быть известно и программе-клиенту.
- Строка 7 приписывает целочисленной константе 64 символическое имя BUF_SIZE. Эта константа будет определять размер буфера, используемого для размещения принимаемых от клиента данных.
- Строка 8 приписывает последовательности символов, составляющих текст вопроса клиенту, символическое имя TXT_QUEST. Последним символом в последовательности является символ перехода на новую строку '\n'. Сделано это для упрощения вывода текста вопроса на стороне клиента.
- В строке 14 создается (открывается) socket для организации режима взаимодействия с установлением логического соединения (SOCK_STREAM) в сети TCP/IP (AF_INET), при выборе протокола транспортного уровня используется протокол "по умолчанию" (0).
- В строках 15...18 сначала обнуляется структура данных sin, а затем заполняются ее отдельные поля. Использование константы INADDR_ANY упрощает текст программы, избавляя от необходимости использовать функцию gethostbyname для получения адреса локального узла, на котором запускается сервер.
- Строка 19 посредством системного вызова bind привязывает socket, задаваемый дескриптором s, к порту с номером SRV_PORT на локальном узле. Bind завершится успешно при условии, что в момент его выполнения на том же узле уже не функционирует программа, использующая этот номер порта.
- Строка 20 посредством системного вызова listen организует очередь на три входящих к серверу запроса на соединение.
- Строка 21 служит заголовком бесконечного цикла обслуживания запросов от клиентов.
- На строке 23, содержащей системный вызов accept, выполнение программы приостанавливается на неопределенное время, если очередь запросов к серверу на установление связи оказывается пуста. При появлении такого запроса accept успешно завершается, возвращая в переменной s_new дескриптор socket'а для обмена информацией с клиентом.
- В строке 24 сервер с помощью системного вызова write отправляет клиенту вопрос.
- В строке 25 с помощью системного вызова read читается ответ клиента.
- В строке 26 ответ направляется в стандартный вывод, имеющий дескриптор файла номер 1. Так как строка ответа содержит в себе символ перехода на новую строку, то текст ответа будет размещен на отдельной строке дисплея.
- Строка 27 содержит системный вывод shutdown, обеспечивающий очистку системных буферов socket'а, содержащих данные для чтения ("лишние" данные могут там оказаться в результате неверной работы клиента).
- В строке 28 закрывается (удаляется) socket, использованный для обмена данными с очередным клиентом.
Примечание. Данная программа (как и большинство реальных программ-серверов) самостоятельно своей работы не завершает, находясь в бесконечном цикле обработки запросов клиентов. Ее выполнение может быть прервано только извне путем посылки ей сигналов (прерываний) завершения. Правильно разработанная программа-сервер должна обрабатывать такие сигналы, корректно завершая работу (закрывая, в частности, посредством close socket с дескриптором s). |