-
1.Если я установлю требование генерации сообщений с помощью функции WSAAsyncSelect, то в какие моменты они будут происходить? Только при приходе от сервера новой порции данных или не только? 2. нужно организовать прием и обработку данных от сервера, который посылает их в виде пакетов такого формата: первые 2 байта - длина пакета, остальное - собственно данные. При проверке трафика между сервером и стандартным клиентом не замечалось "разрывов" отдельных пакетов (у них максимальная длина не очень большая) и склеек нескольких подряд идущих пакетов, но тем не менее, чтобы подстраховаться от возможных таких случаев, планирую делать примерно так:
type TPacket: packed record
case boolean of
true: (buf: array[0..8191] of byte;);
false: (len:word;
typepack:byte;);
end;
var
Packet:TPacket;
...
begin
while true do
begin
if recv(FSocket,Packet,2,MSG_PEEK)<2 then Exit;
if recv(FSocket,Packet,Packet.len,MSG_PEEK)<packet.len then Exit;
recv(FSocket,Packet,Packet.len,0);
Obrabotka(Packet); end;
end;
Если так делать, то получается как-то "некрасиво" что-ли, из-за многократного дергания функции recv. Это нормально или лучше переписать по другому, если да, то как?
-
1. Не только, т.к. будут приходить оконные сообщения, может произойти ситуация что сообщение посланное через HWND_BROADCAST может пойматься твоим ЦВС и если оно будет идентично тем, которые ты обрабатываешь, то... 2. Такой сервер можно легко "положить" отправив FFFF первым словом. Для разделения пакетов? в случае бинарных данных? можно использовать Base64? используя в качестве разделителя байт, не входящий в последовательность BASE64
-
Не, я пишу не сервер. Это у меня клиент. Т.е. тут я не изобретаю свой протокол обмена, а придерживаюсь того, с которым работает сервер. Просто протокол обмена большей частью описан в некоторых статьях в интернете, ну и частично я могу просниферить то, как с сервером общается стандартный клиент. Что касается буфера [0..8191], то этого более чем достаточно для приема любого из реальных пакетов посланных сервером. Защищать мой клиент от FFFF или нечто подобного можно разве что на случай какого-нить сбоя, что вроде бы и невозможно, но если такой сбой произойдет, то весь дальнейший обмен трафиком потеряет смысл, и можно будет дисконектить соединение. А защитить клиент от этого можно добавивши еще одну проверку.
-
> Если так делать, то получается как-то "некрасиво" что-ли, из-за многократного дергания функции recv.
Так делать неправильно. recv читает данные в буфер и возвращает размер прочитанных данных, либо число меньше нуля (тогда надо вызвать WSAGetLastError, чтобы обработать ошибку). Если recv вернул размер, равный размеру буфера, то надо читать дальше, пока не вернет размер меньше размера буфера или 0. Всё прочитанное последовательно складывается в буфер (а лучше сразу в него и считывать, передавая указатель на текущую позицию), затем там ищутся пакеты. По-хорошему, у пакетов должна быть сигнатура и контрольная сумма.
> if recv(FSocket,Packet,Packet.len,MSG_PEEK)<packet.len then Exit;
Прийти может 1 байт - и ты просто потеряешь его.
-
> Омлет © (25.10.11 08:03) [3]
А, пардно, кое в чем не прав. Не заметил флаг MSG_PEEK )) Но, всё равно, незачем лишний раз вызывать recv, если можно сразу считать данные.
-
я делаю по такому принципу function RecvBufFully(Peer:TSocket;var Buf; Count: Integer):boolean;
var pBuf:PByte;
s:integer;
begin
result:=false;
pBuf:=@Buf;
while count>0 do
begin
s:=recv(Peer,pBuf,Count,0);
if s=0 then exit;
inc(pBuf,s);
dec(Count,s);
end;
result:=true;
end;
if RecvBufFully(FSocket,Packet.len,SizeOf(Packet.len)) then
if not RecvBufFully(FSocket,Packet.typepack,Packet.len) then БитыйПакет;
-
> Slym © (25.10.11 09:16) [5]
У тебя нет проверки на s < 0, огребешь ведь.. И что будет, если еще не все данные пришли? recv вернет 0, а через секунду придет остаток данных - ты же пакет потеряешь. Или, к примеру, придет сначала часть пакета..
-
> Омлет © (25.10.11 08:09) [4] > > > Омлет © (25.10.11 08:03) [3] > > А, пардно, кое в чем не прав. Не заметил флаг MSG_PEEK )) > Но, всё равно, незачем лишний раз вызывать recv, если можно > сразу считать данные.
Вот и интересует как можно по-другому считать данные, если:
if recv(FSocket,Packet,2,MSG_PEEK)<2 then Exit;
if recv(FSocket,Packet,Packet.len,MSG_PEEK)<packet.len then Exit;
recv(FSocket,Packet,Packet.len,0);
-
> как можно по-другому считать данные
Не использовать MSG_PEEK.
-
> Не использовать MSG_PEEK.
Т.е. читать в буфер все что получили, а потом работать с буфером и пакеты выделять из буфера? насколько это будет "лучше"?
-
Не "все что получили", а не более того сколько ожидается до окончания приема текущего принимаемого пакета.
> насколько это будет "лучше"?
Ну ты же сам сказал что
> "некрасиво" ..из-за многократного дергания функции recv
И это действительно "некрасиво", потому что ее можно и нужно "дергать" тогда когда нужно получить очередную "порцию" данных из.входного потока.
-
> Очень Злой (25.10.11 12:43) [9] > Т.е. читать в буфер все что получили, а потом работать с буфером и пакеты выделять из буфера?
Да.
> насколько это будет "лучше"?
В чем тебе измерить? ))
-
Омлет © (25.10.11 9:38) [6] У тебя нет проверки на s < 0, огребешь ведь..уговорил uses RTLConsts;
function RecvBufFully(Peer:TSocket;var Buf; Count: Integer):boolean;
var pBuf:PByte;
s,err:integer;
begin
result:=false;
pBuf:=@Buf;
while count>0 do
begin
s:=recv(Peer,pBuf,Count,0);
if s=SOCKET_ERROR then
begin
err:=WSAGetLastError;
raise Exception.CreateResFmt(@sWindowsSocketError, [SysErrorMessage(err), err, 'recv']);
end else
if s=0 then exit; inc(pBuf,s);
dec(Count,s);
end;
result:=true;
end;
if RecvBufFully(FSocket,Packet.len,SizeOf(Packet.len)) then
if not RecvBufFully(FSocket,Packet.typepack,Packet.len) then БитыйПакет;
-
> Slym © (26.10.11 06:44) [12]
Так тоже нехорошо делать, т.к. ошибки-то разные бывают. Например, WSAEWOULDBLOCK.
-
> Например, WSAEWOULDBLOCK
WSAEWOULDBLOCK никогда не возникнет при вызове recv()
-
> WSAEWOULDBLOCK никогда не возникнет при вызове recv()
- не надо обманывать - в неблокирующем режиме WSAEWOULDBLOCK - это обычное состояние, если перед recv не дожидаться данных с помощью select(0, readfds,...) If no incoming data is available at the socket, the recv call blocks and waits for data to arrive according to the blocking rules defined for WSARecv with the MSG_PARTIAL flag not set unless the socket is nonblocking. In this case, a value of SOCKET_ERROR is returned with the error code set to WSAEWOULDBLOCK. The select, WSAAsyncSelect, or WSAEventSelect functions can be used to determine when more data arrives.З.Ы. Если что recv() = 0 - означает "элегантное закрытие" соединения...
-
а вот MSG_PEEK - это плохо: MSG_PEEK: Peeks at the incoming data. The data is copied into the buffer, but is not removed from the input queue. The function subsequently returns the amount of data that can be read in a single call to the recv (or recvfrom) function, which may not be the same as the total amount of data queued on the socket. The amount of data that can actually be read in a single call to the recv (or recvfrom) function is limited to the data size written in the send or sendto function call.
Если пакет разобьется на несколько датаграмм(а это нормальная ситуация на потоковых сокетах) - то "аля-улю - гони госей"...
-
-
han_malign (26.10.11 9:46) [15] вот нафега новички лезут в неблокирующий режим? в блоке все по порядку никаких сообщений все линейно
-
> han_malign (26.10.11 09:46) [15]
Согласен. Ляпнул не освежив в памяти сабж и поленившись приоткрыть справку)
|