Наш простейший сервер будет использовать только одну нить. Как мы помним, сервер должен вызывать две функции, которые блокируют работу нити: accept и recv. Очевидно, что задействовать их обе сразу в одной нити не получится, именно поэтому наш сервер сможет работать только с одним клиентом одновременно. И чтобы не блокировать пользовательский интерфейс, наш сервер будет консольным приложением. В командной строке ему передается номер порта, к которому привязывается слушающий сокет.
Первое, что должен сделать сервер, — это создать сокет. привязать его к требуемому адресу и перевести в режим прослушивания. Этот код мало чем отличается от приведенного ранее примера создания сокета для UDP (см.
листинг 2.8). Вся разница только в том, что вместо сокета типа
SOCK_DGRAM
создается сокет типа
SOCK_STREAM
, а в конце еще вызывается функция
listen
(листинг 2.14).
Листинг 2.14. Создание сокета в программе SimplestServer
var
// Порт, который будет "слушать" сервер
Port: Word;
// "Слушающей" сокет
MainSocket: TSocket;
// Сокет, создающийся для обслуживания клиента
ClientSocket: TSocket;
// Адрес "слушающего" сокета
MainSockAddr: TSockAddr;
// Адрес подключившегося клиента
ClientSockAddr: TSockAddr;
// Размер адреса подключившегося клиента
ClientSockAddrLen: Integer;
//Без этой переменной не удастся инициализировать библиотеку сокетов
WSAData: TWSAData;
StrLen: Integer;
Str: string;
begin
try
if ParamCount = 0 then
// Если в командной строке порт не задан, назначаем его
Port := 12345;
else
// В противном случае анализируем командную строку и назначаем порт
try
Port := StrToInt(ParamStr(1));
if Port = 0 then
raise ESocketException.Create(
'Номер порта должен находиться в диапазоне 1-65535');
except
on EConvertError do
raise ESocketException.Create(
'Параметр "' + ParamStr(1) + '" не является целым числом');
on ERangeError do
raise ESocketException.Create(
'Номер порта должен находиться в диапазоне 1-65535');
end;
// инициализация библиотеки сокетов
if WSAStartup($101, WSAData) <> 0 then
raise ESocketException.Create(
'Ошибка при инициализации библиотеки WinSock');
// Создание сокета, который затем будет "слушать" порт
Основная часть кода сервера — это два цикла, один из которых вложен в другой (листинг 2.15). Перед внешним циклом сервер создает сокет и переводит его в режим прослушивания, и внешний цикл начинается с вызова функции
accept
. Завершение
accept
указывает на подключение клиента. После этого начинается внутренний цикл, который состоит из получения сообщений от клиента, преобразования строки и отправки ответа. Внутренний цикл завершается, когда соединение разрывается либо самим клиентом, либо из-за ошибки в сети. После этого управление вновь передается на начало внешнего цикла, т.е. на
accept
, и сервер может принять подключение другого клиента (или повторное подключение того же клиента).
Листинг 2.15. Основная часть сервера SimplestServer
// Начало цикла подключения и общения с клиентом
repeat
ClientSockAddrLen := SizeOf(ClientSockAddr);
// Принимаем подключившегося клиента. Для общения с ним создается новый
// сокет, дескриптор которого помещается в ClientSocket.