Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading Delphi:为什么Socket.ReadString(10)有时不';我得不到正确的数据_Multithreading_Delphi - Fatal编程技术网

Multithreading Delphi:为什么Socket.ReadString(10)有时不';我得不到正确的数据

Multithreading Delphi:为什么Socket.ReadString(10)有时不';我得不到正确的数据,multithreading,delphi,Multithreading,Delphi,服务器: procedure TForm1.IdTCPServer1Execute(AContext: TIdContext); var len : integer; rest_packet : string; packet_length : string; begin try // length 10, it will be the rest packet length: 0000000545 // So the bug i

服务器:

procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
var
  len : integer;
  rest_packet : string;
  packet_length : string;
begin

        try
          // length 10, it will be the rest packet length: 0000000545
          // So the bug is in ReadBytes(10) , some times it do not receive the length 0000000015 but receive a mix of data+length, something like: "00015Hello" or "loWorldApp" etc.
          packet_length := AContext.Connection.Socket.ReadString(10);
        except
          on E:Exception do begin Exit; end;
        end;

        // Convert string to int: 0000000545 , to 545
        try
          len := StrToInt(packet_length);
        except
          on E:Exception do begin Exit; end;
        end;


        // get reset of data, size: 545
        try
          rest_packet := AContext.Connection.Socket.ReadString(len);
        except
          on E:Exception do begin Exit; end;
        end;
客户端:

procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
var
  len : integer;
  rest_packet : string;
  packet_length : string;
begin

        try
          // length 10, it will be the rest packet length: 0000000545
          // So the bug is in ReadBytes(10) , some times it do not receive the length 0000000015 but receive a mix of data+length, something like: "00015Hello" or "loWorldApp" etc.
          packet_length := AContext.Connection.Socket.ReadString(10);
        except
          on E:Exception do begin Exit; end;
        end;

        // Convert string to int: 0000000545 , to 545
        try
          len := StrToInt(packet_length);
        except
          on E:Exception do begin Exit; end;
        end;


        // get reset of data, size: 545
        try
          rest_packet := AContext.Connection.Socket.ReadString(len);
        except
          on E:Exception do begin Exit; end;
        end;
客户端以多线程方式向服务器发送数据,大约13个客户端通过一个套接字连接向服务器发送数据。客户端发送的数据如下所示:

    EnterCriticalSection(CS);
    // Memo1.Lines.Add(packet); // i can see here that all works fine.
    Ftunnel.Socket.Write(packet);
    LeaveCriticalSection(CS);
initialization
  InitializeCriticalSection(CS);
finalization
  DeleteCriticalSection(CS);
CS-是全局的,创建方式如下:

    EnterCriticalSection(CS);
    // Memo1.Lines.Add(packet); // i can see here that all works fine.
    Ftunnel.Socket.Write(packet);
    LeaveCriticalSection(CS);
initialization
  InitializeCriticalSection(CS);
finalization
  DeleteCriticalSection(CS);
数据包的结构是:

长度数据

例如:

00000000 15HelloWorldApple

所以这个bug是以ReadBytes(10)表示的,有时候它不接收长度0000000015,而是接收数据+长度的混合,比如:“00015Hello”或“loWorldApp”等等

为什么会这样,我做错了什么

ReadTimeout默认为-1

下面是如何查看客户端代码:

procedure ss_thread.Execute;
var ss : TIdTCPClient;
    unix_time : integer;
    data : TIdBytes;
    packet, s : string;
