powered by simpleCommunicator - 2.0.41     © 2025 Programmizd 02
Форумы / Delphi [игнор отключен] [закрыт для гостей] / Как правильно принять TCP поток ?
22 сообщений из 22, страница 1 из 1
Как правильно принять TCP поток ?
    #40132457
NewbieUser
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Привет.
Заранее извиняюсь если что не так объяснил.
Подскажите как правильно принимать TCP пакеты ?
Сделал вот так:
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
procedure TForm1.WMSOCK_RG_EVENT(var Msg: TMessage);
var
  SockError: integer;
  Len:integer;
begin
  //check error
  SockError:=WSAGetSelectError(Msg.lParam);
  if (SockError<>0) then begin
    if SockError=10053 then RichEdit1.Lines.Add('Соединение разорвано')
    else RichEdit1.Lines.Add('RG error: '+inttostr(SockError));
    CloseSock(RG_sock);
    Exit;
  end;
  // если ошибок нет:
  case WSAGetSelectEvent(Msg.lParam) of
    FD_Read: begin
      FillChar(main_buff,Length(main_buff),0);
      Len:=recv(RG_Sock,main_buff,MAX_PKT_SIZE,0);
      inc(RecvPktCount);
      GS_parser(main_buff,Len);
    end;
    FD_Close: begin
      CloseSocket(RG_Sock);
    end;
  end;
end;



Если пакеты приходят по одному, то все работает. Но иногда вместо несколько маленьких пакетов, приходит один большой (склееный) пакет и он обрабатывается целиком.
Пример (цветом выделены отдельные пакеты склееные в один):
0000 ba 00 44 eb 4b 42 ba bc 2e f2 6c 5c cf ce 6f ca
0010 c2 54 06 57 4d 46 ae 58 8f a3 c2 f2 61 60 c1 ad
0020 9c 0b 59 0f 2c 25 cd 18 3c ef 71 c3 50 51 f0 aa
0030 9b 0c 5e c7 e5 ec 04 f7 d2 01 9f 2d be bf 1e 44
0040 75 e2 b0 29 0b 02 ea 19 3c ef 71 41 d2 d3 72 1e
0050 2f 48 25 3e 1c 15 9d 1b 60 5c fd cd 5e 5f fe 92
0060 a3 14 06 1d 3f 36 de 1b 3e dc 02 32 a1 a0 01 6d
0070 5c cb 99 82 a0 a9 41 85 a0 73 ed dd 4e 4f ee 82
0080 b3 24 76 6d 4f 46 ae 6b 4e 9d 03 33 a0 a1 00 6c
0090 48 b0 c4 cf e7 ee ef 36 13 c0 5e 6e fd fc 5d 31
00a0 00 97 c5 de fc f5 1d d8 dd 4e d0 e0 73 72 d3 bf
00b0 bf 68 3a 21 03 0a e2 27 02 d1 ba 00 44 e8 48 41
00c0 b9 ba 28 f4 a2 93 00 01 a0 bc b6 20 72 89 90 9b
00d0 73 85 52 7e d7 e6 35 34 95 f9 c8 5f 0d 5b 78 71
00e0 99 4c 68 bb ed 5e cd cc 6d 37 06 91 c3 5a 78 71
00f0 99 6a 4f 9c ca 79 ea eb 4a 10 21 b6 e4 7d 5f 56
0100 be 4d 68 bb ed dc 4f 4e ef 83 b2 d5 b8 a3 81 88
0110 00 86 fd c1 a8 99 0a 0b aa c6 f7 40 52 49 6b 62
0120 8a 4f 6a 88 9e af 3c 3d 9c f0 c1 56 04 1f 3d 34
0130 dc 18 3d ee b8 89 1a 1b ba d6 e7 70 22 39 1b 12
0140 fa 3f 1a c9 9f ae 3d 3c 9d f1 d5 2d 59 52 7a 73
0150 72 ab 8e 5d 0b 3a a9 a8 09 65 54 c3 91 8a a8 a1
0160 49 8c 89 1a 4c 7d ee ef 4e 22 22 f5 a7 bc 9e 97
0170 7f ba 9f 4c ba 00 44 91 37 3e c6 ae 31 ed e3 d1 ///
0180 42 43 e2 42 4f d9 8b 20 3b 30 d8 28 ff d3 22 10
0190 03 02 a3 cf fe 69 3b 6d 4e 47 af 7a 5e 8d 83 33
01a0 a0 a1 00 5a 6b fc ae 37 15 1c f4 07 22 f1 ff 4f
01b0 dc dd 7c 26 17 80 d2 4b 69 60 88 7b 5e 8d 83 b1
01c0 22 23 82 ee df b8 d5 ce ec e5 6d eb 90 ac 9d af
01d0 3c 3d 9c f0 c1 76 64 7f 5d 54 bc 79 dc 37 79 4b
01e0 d8 d9 78 14 25 b2 e0 fb d9 d0 38 fc d9 0a 04 36
01f0 a5 a4 05 69 58 cf 9d 86 a4 ad 45 80 a5 76 78 4a
0200 d9 d8 79 15 31 c9 bd b6 9e 97 96 4f 6a b9 b7 85
0210 16 17 b6 da eb 7c 2e 35 17 1e f6 33 36 a5 ab 99
0220 0a 0b aa 46 4f 98 ca d1 f3 fa 12 d7 f2 21

