-
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]
Согласен. Ляпнул не освежив в памяти сабж и поленившись приоткрыть справку)
-
> 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 года, да и то в условиях ужасно паршивой связи (провайдера колбасило). была такая ситуация когда сервер не реагировал ни на какие команды клиента, но информация с сервера продолжала приходить и успешно обрабатывалась клиентом. Я только недавно понял из-за чего такой прикол вышел. Оказалось что принимаемый трафик и отправляемый шифруются разными ключами, и друг от друга не зависят. Т.е. непонятно как возникшее искажение данных передаваемых клиентом сделало полностью неработоспособным дальнейшую обработку этих данных на сервере, в то время как данные в обратном направлении продолжали нормально приходить и обрабатываться. Но такие ситуации чрезвычайно редки...
-
> При обрыве связи, к примеру
В TCP при обрыве ничего потеряться не может - все что успешно отправлено до момента обрыва, успешно и будет доставлено в той же последовательности.
-
> такая кака не смотря ни на что все-таки возникала, при работе > со стандартным клиентом
Эту каку подложил себе сам стандартный клиент, и TCP здесь совершенно ни причем. В поточном транспортном протоколе с квитированием доставки потери дейтаграмм, их искажения или нарушения последовательности их доставки попросту исключены.
-
> Эту каку подложил себе сам стандартный клиент, и TCP здесь > совершенно ни причем. > В поточном транспортном протоколе с квитированием доставки > потери дейтаграмм, их искажения или нарушения последовательности > их доставки попросту исключены.
ну не знаю... Насчет потери - согласен, а вот насчет искажения... даже не знаю... А чем определяется искажены данные или нет? Если контрольной суммой, то она также может быть искажена вместе с данными и теоретически возможны случаи, когда искаженная контрольная сумма будет соответствовать искаженным данным...
-
> а вот насчет искажения
Искажения на уровнях ниже прикладного отсечет сам стек TCP/IP. А за контроль искажений на прикладном уровне ответственны сами обменивающиеся прикладные стороны.
-
> Очень Злой (27.10.2011 15:56:42) [42]
Зачем контрольная сумма, MD5 хеш, CRC64, коды хеминга
-
> чем определяется искажены данные или нет?
Ну как чем ? Если принятые данные не соответствуют прикладному протоколу, значит они искажены. При идеальном прикл.следовании протоколу сос тороны приемника винить в искажении можно только либо передатчик либо "человека посередине". Либо и того и другого одновременно)
-
> чем определяется искажены данные или нет?
Ну как чем ? Если принятые данные не соответствуют прикладному протоколу, значит они искажены. При идеальном прикл.следовании протоколу сос тороны приемника винить в искажении можно только либо передатчик либо "человека посередине". Либо и того и другого одновременно)
-
> контрольная сумма, MD5 хеш, CRC64, коды хеминга
- обычно достаточно контроля несущей(CSMA/CD), сбои ЦАП/АЦП относятся уже к условиям ядерной войны(либо феноменальной жадности при прокладке сегмента) - если не хватает 32-бит CRC Ethernet-пакета и 16-бит контрольной суммы TCP(не считая приличного количества контекстно-фиксированных бит в заголовках) - то спасет только тройное дублирование...
-
забудь про пакеты! для пользователя TCP-это поток данных, контроль над порядком и целостностью лежит на системе, потеряться или измениться порядок без злоумышленника не может
-
Очень Злой (27.10.11 12:29) [29] В блоке я даже и не представляю пока как это сделать... как раз твоя писанина и напоминает блочную работу, но сокет установлен в неблок - и это не правильно в неблоке никаких ReceivePackets не должно быть, а должно быть OnRead и OnWrite
-
примерно так type
TMyProtoReader=class
private
Socket:TClientSocket;
Packet:Pointer;
public
constructor Create;
destructor Destroy;override;
function Connect:boolean;
function GetPacket:Pointer;
end;
constructor TMyProtoReader.Create;
begin
Socket:=TClientSocket.Create(nil);
Socket.ClientType:=ctBlocking;
end;
destructor TMyProtoReader.Destroy;
begin
Socket.Free;
FreeMem(Packet);
inherited;
end;
function TMyProtoReader.Connect: boolean;
begin
Socket.Address:='127.0.0.1';
Socket.Port:=12345;
Socket.Active:=true;
result:=Socket.Active;
end;
function TMyProtoReader.GetPacket: Pointer;
type
PPacket=^TPacket;
TPacket=packed record
Len:word;
data:array[0..0] of byte;
end;
function RecvBufFully(Peer:TCustomWinSocket;var Buf; Count: Integer):boolean;
var pBuf:PByte;
s:integer;
begin
result:=false;
pBuf:=@Buf;
while count>0 do
begin
s:=Peer.ReceiveBuf(pBuf^,Count);
if s=0 then exit;
inc(pBuf,s);
dec(Count,s);
end;
result:=true;
end;
var len:word;
begin
if not Socket.Active then
raise Exception.Create('Not connected');
if not RecvBufFully(Socket.Socket,len,SizeOf(len)) then
begin
Socket.Active:=false;
raise Exception.Create('Bad packet');
end;
ReallocMem(Packet,len);
PPacket(Packet).Len:=len;
if not RecvBufFully(Socket.Socket,PPacket(Packet).Data,PPacket(Packet).len) then
begin
Socket.Active:=false;
raise Exception.Create('Bad packet');
end;
result:=Packet;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
reader:TMyProtoReader;
Packet:pointer;
begin
reader:=TMyProtoReader.Create;
try
reader.Connect;
while true do
begin
Packet:=reader.GetPacket;
ProcessPacket(Packet);
Application.ProcessMessages;
end;
finally
reader.Free;
end;
end;
-
> напоминает блочную работу, но сокет установлен в неблок - и это не правильно
- приберегите свой свою категоричность(и откровенный говнокод) до времен - когда поймете что (non)blocking - означает (не)блокирующий, а не "блочный"...
Блокирующий режим может быть использован в крайне ограниченном количестве сценариев - когда до получения определенного набора данных дальнейшее функционирование приложения невозможно... И при этом циклами не заморачиваются, а используют асинхронный ReadFile или асинхронный WSARecv...
-
han_malign (28.10.11 11:16) [51] покажи пример неблочного режима и сравни с тем что Очень злой господин пишет... а пишет он неблокирующий сокет в блокирующей манере - где хоть одно сообщение OnRead? han_malign (28.10.11 11:16) [51] Блокирующий режим может быть использован в крайне ограниченном количестве сценариев кто тебе сказал? Сокеты Беркли - блокирующие, весь линух на них, а микрософт к ним прицепил нативную для винды асинхронную модель сообщений есть один сценарий неприменимый для влоб блочного режима - высоконагруженный многоклиентский сервер где ограничение в колве потоков клиентскому сокету побарабану блок/неблок монописуально 1 поток
-
han_malign (28.10.11 11:16) [51] приберегите свой свою категоричность(и откровенный говнокод) до времен - когда поймете что (non)blocking - означает (не)блокирующий, а не "блочный"... напиши на данную тему в неблокирующей манере свой пример и посмотрим чей код говнее
-
> где хоть одно сообщение OnRead? ... > Сокеты Беркли - блокирующие, весь линух на них
- ссылку пожалуйста, на отсутствие неблокирующего режима в сокетах Беркли...
> и посмотрим чей код говнее
- Вы будете спорить, что код вешающий приложение до следующего входящего пакета и вываливающий исключение, если оно не пришло в течение getsockopt(SO_RCVTIMEO) - плохо пахнет?
-
-
han_malign (28.10.11 17:15) [54] - Вы будете спорить, что код вешающий приложение до следующего входящего пакета и вываливающий исключение, если оно не пришло в течение getsockopt(SO_RCVTIMEO) - плохо пахнет? продолжаем обсирать... на этот раз обделан код никак не связаный с сабжом сокетами... СЛИВ ЗАСЧИТАН...
-
Сергей М. © (28.10.11 19:56) [55] тут я погорячился... примеров unix+nonblock я не встречал вот и сделал неправильный вывод
-
> в неблоке никаких ReceivePackets не должно быть, а должно > быть OnRead и OnWrite
> а пишет он неблокирующий сокет в блокирующей манере - где > хоть одно сообщение OnRead?
Почему? ...
procedure SocketEvent(var M:TMessage); message WM_SOCKET_AUTH_EVENT;
...
if WSAAsyncSelect(FSocket,FWindowHandle,WM_SOCKET_AUTH_EVENT, FD_CONNECT or FD_READ or FD_WRITE or FD_CLOSE)= SOCKET_ERROR then
...
procedure TXXX.SocketEvent(var M:TMessage);
begin
case M.LParam of
FD_CONNECT:;
FD_READ: ReceivePackets;
FD_WRITE: SendPackets;
FD_CLOSE: CloseConnection;
end;
end;
разве это в "блокирующей манере"?
-
Очень Злой (31.10.11 11:11) [58] WSAAsyncSelect первое упоминание AsyncSelect...
-
> первое упоминание AsyncSelect...
первое упоминание было в [0], но чуть детальнее в: > Очень Злой (27.10.11 11:26) [23]
Ну не привел я с самого начала весь код, ибо меня больше интересовала методика прием данных с сокетов (с точки зрения транспортного уровня) и обработка пакетов (с точки зрения протокола прикладного уровня). Т.е. оптимальная организация такого процесса...
-
Вобщем написал я класс, который инкапсулирует в себе соединение с сервером, прохождение довольно сложной авторизации (на уровне прикладного протокола), после чего позволяет пользователю работать с сервером выполняя криптование исодящих пакетов и декриптование входящих.
Но, если мне нужно создавать несколько соединений с сервером, то я должен создавать столько же объектов - экземпляров этого класса. На мой взгляд это расточительно с точки зрения использования ресурсов. Кроме того есть другие причины, по которым желательно было бы чтобы данный класс поддерживал не 1 соединение с сервером а несколько. Пока возникла проблема такого рода: Кроме дескриптора сокета, имеются другие данные привязанные к этому сокету (ключи шифрования, буфер для временного хранения неполных пакетов, и т.д.). Но при использовании WSAAsyncSelect оконная функция получает сообщение, в котором указывается дескриптор сокета и прочие данные, а вот как мне при этом передать и получить ссылку на остальные данные, привязанные к сокету?
-
У тебя один-единственный поток обслуживает туеву хучу сокетов ?
-
> Сергей М. © (02.11.11 19:29) [62] > > У тебя один-единственный поток обслуживает туеву хучу сокетов > ?
Да. Ну не то чтобы туеву хучу, но сокетов 10-15
-
> сокетов 10-15
Смешная же цифирь)..
И чего ж там такого "расточительного" ?
-
> Смешная же цифирь)..
Ну в принципе да.
Я понимаю, что такие оптимизации в наше время не в моде, но все равно хочется сделать оптимальнее. Хотя бы не ради самого класса, а для усовершенствования своих знаний. Гуглить пробовал, но пока ничего не нашел, возможно потому, что не знаю как правильнее сформулировать поисковый запрос...
-
imho, ради десятка-другого коннектов нет резона всерьез задумываться над оптимальностью.
Пройтись в цикле по списку структур, ассоциированных с коннектами, в поисках нужного коннекта - это займет смехотворное время.
Даже в TServerSocket над этим Борланд не задумывалась (хотя это вовсе и не показатель оптимальности) - в неблок.режиме там для каждого сокета, включая слушающий, создается отдельное окно (читай - выделяется немалый общесистемный ресурс). Таким образом в ущерб объему занимаемых ресурсов там упрощается диспетчеризация асинхронных сокетных событий, хотя можно было бы обойтись всего одним окном и беготней по списку Connections[].
|