begin
    // connecting to website to get a data
    ss := TIdTCPClient.Create(nil);
    try
      with TIdTcpClient(ss) do
      begin
        Host := '127.0.0.1';
        Port := 80;
        Connect;
      end;
    except
      on E:Exception do
      begin
        ss.Disconnect;
        exit;
      end;
    end;


    // writing a data from server to connected web site server
    try
      ss.Socket.Write(Fff_data);
    except
      on E:Exception do begin end;
    end;


    unix_time := DateTimeToUnix(NOW);


    // getting data from web site server, all looks thread safe?
    while True do
    begin
      ss.Socket.CheckForDataOnSource(5);
      if not ss.Socket.InputBufferIsEmpty then
      begin
        SetLength(data, 0);
        ss.Socket.InputBuffer.ExtractToBytes(data);
        s := TIdEncoderMIME.EncodeBytes(data);
        packet := make_my_length(s) + s;
        ss.Socket.InputBuffer.Clear;
        unix_time := DateTimeToUnix(NOW);
        try
            // sending data from web site, to server
            EnterCriticalSection(CS);
            tunnel.Socket.Write(packet);
            LeaveCriticalSection(CS);
        except
          on E:Exception do begin end;
        end;
      end;

      // disconnecting if no more data for 120sec
      if (DateTimeToUnix(NOW) - unix_time) > 120 then
      begin
        ss.Disconnect;
        break;
      end;

      // killing thread if web site server disconnected us
      if not ss.Connected then
      begin
        break;
      end;

      // disconnecting from web site server and killing thread if tunnel died
      if not tunnel.Connected then
      begin
        ss.Disconnect;
        break;
      end;


    end;

// terminate thread
Terminate;
end;
使我的长度代码,工作良好:

function make_my_length(s:string):string;
var len, i : integer;
    res : string;
begin
  if s='' then
  begin
    Result := '';
    Exit;
  end;
  res := '';
  len := Length(s);
  if len < 10 then
    for i:=1 to (10 - len) do
    begin
      res := res + '0';
    end;
  Result := res + s;
end;
函数make_my_length(s:string):string;
变量len,i:整数;
res:字符串;
开始
如果s='',那么
开始
结果:='';
出口
结束;
res:='';
长度:=长度(s);
如果len<10,则
对于i:=1到(10-len)do
开始
res:=res+'0';
结束;
结果:=res+s;
结束;

Delphi 2010,Indy 10,Win7

您描述的症状与您的客户端线程在访问
Ftunnel
时未正确共享单个CS一致,因此多个写入操作可能会继续相互重叠,这是CS试图避免的。您是否在与
CheckForData\u from\u ss()
方法相同的单元中创建CS?您是否验证了每个线程确实正在访问相同的CS而不是不同的CS?实际上从哪里调用
CheckForData\u from\u ss
()?直接从工作线程访问
TMemo
(或任何其他UI控件,就这一点而言),就像代码所暗示的那样,是危险的,如果内存被破坏,可能会导致各种副作用。访问UI时,您需要与主线程同步,例如与Indy的
TIdSync
TIdNotify
类同步

另一种可能是接收服务器端没有正确读取隧道数据。例如,如果服务器在某一点丢失了一些字节,那么所有后续读取都将位于错误的帧偏移。如果要对隧道数据进行帧处理,则应在帧中添加识别标记,以便在尝试解码之前知道服务器是否接收到有效帧,并在标记不同步时进行恢复

编辑:在客户端线程中尝试以下操作:

var
  CS: TCriticalSection;

procedure ss_thread.Execute; 
var
  ss : TIdTCPClient; 
  data : TIdBytes; 
  packet, s : string; 
begin 
  // connecting to website to get a data 
  ss := TIdTCPClient.Create(nil); 
  try
    ss.Host := '127.0.0.1'; 
    ss.Port := 80; 
    ss.ReadTimeout := 120000;
    ss.Connect; 

    // writing a data from server to connected web site server 
    ss.IOHandler.Write(Fff_data); 

    // getting data from web site server
    while not Terminated do
    begin
      SetLength(data, 0); 
      ss.IOHandler.ReadBytes(data, -1, False);
      s := TIdEncoderMIME.EncodeBytes(data); 
      packet := make_my_length(s) + s; 
      // sending data from web site, to server 
      CS.Enter; 
      try
        tunnel.IOHandler.Write(packet); 
      finally
        CS.Leave; 
      end;
    end; 
  finally
    ss.Free;
  end;
end; 