Как сделать так чтобы такие пакеты при приеме делились на части и обрабатывались по отдельности ? Разделителем служит размер пакета это первые 2 байта. Пишут что нужно принимать в цикле и читать весь поток, но пример как это сделать я не нашел.
Заранее спасибо.
...
Рейтинг: 0 / 0
Как правильно принять TCP поток ?
    #40132477
ъъъъъ
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
NewbieUser,

ищи книжку Антона Григорьева "О чем не пишут в книгах по Delphi".
...
Рейтинг: 0 / 0
Как правильно принять TCP поток ?
    #40132496
Barlone
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ну а как бы вы читали это из файла? Как читать - зависит от самого содержимого.
Пакеты в tcp не имеют отношения к прикладному уровню. Все, что отправлено, может при отправке склеиться вместе, а при приеме порезаться на пакеты произвольной длины.
...
Рейтинг: 0 / 0
Как правильно принять TCP поток ?
    #40132661
Фотография _Vasilisk_
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
NewbieUser
Но иногда вместо несколько маленьких пакетов, приходит один большой (склееный) пакет и он обрабатывается целиком.
Я даже больше скажу - маленькие пакеты могут разбиваться на части
NewbieUser
Разделителем служит размер пакета это первые 2 байта
Вот так и делаете.
1. Читаете два байта длины
2. Читаете данные пакета, пока не вычитаете всю длину
3. Обрабатываете полученные данные
4. Возвращаетесь к пункту 1
...
Рейтинг: 0 / 0
Как правильно принять TCP поток ?
    #40133231
NewbieUser
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
ъъъъъ
NewbieUser,

ищи книжку Антона Григорьева "О чем не пишут в книгах по Delphi".

Спасибо.
Я так понял читать нужно как то так:
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
function ReadFromSocket(var Buffer; Cnt:Integer):Boolean;
var	
Res,Total:	Integer;
begin
	Total:= 0;
	repeat
	 Res:= recv(RG_sock, (PChar(@Buffer) + Total)^, Cnt —Total, 0);
	if Res = 0 then
	begin
	Exit;
	end;
	if Res < 0	then
	begin
//	Произошла ошибка при чтении
	Exit;
	end;
	Inc(Total,	Res);
	until Total >= Cnt;
	Result:=	True;

и в цикле как-то дергать эту функцию

while ReadFromSocket(buffer, 2) do   //читаем 2 байта длины, если прочиталось
if RecvPacket(buffer, cnt-2) then begin //читаем данные, если прочиталось
GS_parser(buffer,cnt);  // обрабатываем.
end;


и делать это наверно нужно в отдельном потоке.
...
Рейтинг: 0 / 0
Как правильно принять TCP поток ?
    #40133254
ъъъъъ
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
NewbieUser,

ну в книжке же миллион примеров...
...
Рейтинг: 0 / 0
Как правильно принять TCP поток ?
    #40133298
Фотография _Vasilisk_
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
NewbieUser
Я так понял читать нужно как то так:
Нет. Вообще.

