首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >通过套接字发送正确的记录大小

通过套接字发送正确的记录大小
EN

Stack Overflow用户
提问于 2016-12-14 06:14:38
回答 1查看 511关注 0票数 1

我有这样的记录:

代码语言:javascript
复制
type
  TSocks5_Packet = record
    Socks_ID: String[6];
    Socks_Packet: array of byte;
  end;

我使用String6是因为我确信这个字符串将始终包含6个字符(并尝试简化我的工作,但这还不够)。

我试着在这里发送这个记录:

代码语言:javascript
复制
procedure TForm1.SocksServerClientRead(Sender: TObject;
  Socket: TCustomWinSocket);
var
  Socks5_Request: TSocks5_Packet;
begin
  SetLength(Socks5_Request.Socks_Packet, Socket.ReceiveLength);
  Socket.ReceiveBuf(Socks5_Request.Socks_Packet[0], Length(Socks5_Request.Socks_Packet));
  Socks5_Request.Socks_ID:= PSocket_Identity(Socket.Data).Sock_ID;
  TunnelClient.SendBuf(Socks5_Request, Length(Socks5_Request.Socks_Packet + Length(Socks5_Request.Socks_ID)));
end;

我很确定问题出在SendBuf的第二个参数上,我在这个参数中指定了要发送的字节数。什么是正确的方法,我应该如何学习来学习它?

EN

回答 1

Stack Overflow用户

发布于 2016-12-14 08:17:33

您的代码有几个问题:

  1. 你期望String[6]是6个字节,但实际上是7个字节。您声明的ShortString的最大长度为6个AnsiChar字符,而ShortString包含该长度的前导字节。
  2. 您的记录不是packed,因此需要对齐填充。所以不要试图按原样发送它。您正在处理可变长度的数据,因此您应该序列化记录的内容。
  3. array of byte是一个动态数组。动态数组是指向内存中其他位置分配的数据的指针。数组变量本身的字节大小是SizeOf(Pointer),在32位上为4,在64位上为8。为了访问分配的内存块,您必须取消对指针的引用。
  4. TCP是一种流传输,它完全没有消息的概念。Socket.ReceiveLength报告在该特定时刻套接字的内部接收缓冲区中当前存在的未读字节数。这些字节是任意的,缓冲区中可能只有1个字节。
  5. 在发送数据时,不保证发送的字节数与请求的字节数一样多,可能会发送较少的字节数。SendBuf()的返回值表示接受发送的实际字节数,因此可能需要多次调用SendBuf()才能发送特定的数据。因此,循环发送,直到数据为exhausted.
  6. Since。如果您正在尝试通过隧道传输任意数据,则需要指定实际正在传输的字节数。

这样说来,可以尝试更多这样的东西:

代码语言:javascript
复制
function TForm1.SendData(Socket: TCustomWinSocket; const Data; DataLen: Integer): Boolean;
var
  PData: PByte;
  NumSent: Integer;
begin
  Result := False;
  PData := PByte(@Data);
  while DataLen > 0 do
  begin
    // SendBuf() returns -1 on error. If that error is WSAEWOULDBLOCK
    // then retry the send again.  Otherwise, TCustomWinSocket disconnects
    // itself, and if its OnError event handler does not set the ErrorCode
    // to 0 then it raises an ESocketError exception...
    //
    NumSent := Socket.SendBuf(PData^, DataLen);
    if NumSent = -1 then
    begin
      if WSAGetLastError() <> WSAEWOULDBLOCK then
        Exit;
    end else
    begin
      Inc(PData, NumSent);
      Dec(DataLen, NumSent);
    end;
  end;
  Result := True;
end;

function TForm1.SendInteger(Socket: TCustomWinSocket; Value: Integer): Boolean;
begin
  Value := htonl(Value);
  Result := SendData(Socket, Value, SizeOf(Value));
end;

function TForm1.SendString(Socket: TCustomWinSocket; const Value: String): Boolean;
var
  S: AnsiString; // or UTF8String
begin
  S := AnsiString(Value);
  // or: S := UTF8Encode(Value);
  Result := SendInteger(Socket, Length(S));
  if Result then
    Result := SendData(Socket, PAnsiChar(S)^, Length(S));
end;

function TForm1.SendBytes(Socket: TCustomWinSocket; const Data: array of Byte): Boolean;
begin
  Result := SendInteger(Socket, Length(Data));
  if Result then
    Result := SendData(Socket, PByte(Data)^, Length(Data));
end;

procedure TForm1.SocksServerClientRead(Sender: TObject; Socket: TCustomWinSocket);
var
  Packet: array of byte;
begin
  Len := Socket.ReceiveLength;
  if Len <= 0 the Exit;

  SetLength(Packet, Len);
  Len := Socket.ReceiveBuf(PByte(Packet)^, Len);
  if Len <= 0 the Exit;
  if Len < Length(Packet) then
    SetLength(Packet, Len);

  if SendString(TunnelClient, PSocket_Identity(Socket.Data).Sock_ID) then
    SendBytes(TunnelClient, Packet);
end;

然后相应地调整您的隧道接收器以根据需要反序列化这些值(读取Integer字节计数,使用ntohl()将其转换为主机字节顺序,然后读取指定的字节数)。

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/41131478

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档