initialization
  CS := TCriticalSection.Create;
finalization
  CS.Free; 

您描述的症状与您的客户端线程在访问
Ftunnel
时未正确共享单个CS一致,因此多个写入操作可能会继续相互重叠,这正是CS试图避免的。您是否在与
CheckForData\u from\u ss()
方法相同的单元中创建CS?您是否验证了每个线程确实正在访问相同的CS而不是不同的CS?实际上从哪里调用
CheckForData\u from\u ss
()?直接从工作线程访问
TMemo
(或任何其他UI控件,就这一点而言),就像代码所暗示的那样,是危险的,如果内存被破坏,可能会导致各种副作用。访问UI时,您需要与主线程同步,例如与Indy的
TIdSync
TIdNotify
类同步

另一种可能是接收服务器端没有正确读取隧道数据。例如,如果服务器在某一点丢失了一些字节,那么所有后续读取都将位于错误的帧偏移。如果要对隧道数据进行帧处理,则应在帧中添加识别标记,以便在尝试解码之前知道服务器是否接收到有效帧,并在标记不同步时进行恢复

编辑:在客户端线程中尝试以下操作:

var
  CS: TCriticalSection;

procedure ss_thread.Execute; 
var
  ss : TIdTCPClient; 
  data : TIdBytes; 
  packet, s : string; 
begin 
  // connecting to website to get a data 
  ss := TIdTCPClient.Create(nil); 
  try
    ss.Host := '127.0.0.1'; 
    ss.Port := 80; 
    ss.ReadTimeout := 120000;
    ss.Connect; 

    // writing a data from server to connected web site server 
    ss.IOHandler.Write(Fff_data); 

    // getting data from web site server
    while not Terminated do
    begin
      SetLength(data, 0); 
      ss.IOHandler.ReadBytes(data, -1, False);
      s := TIdEncoderMIME.EncodeBytes(data); 
      packet := make_my_length(s) + s; 
      // sending data from web site, to server 
      CS.Enter; 
      try
        tunnel.IOHandler.Write(packet); 
      finally
        CS.Leave; 
      end;
    end; 
  finally
    ss.Free;
  end;
end; 

initialization
  CS := TCriticalSection.Create;
finalization
  CS.Free; 

我添加了代码,看。如果它在自己的线程中运行,为什么它必须是线程安全的?@waza123:他有多个线程同时向一个套接字连接写入数据。这就是为什么他有一个关于写作的关键部分。@MarcusAdams:
make_my_length()
不需要是这个类的成员。它不访问任何其他类成员。它接受一个输入字符串,并且只对该值进行操作。这是线程安全的。是的,马库斯·亚当斯建议不起作用。我添加了代码,看。如果它在自己的线程中运行,为什么它必须是线程安全的?@waza123:他有多个线程同时向一个套接字连接写入。这就是为什么他有一个关于写作的关键部分。@MarcusAdams:
make_my_length()
不需要是这个类的成员。它不访问任何其他类成员。它接受一个输入字符串,并且只对该值进行操作。这是线程安全的。是的,Marcus Adams建议不起作用。*“您是否在与CheckForData_from_ss()方法相同的单元中创建CS?”是。*“您是否验证了每个线程确实正在访问相同的CS而不是不同的CS?”是。*“CheckForData_from_ss()实际从何处调用?”来自过程ss_thread.Execute;*“如果服务器在某一点上丢失了一些字节,那么所有后续读取都将在错误的帧偏移处。”这是怎么发生的?因为
CheckForData\u from\u ss()
是在线程中调用的,所以您必须确保其代码是完全线程安全的,以避免任何不必要的副作用。您显示的不是线程安全的。除此之外,我建议您使用数据包嗅探器,如Wireshark,或将Indy自己的
TIdLog…
组件连接到
Ftunnel
,以便您可以看到正在传输的实际数据。这将告诉你,你的客户端应用程序是否正在向隧道发送坏数据。如果不是,那么服务器端没有正确读取隧道数据