Если у вас сокет блокирующий, то достаточно одного вызова recv и он будет висеть, пока не прочитает все байты, которые ему сказали. Если неблокирующий, то он будет читать столько байт, сколько сможет и при повторном вызове это нужно учитывать. Вы это предусмотрели в ReadFromSocket, но потом вызываете метод в цикле не изменяя указателей на буфер
...
Рейтинг: 0 / 0
Как правильно принять TCP поток ?
    #40133310
Dimitry Sibiryakov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
_Vasilisk_Если у вас сокет блокирующий, то достаточно одного вызова recv и он будет
висеть, пока не прочитает все байты, которые ему сказали.

Уверен? Документация на него утверждает обратное:
MSDNFor connection-oriented sockets (type SOCK_STREAM for example),
calling recv will return as much data as is currently available—up to the size
of the buffer specified.
manThe receive calls normally return any data available, up to the
requested amount, rather than waiting for receipt of the full amount
requested.
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
Как правильно принять TCP поток ?
    #40133313
Фотография _Vasilisk_
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dimitry Sibiryakov
Уверен?
Да. Когда-то приходилось использовать setsockopt(SO_RCVTIMEO,...) чтобы не потерять программу
...
Рейтинг: 0 / 0
Как правильно принять TCP поток ?
    #40133315
Dimitry Sibiryakov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Это если оно встало в ожидание при пустом буфере, да. Но если пришли только два
килобайта из отправленного и запрошенного мегабайта - оно таки сразу вернёт эти
два килобайта.
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
Как правильно принять TCP поток ?
    #40133320
Фотография _Vasilisk_
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dimitry Sibiryakov
Это если оно встало в ожидание при пустом буфере, да
Верно. Был не прав.
...
Рейтинг: 0 / 0
Как правильно принять TCP поток ?
    #40133779
NewbieUser
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Вот так сделал, вроде работает )
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
procedure TForm1.WMSOCK_RG_EVENT(var Msg: TMessage);
var
  SockError: integer;
  P,L,size,Len:integer;

  temp_buff: array of byte;
begin
    //чтото пошло не так
  SockError:=WSAGetSelectError(Msg.lParam);
  if (SockError<>0) then begin
    if SockError=10053 then RichEdit1.Lines.Add('RG was Disconnected')
    else RichEdit1.Lines.Add('RG error: '+inttostr(SockError));
    CloseSock(RG_sock);
    Exit;
  end;
  //если все работает:
  case WSAGetSelectEvent(Msg.lParam) of
    FD_Read: begin
    //  FillChar(main_buff,Length(main_buff),0);
      Len:=recv(RG_Sock,main_buff,MAX_PKT_SIZE,0);
      P:=0;
      if Len>2 then begin

      while P<Len do
        begin
       Move( main_buff[p],size,2);  //берем размер пакета
        SetLength(temp_buff,size);
        Move( main_buff[p], temp_buff[0],size); //копируем пакет в буфер для обработки
          INC (P,size);      //смещение
      inc(RecvPktCount);
      if size >3 then begin

      GS_parser(temp_buff,size);
      //
    end;
    end;
    end;
    end;
    FD_Close: begin
      RichEdit1.Lines.Add('RG Disconnected');
   
      CloseSocket(RG_Sock);
    end;
  end;
end;

...
Рейтинг: 0 / 0
Как правильно принять TCP поток ?
    #40133813
Фотография _Vasilisk_
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
NewbieUser
Вот так сделал, вроде работает )
Это только так кажется
NewbieUser
Код: pascal
1.
2.
3.
Len:=recv(RG_Sock,main_buff,MAX_PKT_SIZE,0);
P:=0;
if Len>2 then begin

Существует вероятность (малая, но все же), что размер мы будем получать по одному байту. Тогда условие не выполнится никогда
NewbieUser
Код: pascal
1.
while P<Len do

В буфере могут быть данные более одного пакета, а здесь считывается только текущий пакет
NewbieUser
Код: pascal
1.
 Move( main_buff[p], temp_buff[0],size); //копируем пакет в буфер для обработки

