-
Клиент отправляет сразу после подключения:
type TPrefix=record CMD: Integer; Size: Integer; end;
procedure TfMain.ClientSocket1Connect(Sender: TObject; Socket: TCustomWinSocket); var Prefix: TPrefix; ClientNo: String[6]; begin if Socket.Connected then begin Prefix.CMD:=CMD_LOGIN; Prefix.Size:=6; ClientSocket1.Socket.SendBuf(Prefix,SizeOf(Prefix)); ClientNo:=C_CLIENTNO; ClientSocket1.Socket.SendBuf(ClientNo,Prefix.Size); end; end;
сервер принимает:
procedure TForm1.ServerClientRead(Sender: TObject; Socket: TCustomWinSocket); var ClientNo: String[6]; SocketData: TclAssoc; Prefix: TPrefix; begin Socket.ReceiveBuf(Prefix,Socket.ReceiveLength); case Prefix.CMD of {CMD_LOGIN} 0:begin Socket.ReceiveBuf(ClientNo,Prefix.Size+1); ShowMessage('CMD_LOGIN'); ShowMessage(ClientNo); ShowMessage(IntTosTr(Length(ClientNo))); end; 2:; end; end;
При первовызове SendBuf(Prefix...) доставляется все как задумано, при втором вызове SendBuf(ClientNo...) принимается 0 байт и ClientNo=''. А если указать что принимать нужно 8 байт, то длина ClientNo в итоге получится 48. Заметил такую штуку. Если установить брекпоинт в том месте где принимается ClientNo то все принимается нормально... Помогите разобраться почему? Я в ступоре...
-
> Socket.ReceiveBuf(Prefix,Socket.ReceiveLength);
- тогда уж Socket.ReceiveBuf(Prefix,sizeof(Prefix)); т.к. в Socket.ReceiveLength - может быть уже весь пакет(и только чудом у тебя не запоролся стек, при переполнении буфера Prefix - размещенного на стеке)... > Socket.ReceiveBuf(ClientNo,Prefix.Size+1);
- отправляешь Prefix.Size, а принимаешь почему то Prefix.Size+1... при этом принимаешь и отправляешь ShortString в которой первый(и похоже лишний) символ - длинна строки...
-
> т.к. в Socket.ReceiveLength - может быть уже весь пакет(и > только чудом у тебя не запоролся стек, при переполнении > буфера Prefix - размещенного на стеке)...
Видимых результатов не дало... Я не пойму почему при брекпоинте я вижу то что отправил??
-
If Socket.ReceiveLength>=SizeOf(Prefix) Then Socket.ReceiveBuf(Prefix,SizeOf(Prefix)); If Socket.ReceiveLength>=Prefix.Size Then {... Считываем что надо!!!! Хотя если размер буфера приема меньше передаваемых данных (по умолчанию 4КБ), то надо просто сохранять все что пришло в свой буфер!!!! Иначе ничего не получиться.... ...}
-
Не кидайте тапками... Обшибся :) Не 4КБ, а 8 КБ... Давно к сетям не обращался...
>molinero TCP - потоковый протокол, это не значит, что если пришло сообщение/событие о приходе данных, то в буфере 100% будет все что ты послал!!!! Т.е., гипотетически, даже твой префикс в 8 байт может приходить по 1 байту! И твой код должен быть готовым к принятию фрагментированных/частичных данных!!! В том числе и то, что конец одного ТВОЕГО пакета может быть склеен с началом другого пакета!!!
-
Как вариант, если собираешься посылать/принимать пакеты свыше 4 КБ (не рекомендуется посылать больше чем MaxSize(8192) div 2 пакеты с данными, может постоянно выдавать ошибку переполнения буфера...), то стоит сделать примерно так... Type
PClientData=^TClientData;
TClientData=Record
Prefix:TPrefix;
ReadBytes:Integer;
prefixReady:Boolean;
<Какой-нибудь буфер на твое усмотрение (Stream/Динамический массив)>
End;
В процедуре коннекта: var
CData:PClientData;
Begin
...
New(CData);
CData^.PrefixReady:=False;
CData^.ReadBytes:=0;
Socket.Data:=CData;
...
End;
В процедуре приема данных: Begin ... If (not Socket.Data^.PrefixReady)and(Socket.ReceiveLength>=SizeOf(TPrefix)) Then <читаем префикс и устанавливаем PrefixReady=True> Else If Socket.Data^.ReadBytes<Socket.Data^.prefix.Size then <Вычисляем кол-во байт, которые необходимо считать, и если считали все необходимое, сбрасываем PrefixReady=False и ReadBytes=0!!!! В принципе УСЕ :), дальше делай с данными все что хочешь ;)> ... End;
-
Да забыл в процедуре дисконнекта подчищаем созданную динам. запись: ...
Dispose(Socket.Data);
...
-
Еще раз оговорюсь, сам заметил ошибку... :) If (Socket.Data^.PrefixReady)and(Socket.Data^.ReadBytes<Socket.Data^.prefix.Size) then
-
FireMan_Alexey благодарю за участие. Я сделал немного подругому. Принимаю в буфер, а потом накладываю на буфер указатель (PTPrefix).
type PTPrefix=^TPrefix; TPrefix=Packed record CMD: Integer; Size: Integer; end;
var Buf: Pointer; Prefix: TPrefix; ReceiveLength: Integer;
begin ReceiveLength:=0;
if Socket.ReceiveLength>=SizeOf(TPrefix)then Socket.ReceiveBuf(Buf^,SizeOf(TPrefix))
else begin while ReceiveLength<=SizeOf(Tprefix)do begin Socket.ReceiveBuf(Buf^,Socket.ReceiveLength); ReceiveLength:=+Socket.ReceiveLength; end; end;
Prefix:=PTPrefix(Buf)^;
В Prefix записывается то что я отправил, но в конце процедуры ошибка доступа к памяти. Нужно освободить памаять (Buf)?
И еще, Если длать так, то Buf будет перезаписваться и указывать на адрес новой порции данных? Как мне добавитьв буфер?
-
> molinero (01.06.2011 09:07:08) [8]
В конце ReceiveLength становится равным 0
-
С ошибкой доступа к памяти разобрался. Сначала GetMem(Buf,SizeOf(TPrefix)) Когда буфер больше ненужен FreeMem(Buf)
Дальше опять непонятно... На форуме прочитал, что если данные приходят не все сразу, нужно принимать в цикле.
Отправляю
procedure TfMain.ClientSocket1Connect(Sender: TObject; Socket: TCustomWinSocket); var Prefix: PTPrefix; ClientNo: String[6]; begin if Socket.Connected then begin New(Prefix); Prefix^.CMD:=CMD_LOGIN; Prefix^.Size:=6; ClientNo:=C_CLIENTNO; ClientSocket1.Socket.SendBuf(Prefix^,SizeOf(TPrefix));//Отправляем размер и команду ClientSocket1.Socket.SendBuf(ClientNo,Length(ClientNo));//Отправляем номер клиента Dispose(Prefix); end; end;
//принимаю
Prefix:=PTPrefix(Buf)^; FreeMem(Buf); case Prefix.CMD of {CMD_LOGIN} 0:begin //здесь я уже принял Prefix и теперь хочу принять номер клиента GetMem(Buf,Prefix.Size); ReceiveLength:=0; if Socket.ReceiveLength>=Prefix.Size then//здесь ReceiveLength=0 Socket.ReceiveBuf(Buf^,Prefix.Size) else while ReceiveLength<=Prefix.Size do//и вот здесь зацикливается begin Socket.ReceiveBuf(Buf^,Socket.ReceiveLength); ReceiveLength:=+Socket.ReceiveLength; end; s:=PClientNo(Buf)^; end; 2:; end;
Собственно вопрос. Как принимать в цикле, если на клиенте был вызван два раза подряд метод отправки данных?
-
> molinero (01.06.2011 10:09:10) [10]
А если один раз нажали, уже умеешь и правильно?
-
>molinero Чего то я ничего не понял...!!! :-\ Если у тебя в параметре Socket.ReceiveLength не хватает цифры до длины твоего префикса, то забей и не читай ничего до следующего события прихода данных!!! Я для чего тебе написал вот это и как заполнять это поле ??????: Type
PClientData=^TClientData;
TClientData=Record
Prefix:TPrefix;
ReadBytes:Integer;
prefixReady:Boolean;
<Какой-нибудь буфер на твое усмотрение (Stream/Динамический массив)>
End; Еще если ты собираешься в цикле писать данные из сокета в буфер, то какого лешего ты пишешь в одно и тоже место: while ReceiveLength<=SizeOf(Tprefix)do
begin
Socket.ReceiveBuf(Buf^,Socket.ReceiveLength);
ReceiveLength:=+Socket.ReceiveLength;
end; ReceiveBuf и SendBuf - это функции!!!!! Они возвращают кол-во посланных байт!!!!!!Даже если ты посылаешь/принимаешь 1 байт это не значит, что эти функции отработают на 100%!!! А как показано в твоем коде, ты не разу даже не поинтересовался ушли ли все данные у тебя или нет!!!!
-
Вот правильное принятие данных! RecSize:=Socket.ReceiveLength;
Size:= Socket.ReceiveBuf(Buf^,RecSize); В нормальном случае Size=RecSize, а может быть и не равны!!!!
-
И зачем тебе динамический префикс?????!!!!!!
Я тебе вот это показал:
PClientData=^TClientData;
Для того, чтобы ты смог использовать свойство Socket.Data: pointer!
В твоем случае, когда на сервере у тебя будет не 1 клиент, то возникнет проблема как привязать данные к каждому клиенту!!!!!!!! По сути расписал тебе всю схему доставки! Только осталось вписать в обработчики событий!!!
-
>molinero Не обижайся, но это полная охинея!!!! procedure TfMain.ClientSocket1Connect(Sender: TObject; Socket: TCustomWinSocket);
var Prefix: PTPrefix;
ClientNo: String[6];
begin
if Socket.Connected then - Он точно законнектился, проверять не надо!!!!
begin
New(Prefix); - Зачем это, сделай свой TPrefix, он все равно умрет в стеке при выходе из процедуры!!!!
Prefix^.CMD:=CMD_LOGIN;
Prefix^.Size:=6;
ClientNo:=C_CLIENTNO;
ClientSocket1.Socket.SendBuf(Prefix^,SizeOf(TPrefix)); - Где проверка, что данные ушли и сколько их ушло???
ClientSocket1.Socket.SendBuf(ClientNo,Length(ClientNo));- здесь аналогично!!!
Dispose(Prefix); - Убрать!!!!! Как и New выше!!!!
end;
end;
-
Да! Работай вот так!!!! Socket.SendBuf(Prefix^,SizeOf(TPrefix)); а не ClientSocket1.Socket.SendBuf(Prefix^,SizeOf(TPrefix));
-
Далее на сервере: procedure TForm1.ServerSocket1ClientConnect(Sender: TObject;
Socket: TCustomWinSocket);
var
CData:PClientData;
Begin
...
New(CData);
CData^.PrefixReady:=False;
CData^.ReadBytes:=0;
Socket.Data:=CData;
...
End;
procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
Socket: TCustomWinSocket);
Begin
...
If (not Socket.Data^.PrefixReady)and(Socket.ReceiveLength>=SizeOf(TPrefix)) Then
<читаем префикс и устанавливаем PrefixReady=True>
Else
If Socket.Data^.ReadBytes<Socket.Data^.prefix.Size then
<Вычисляем кол-во байт, которые необходимо считать, и если считали все необходимое, сбрасываем PrefixReady=False и ReadBytes=0!!!! В принципе УСЕ :), дальше делай с данными все что хочешь ;)>
...
End;
Повторился, но это только для понимания!!!
-
procedure TForm1.ServerSocket1ClientDisconnect(Sender: TObject;
Socket: TCustomWinSocket);
begin
Dispose(Socket.Data);
end;
-
Совет! Почитай про динамические массивы! Функцию SetLength! Потому что, судя из выше написанного тобою, ты слабо разбираешься/понимаешь в работе указателей и адресации!
Без обид! ;)
|