-
> han_malign (26.10.11 09:55) [16] > > а вот 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. > > Если пакет разобьется на несколько датаграмм(а это нормальная > ситуация на потоковых сокетах) - то "аля-улю - гони госей". > ..
Или я неправильно перевел или хз, ибо получается что функция возвращает кол-во прочитанных данных и оно не может быть такое как общее кол-во данных в сокете? Какой тогда вообще смысл в MSG_PEEK ?
Я его пытался использовать чтобы не забирать из сокета данные, если их кол-во менее одного целого пакета, а когда имеется пакет или больше - забирать только целыми пакетами. Конечно, можно переделать и забирать все что есть в буфер справа, а слева постоянно забирать готовые пакеты, и постоянно сдвигать буфер влево... Но посчитал что лишние операции и дополнительные буферы это не есть хорошо...
> Омлет © (25.10.11 15:37) [11] > > > Очень Злой (25.10.11 12:43) [9] > > Т.е. читать в буфер все что получили, а потом работать > с буфером и пакеты выделять из буфера? > > Да. > > > насколько это будет "лучше"? > > В чем тебе измерить? ))
Ну я пока как новичек хочу научиться изначально делать "правильно", и совета более опытных программистов вроде как достаточно.
-
> Но посчитал что лишние операции и дополнительные буферы это не есть хорошо...
Вызывать recv и ждать, пока там накапает на целый пакет - это и есть лишние операции. А считывать сразу всё, что пришло, в свой буфер - нормально. Главное, чтобы буфер был больше, чем максимальные размер пакета.
-
> и оно не может быть такое как общее кол-во данных в сокете?
returns an amount of data which can be read in a single recv; this may or may not be the same as the total amount of data queued on the socket.
Это из справке к ioctlsocket/
-
> Омлет © (27.10.11 11:01) [21] > > > Но посчитал что лишние операции и дополнительные буферы > это не есть хорошо... > > Вызывать recv и ждать, пока там накапает на целый пакет > - это и есть лишние операции. А считывать сразу всё, что > пришло, в свой буфер - нормально. Главное, чтобы буфер был > больше, чем максимальные размер пакета.
А разве я сказал, что я жду пока накапает пакет? у меня: ...
if WSAAsyncSelect(FSocket,FWindowHandle,WM_SOCKET_AUTH_EVENT, FD_CONNECT or FD_READ or FD_WRITE or FD_CLOSE)= SOCKET_ERROR then
... и если пришло сообщение с FD_READ, то я читаю из сокета данные целыми пакетами, и обрабатываю, и если при очередном чтении там нет целого пакета, то эти данные оставляю в сокете, чтобы прочитать в следующий раз... Или Вы хотите сказать, что если в сокете останутся данные, то даже при отсутствии новых данных все равно будет генерироваться сообщение для чтения?
-
> Очень Злой (27.10.11 11:26) [23] > Или Вы хотите сказать, что если в сокете останутся данные, то даже при отсутствии новых данных все равно будет генерироваться сообщение для чтения?
Не будет.
> данные оставляю в сокете, чтобы прочитать в следующий раз
Какой в этом смысл, если тем же вызовом recv можно сразу забрать данные? Их ведь всё равно придется забирать, только уже не небольшими порциями.
-
> Какой в этом смысл, если тем же вызовом recv можно сразу > забрать данные? Их ведь всё равно придется забирать, только > уже не небольшими порциями.
Ну просто я считал, что таким образом можно обойтись без дополнительного буфера и уменьшить кол-во операций по перемещению данных в памяти: т.е. кроме чтения данных из сокета в буфер придется еще переносить данные из буфера в другой буфер (предназначенный для хранения одного пакета), и кроме того перемещать данные в буфере влево по мере того, как они будут забираться оттуда...
-
> Очень Злой (27.10.11 11:42) [25]
Зачем нужен буфер для одного пакета? Пришли данные - положили в буфер. Если накапало на пакет - весь пакет уже в буфере - его можно обрабатывать из этого же буфера, никуда не перемещая больше.
-
Если обработка в отдельном потоке - блокируй запись в буфер, пока пакет из него не обработается.
-
Очень Злой (27.10.11 11:42) [25] либо что-то плохое читал либо плохо читал 1. recv(FSocket,Packet,Packet.len,MSG_PEEK) - копирует данные из буфера сокета в пользовательский буфер, т.е. ты не избежал операции чтения 2. игнорировать сообщение плохо. сказано читай - а ты не буду 3. есть другой способ узнать размер данных ioctlsocket(FSocket, FIONREAD, Longint(Len)) 4. делай в блоке и не парься
-
> Если накапало на пакет - весь пакет уже в буфере - его можно > обрабатывать из этого же буфера, никуда не перемещая больше. >
пакеты имеют разный размер, от нескольких байт до нескольких сотен байт и теоретически возможно, что при очередном чтении мы получим несколько слепленных пакетов, а также может я параноик, но не исключаю возможности, когда пакеты могут быть разорваны, например один раз пришло 2,5 пакета, 2 мы должны обработать, остальное оставить, для обработки после того как третий пакет будет полностью получен... > 4. делай в блоке и не парься
Вобще-то в итоге планируется из этого сделать компонент, который будет вызывать пользовательские обработчики событий. В блоке я даже и не представляю пока как это сделать... > 2. игнорировать сообщение плохо. сказано читай - а ты не > буду
В смысле? Что я пропустил? Пока переписал так (еще не тестировал):
...
private
FBuffer: packed array[0..8191] of byte;
FStartBuffer:integer;
FEndBuffer:integer;
...
procedure TMyClass.ReceivePackets;
var
s:Integer;
begin
s:=recv(FSocket,FBuffer[FEndBuffer],length(FBuffer)-FEndBuffer,0);
inc(FEndBuffer,s);
while true do
begin
if FEndBuffer-FStartBuffer<2 then Break;
move(FBuffer[FStartBuffer],Fpacket,2);
if FPacket.len>sizeof(FPacket) then
begin
Exit;
end;
if FEndBuffer-FStartBuffer<Fpacket.len then Break;
move(FBuffer[FStartBuffer],FPacket,Fpacket.len);
inc(FstartBuffer,Fpacket.len); if FGame then ProcessingGame else ProcessingAuth; end;
if FStartBuffer>=FEndBuffer then
begin
FStartBuffer:=0;
FEndBuffer:=0;
end;
if length(FBuffer)-FEndBuffer<2048 then
begin
move(FBuffer[FStartBuffer],FBuffer[0], FEndBuffer-FStartBuffer);
FEndBuffer:=FEndBuffer-FStartBuffer;
FStartBuffer:=0;
end;
end;
-
Вобщем суть в том, что мне нужно последовательно принимать и обрабатывать некие пакеты данных. пакет представляет собой последовательность данных, где первые 2 байта содержат размер пакета (включительно с этими двумя байтами). Сервер не мой. И я не уверен что 1 дейтаграмма = 1 пакет. Поэтому исхожу из предположения что в 1 дейтаграмме может быть как 1 пакет так и несколько, а также что 1 пакет может содердаться в двух дейтаграммах,начало в одном, конец в другом.
-
> переносить данные из буфера в другой буфер (предназначенный для хранения одного пакета)
- а зачем спрашивается хранить один пакет? res:= recv(FSocket,F_rgbBuf[F_cbDepth],sizeof(F_rgbBuf) - F_cbDepth,0);
if( res > 0 )then begin
inc(F_cbDepth, res);
offs:= 0;
while( (F_cbDepth >= sizeof(PPacket(@F_rgbBuf[offs]).len)) and
(F_cbDepth >= PPacket(@F_rgbBuf[offs]).len)
) do begin
_process(PPacket(@F_rgbBuf[offs])^);
inc(offs, PPacket(@F_rgbBuf[offs]).len);
dec(F_cbDepth, PPacket(@F_rgbBuf[offs]).len);
end;
if( (offs > 0) and (F_cbDepth > 0) )then
move(F_rgbBuf[offs], F_cbDepth[0], F_cbDepth);
end else
... - перемещаемые огрызки обычно достаточно малы, либо редки - и суммарный объем перемещаемой памяти заведомо меньше чем если читать два раза(либо бесконечное число раз, поскольку успешный MSG_PEEK - может(в зависимости от реализации winsock) каждый раз возвращать один и тот же размер(датаграммы) независимо от количества принятых данных)... При этом - при интенсивном обмене данными - за один recv может быть обработано несколько пакетов...
-
> - а зачем спрашивается хранить один пакет?
Чтобы передать его в другую процедуру для обработки.
> _process(PPacket(@F_rgbBuf[offs])^);
Насчет этого согласен. Как-то не подумал. Спасибо...
> - перемещаемые огрызки обычно достаточно малы, либо редки > - и суммарный объем перемещаемой памяти заведомо меньше > чем если читать два раза.
Похоже что да. Вы меня убедили.. :)
-
function ReceiveDataBlock(hSocket: THandle; DataBlockSize: Integer; out DataBlock; out ErrorCode: Integer): Boolean;
var
i: Integer;
BytesToRead, BytesRead: Integer;
begin
Result := False;
i := 0;
while i < DataBlockSize do begin
BytesToRead := DataBlockSize - i;
BytesRead := Recv(hSocket, PByteArray(@DataBlock)[i], BytesToRead, 0);
Inc(i, BytesRead);
if BytesRead > 0 then Continue;
case BytesRead of
0: ErrorCode := 0;
SOCKET_ERROR: ErrorCode := WSAGetLastError;
end;
Exit;
end;
Result := True;
end;
-
> Сервер не мой. И я не уверен что 1 дейтаграмма = 1 пакет.
- размер датаграммы потокового сокета определяется сетевым окружением(включая шлюзы, маршрутизаторы и L3-комутаторы) и системными настройками(например - алгоритм Нага, QoS) , а не реализацией сервера...
Размер датаграммы(для потокового сокета) обычно определяется максимальным размером Ethernet II пакета(1514-14{MAC}-20{IP}-20{TCP} = 1460) - во избежание лишних накладных расходов на обработку IP-фрагментов...
-
> Очень Злой (27.10.11 12:39) [30]
Формат пакетов у вас не безопасный - при потере tcp-пакетов случится кака. Про udp вообще молчу )
-
> Омлет © (27.10.11 15:15) [35]
> при потере tcp-пакетов
Стесняюсь спросить, и какая же "кака" в подлунном мире должна случиться, чтобы из трех подряд успешно отправленных по TCP прикладных пакетов A, B и С до получателя дошли только, скажем, A и C ?)
-
> Формат пакетов у вас не безопасный - при потере tcp-пакетов > случится кака. Про udp вообще молчу )
Формат пакетов определяю не я, а сервер... А при потере хотя бы одного пакета , дальнейшая работа программы становится невозможной, даже если я смогу найти начало очередного пакета. Все пакеты шифруются, причем с динамическим ключем, который меняется с каждым принятым пакетом, на величину, зависящую от самого пакета. Т.е. пропустивши очередной пакет, я даже если и найду начало следующего - то не смогу его расшифровать.
Хотя о какой потере может идти речь в TCP-протоколе ?
-
При обрыве связи, к примеру. Но это, конечно, смотря как логика восстановления соединения прописана.
-
> Стесняюсь спросить, и какая же "кака" в подлунном мире должна > случиться, чтобы из трех подряд успешно отправленных по > TCP прикладных пакетов A, B и С до получателя дошли только, > скажем, A и C ?)
Вобще-то такая кака не смотря ни на что все-таки возникала, при работе со стандартным клиентом. Но такое я замечал только 2 раза за последние 1,5 года, да и то в условиях ужасно паршивой связи (провайдера колбасило). была такая ситуация когда сервер не реагировал ни на какие команды клиента, но информация с сервера продолжала приходить и успешно обрабатывалась клиентом. Я только недавно понял из-за чего такой прикол вышел. Оказалось что принимаемый трафик и отправляемый шифруются разными ключами, и друг от друга не зависят. Т.е. непонятно как возникшее искажение данных передаваемых клиентом сделало полностью неработоспособным дальнейшую обработку этих данных на сервере, в то время как данные в обратном направлении продолжали нормально приходить и обрабатываться. Но такие ситуации чрезвычайно редки...
|