В буфере может быть меньше данных, чем длина пакета
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
 FD_Read: begin
  while True do begin
    Len := SockCheck(recv(RG_Sock, cur_main_buff^, RestLen, 0);
    Dec(RestLen, Len);
    Inc(cur_main_buff, Len);
    if RestLen > 0 then
      Break;
  case Phase of
    waitLen: begin
      Phase := waitData;
      RestLen := PSmallint(@main_buff[0])^;
    end;
    waitData: begin
      ProcessPacket(main_buff);
      Phase := waitLen;
      RestLen := SzeOf(Smallint);
      cur_main_buff := @main_buff[0];
    end;
  end;
end;

Все переменные, кроме Len - поля класса
...
Рейтинг: 0 / 0
Как правильно принять TCP поток ?
    #40134444
NewbieUser
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
_Vasilisk_,
Вы были правы, на локальном компе оно работает нормально, а при подключении через интернет полезли глюки.

Можете уточнить некоторые моменты ?
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
 FD_Read: begin
  while True do begin
    Len := SockCheck(recv(RG_Sock, cur_main_buff^, RestLen, 0); //SockCheck непонятно.
    Dec(RestLen, Len); //получаем разницу между ожидаемым и полученным
    Inc(cur_main_buff, Len); //Увеличиваем буфер на его длину.
    if RestLen > 0 then //если пришло меньше чем нужно ждем оставшиеся данные
      Break;
  case Phase of //попеременно получаем длину и данные
    waitLen: begin
      Phase := waitData;
      RestLen := PSmallint(@main_buff[0])^;//получаем 2 байта размера
    end;
    waitData: begin
      ProcessPacket(main_buff); //Непонятно.
      Phase := waitLen;
      RestLen := SizeOf(Smallint); //сколько данных необходимо читать
      cur_main_buff := @main_buff[0]; // Не совсем понятно. Тут наверно cur_main_buff и main_buff нужно поменять местами.
    end;
  end;
end;
...
Рейтинг: 0 / 0
Как правильно принять TCP поток ?
    #40134515
Фотография _Vasilisk_
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
NewbieUser
Код: pascal
1.
//SockCheck непонятно.

Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
procedure RaiseSocketError(AError: Integer);
var
  LExcept: ESocketError;
begin
  LExcept := ESocketError.CreateResFmt(@SOSError, [AError, SysErrorMessage(AError), '']);
  LExcept.ErrorCode := AError;
  raise LExcept;
end;

function SockCheck(ARes: Integer; ASkipNonBlock: Boolean): Integer;
var
  LError: Integer;
begin
  if ARes = SOCKET_ERROR then begin
    LError := WSAGetLastError;
    if not ASkipNonBlock or (LError <> WSAEWOULDBLOCK) then
      RaiseSocketError(LError);
  end;
  Result := ARes;
end;


NewbieUser
Код: pascal
1.
 Dec(RestLen, Len); //получаем разницу между ожидаемым и полученным

Нет. Уменьшаем размер ожидаемых данных на размер полученных
NewbieUser
Код: pascal
1.
 Inc(cur_main_buff, Len); //Увеличиваем буфер на его длину.

Нет. Смещаем указатель в буфере на размер прочитанных данных. cur_main_buff объявлен как
Код: pascal
1.
cur_main_buff: PByte;


NewbieUser
Код: pascal
1.
ProcessPacket(main_buff); //Непонятно.

Обрабатываем весь полученный пакет. Все данные по нему мы уже получили
NewbieUser
Код: pascal
1.
RestLen := SizeOf(Smallint); //сколько данных необходимо читать

для следующего пакета ожидаем вначале два байта длины
NewbieUser
Код: pascal
1.
cur_main_buff := @main_buff[0]; // Не совсем понятно. Тут наверно cur_main_buff и main_buff нужно поменять местами.

Нет. main_buff - это массив байт куда мы читаем, cur_main_buff - это указатель на первый свободный байт этого массива
...
Рейтинг: 0 / 0
Как правильно принять TCP поток ?
    #40135057
NewbieUser
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
_Vasilisk_,

Не получается. Точнее с отдельными пакетами работает,а на склейке опять затык.
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
var
 RestLen:integer;
 Phase:integer;
 cur_main_buff: PByte;
 main_buff: array[0..65536] of byte;

procedure TForm1.Button3Click(Sender: TObject);
begin
  RG_connect;
   Phase:=2;
    cur_main_buff := @main_buff;
    RestLen:=0;
end;

procedure TForm1.WMSOCK_RG_EVENT(var Msg: TMessage);
var
  SockError: integer;
  Len:integer;
begin 
  SockError:=WSAGetSelectError(Msg.lParam);
  if (SockError<>0) then begin
    if SockError=10053 then RichEdit1.Lines.Add('RG was Disconnected')
    else RichEdit1.Lines.Add('RG error: '+inttostr(SockError));
   CloseSock(RG_sock);
    Exit;
  end;
  case WSAGetSelectEvent(Msg.lParam) of
    FD_Read: begin
     while True do begin
    Len := recv(RG_Sock, cur_main_buff^, RestLen, 0); 
    Dec(RestLen, Len); //Уменьшаем размер ожидаемых данных на размер полученных
    Inc(cur_main_buff, Len); //Смещаем указатель в буфере на размер прочитанных данных
    if RestLen-2 > 0 then //Иначе на выходе всегда получается разница в 2 байта от сюда RestLen := SizeOf(Smallint)
      Break;
  case Phase of //
    1: begin
      Phase := 2;
      RestLen := PSmallint(@main_buff[0])^;//получаем длину пакета
    end;
    2: begin
    Gs_parser(main_buff); //обработка
      Phase := 1;
      RestLen := SizeOf(Smallint); //ожидаем вначале два байта длины
      cur_main_buff := @main_buff[0];
      end;
  end;
end;
end;
    FD_Close: begin
      RichEdit1.Lines.Add('RG was Disconnected');    
    end;
  end;
end;
...
Рейтинг: 0 / 0
Как правильно принять TCP поток ?
    #40135065
ъъъъъ
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
NewbieUser,

а что мешается тупо сделать, как в книжке?
Там твоя "проблемма" тщательно разжевана.
...
Рейтинг: 0 / 0
Как правильно принять TCP поток ?
    #40135153
Фотография _Vasilisk_
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
NewbieUser
Код: pascal
1.
2.
3.
4.
5.
6.
7.
procedure TForm1.Button3Click(Sender: TObject);
begin
  RG_connect;
   Phase:=2;
    cur_main_buff := @main_buff;
    RestLen:=0;
end;

При написании этого голову использовать не пробовали?
NewbieUser
Код: pascal
1.
if RestLen-2 > 0 then //Иначе на выходе всегда получается разница в 2 байта от сюда RestLen := SizeOf(Smallint)

Продолжайте фантазировать. Дальше я умываю руки
...
Рейтинг: 0 / 0
Как правильно принять TCP поток ?
    #40135624
NewbieUser
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Все, сделал.
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
function ReadFromSocket(var Buffer; Cnt:Integer):Boolean;
var	
Res,Total:	Integer;
begin
	Total:= 0;
	repeat
	 Res:= recv(RG_sock, Pointer(Cardinal(@Buffer) + Total)^, Cnt -Total, 0);
	if Res = 0 then
	begin
	Exit;
	end;
	if Res < 0	then
	begin
	Exit;
	end;
	Inc(Total,	Res);
	until Total >= Cnt;
	Result:=	True;
end;


while ReadFromSocket(RWbuf, 2) do   
if ReadFromSocket(RWbuf, cnt-2) then begin 
GS_parser;  // обрабатываем.
...
Рейтинг: 0 / 0
Как правильно принять TCP поток ?
    #40135633
Dimitry Sibiryakov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ужос. Просто ужос.
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
Как правильно принять TCP поток ?
    #40135637
YuRock
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
NewbieUser
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
	if Res = 0 then
	begin
	Exit;
	end;
	if Res < 0	then
	begin
	Exit;
	end;
	Inc(Total,	Res);
	until Total >= Cnt;
	Result:=	True;


Первый then неправильно выровнен.
...
Рейтинг: 0 / 0
Как правильно принять TCP поток ?
    #40135659
ъъъъъ
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Прикольно!
...
Рейтинг: 0 / 0
22 сообщений из 22, страница 1 из 1
Форумы / Delphi [игнор отключен] [закрыт для гостей] / Как правильно принять TCP поток ?
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]