Получение данных через "соединенные" сокеты может также осуществляться с помощью функции reсv
function recv(s: TSocket; var Buf; len, flags: Integer): Integer;
От своего аналога recvfrom
from
и fromlen
, через которые передается адрес отправителя дейтаграммы.Рис. 2.1.
Последовательность действий программы при обмене данными с помощью UDPСтрого говоря, функцию recv
connect
, а дейтаграммы всех других отправителей будут отбрасываться. Функция recvfrom
также пригодна для "соединенных" сокетов, но адрес отправителя, который она возвращает, в данном случае может быть только тот, который определен в функции connect
.Таким образом, функция connect
recvfrom
и sendto
— recv
и send
.Возможные последовательности действий программы для протокола UDP показаны на рис. 2.1.
2.1.10. Пример программы: простейший чат на UDP
Попробуем применить свои знания на практике и напишем простейший чат на основе протокола UDP. Пример этой программы находится на прилагаемом к книге компакт-диске и называется UDPChat, окно приложения показано на рис. 2.2.
Прежде чем писать программу, необходимо определиться с форматом передаваемых данных (т.е. договориться о протоколе уровня представлений). Так как мы пишем простейший пример, то и протокол у нас будет простейшим: дейтаграмма содержит текстовое сообщение, введенное пользователем, без завершающего нуля (он не нужен, т.к. размер строки определяется размером дейтаграммы) и без дополнительной служебной информации.
Для начала нам потребуется научиться сообщать пользователю об ошибках. Номер ошибки мало что дает даже опытному пользователю, поэтому сообщения должны быть дружественными, с внятным объяснением того, какая именно ошибка произошла. К счастью, мы избавлены от необходимости вручную писать текстовое сообщение для каждой из возможных ошибок, т.к. в системе уже есть функция FormatMessage
FormatMessage
мы создадим функцию GetErrorString
(листинг 2.6), которая возвращает сообщение, соответствующее коду ошибки, возвращаемому функцией WSAGetLastError
. Эта функция будет встречаться во всех наших примерах.Рис. 2.2.
Главное окно UDP-чатаGetErrorString
// функция GetErrorString возвращает сообщение об ошибке,
// сформированное системой из основе значения, которое
// вернула функция WSAGetLastError. Для получения сообщения
// используется системная функция FormatMessage.
function GetErrorString: string;
var
Buffer: array [0..2047] of Char;
begin
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, nil, WSAGetLastError, $400,
@Buffer, SizeOf(Buffer), nil);
Result := Buffer;
end;
Нам понадобится и принимать, и передавать данные. Как мы помним, функция recvfrom
recvfrom
в главной нити, то при отсутствии входящих дейтаграмм программа просто повиснет, т.к. не сможет обрабатывать оконные сообщения. Поэтому все действия по приему сообщений мы должны вынести в отдельную нить. Задача этой нити очень проста: она в бесконечном цикле вызывает recvfrom и все полученные дейтаграммы передает в главное окно для отображения на экране.Нить, читающая данные, создается обычным образом — порождением наследника от класса TThread
FSocket
. Код нити, читающей сообщения, показан в листинге 2.7.unit ReceiveThread;
{