О чём не пишут в книгах по Delphi
Шрифт:
Примечание
Эта ситуация отличается от использования
select
для UDP-сокетов. С ними такой проблемы не возникает, т.к. дейтаграмма никогда не приходит по частям, и если функция select
показала готовность сокета. значит, уже получено все сообщение целиком. Завершается основной цикл сервера удалением всех ресурсов, связанных с закрытыми соединениями. После небольшой паузы, сделанной для того, чтобы сервер не нагружал процессор непрерывно, управление передается
Листинг 2.26. Основная часть сервера SelectServer
// Тайм-аут для функции select, хотя и передается через указатель,
// является для нее входным параметром, который не изменяется.
// Так как у нас везде будет использоваться один и тот же нулевой
// тайм-аут, можем один раз задать значение переменной Timeout
// и в дальнейшем всегда им пользоваться.
Timeout.tv_sec := 0;
Timeout.tv_usec := 0;
// Начало цикла подключения и общения с клиентами
repeat
// Сначала проверяем, готов ли слушающий сокет.
// Если он готов, это означает, что есть подключившийся,
// но не обработанный функцией accept клиент
FD_ZERO(SockSet);
FD_SET(MainSocket, SockSet);
if select(0, @SockSet, nil, nil, @Timeout) = SOCKET_ERROR then
raise ESocketException.Create('Ошибка при проверке готовности слушающего сокета: ' +
GetErrorString);
// Если функция select оставила MainSocket в множестве, значит,
// зафиксировано подключение клиента, и функция accept не приведет
// к блокированию нити.
if FD_ISSET(MainSocket, SockSet) then
begin
ClientSockAddrLen := SizeOf(ClientSockAddr);
// Принимаем подключившегося клиента. Для общения с ним создается
// новый сокет, дескриптор которого помещается в ClientSocket.
ClientSocket :=
accept(MainSocket, @ClientSockAddr, @ClientSockAddrLen);
if ClientSocket = INVALID_SOCKET then raise
ESocketException.Create(
'Ошибка при ожидании подключения клиента: ' + GetErrorString);
// Создаем в динамической памяти новый экземпляр TConnection
// и заполняем его данными, соответствующими подключившемуся клиенту
New(NewConnection);
NewConnection.ClientSocket := ClientSocket;
NewConnection.ClientAddr :=
Format('%u.%u.%u.%u:%u',
Ord(ClientSockAddr.sin_addr.S_un_b.s_bl),
Ord(ClientSockAddr.sin_addr.S_un_b.s_b2),
Ord(ClientSockAddr.sin_addr.S_un_b.s_b3),
Ord(ClientSockAddr.sin_addr.S_un_b.s_b4),
ntohs(ClientSockAddr.sin_port));
NewConnection.Deleted := False;
//
Добавляем соединение в список
Connections.Add(NewConnection);
WriteLn(OemString('Зафиксировано подключение с адреса ' +
NewConnection.ClientAddr));
end;
// Теперь проверяем готовность всех сокетов подключившихся клиентов.
// Так как множество SockSet не может содержать более чем FT_SETSIZE
// элементов, а размер списка Connections мы нигде не ограничиваем,
// приходится разбивать Connections на "куски" размером не более
// FD_SETSIZE и обрабатывать этот список по частям.
// Поэтому у нас появляется два цикла - внешний, который повторяется
// столько раз, сколько у нас будет кусков, и внутренний, который
// повторяется столько раз, сколько элементов в одном куске.
for J := 0 to Ceil(Connections.Count, FD_SETSIZE) - 1 do
begin
FD_ZERO(SockSet);
for I := FD_SETSIZE * J to Min(FD_SETSIZE * (J + 1) - 1, Connections.Count - 1) do
FD_SET(PConnection(Connections[I])^.ClientSocket, SockSet);
if select(0, @SockSet, nil, nil, @Timeout) = SOCKET_ERROR then
raise ESocketException.Create(
'Ошибка при проверке готовности сокетов: ' + GetErrorString);
// Проверяем, какие сокеты функция select оставила в множестве,
// и вызываем для них ProcessSocketMessage. В этом есть некоторый
// риск, т.к. для того, чтобы select оставила сокет в множестве,
// достаточно, чтобы он получил хотя бы один байт от клиента,
// а не все сообщение. Поэтому может возникнуть такая ситуация,
// когда сервер получил только часть сообщения, но уже пытается
Поделиться:
Популярные книги
Титан империи 2
2. Титан Империи
Фантастика:
фэнтези
боевая фантастика
аниме
5.00
рейтинг книги
Вперед в прошлое!
1. Вперед в прошлое
Фантастика:
попаданцы
5.00
рейтинг книги
Ты нас предал
1. Измены. Кантемировы
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Огни Аль-Тура. Желанная
3. Эйнар
Любовные романы:
любовно-фантастические романы
эро литература
5.25
рейтинг книги
Личник
3. Ермак
Фантастика:
альтернативная история
6.33
рейтинг книги
Приручитель женщин-монстров. Том 14
14. Покемоны? Какие покемоны?
Фантастика:
юмористическое фэнтези
аниме
фэнтези
5.00
рейтинг книги
Идеальный мир для Лекаря 26
26. Лекарь
Фантастика:
аниме
фэнтези
5.00
рейтинг книги
Вечный. Книга I
1. Вечный
Фантастика:
боевая фантастика
попаданцы
рпг
5.00
рейтинг книги
Последний попаданец
1. Последний попаданец
Фантастика:
фэнтези
попаданцы
рпг
5.00
рейтинг книги
Найди меня Шерхан
3. Ямпольские-Демидовы
Любовные романы:
современные любовные романы
короткие любовные романы
7.70
рейтинг книги
Гром над Академией. Часть 2
3. Гром над миром
Фантастика:
боевая фантастика
5.50
рейтинг книги
Имперец. Том 5
4. Имперец
Фантастика:
попаданцы
альтернативная история
аниме
6.00
рейтинг книги
Бастард
1. Последняя жизнь
Фантастика:
фэнтези
героическая фантастика
попаданцы
5.86
рейтинг книги
Метатель
1. Метатель
Фантастика:
боевая фантастика
попаданцы
рпг
фэнтези
фантастика: прочее
постапокалипсис
5.00