简体   繁体   English

重新连接客户端后,TIdTCPServer无法发送数据

[英]TIdTCPServer not able to send data after re-connection of client

I am using TIdTCPServer in my application. 我在我的应用程序中使用TIdTCPServer。 Here hardware acting as tcp client. 在这里,硬件充当tcp客户端。 Client establishes the connection with SYN command (observed in wire shark which is a tool). 客户端使用SYN命令建立连接(在“工具鲨鱼”中可以看到)。 My application has multiple clients so every client connects to my server. 我的应用程序有多个客户端,因此每个客户端都连接到我的服务器。 For the first time connection, data sending & receiving is fine. 对于第一次连接,数据发送和接收都很好。 But when hardware power off & on happens, my server not able to send data, to hardware, until application restart. 但是,当发生硬件电源开和关时,我的服务器无法将数据发送到硬件,直到应用程序重新启动。 Following are the observations regarding this: 以下是对此的观察:

1.When first time client connects SYN with Seq No = 0 received, SYN ACK with Seq No = 1 send to the client from server 1,当客户端第一次连接到SYN = 0的SYN时,从服务器向客户端发送Seq = 1的SYN ACK

2.Data sending & receiving are working fine 2.数据收发正常

3.Hardware power off happened 3.硬件断电发生

4.In command prompt by using “netstat” i observed there is connection established for the disconnected IP & port number. 4.在命令提示符下,通过使用“ netstat”,我观察到已建立断开的IP和端口号的连接。

5.I send some data (in wire shark it displayed 6 times retransmission) 5.我发送一些数据(在鲨鱼中它显示了6次重发)

6.After this in “command prompt” corresponded connection established data not appeared 6.此后在“命令提示符”中对应的连接建立的数据没有出现

7.I send data to client now "Connection closed" exception raised by the IdTCPServer (after this exception, in on except, i closed the connection by using connection.disconnect in code & deleted that particular client from Locklist of IdTCPServer.) 7.我现在将数据发送到客户端,由IdTCPServer引发“连接已关闭”异常(此异常发生后,除了,我使用代码中的connection.disconnect关闭了连接,并从IdTCPServer的锁定列表中删除了该特定客户端。)

8.Hardware powered on & send SYN with Seq No = 0 8,硬件上电并发送SYN且Seq No = 0

9.In wire shark SYN ACK with Seq No like 45678452 sent to hardware 9.在线鲨鱼SYN ACK中将Seq No之类的45678452发送给硬件

10.After that in command prompt connection establishment was observed 10.之后,在命令提示符下观察到连接建立

11.I tried to send data to client, but “Locklist” not updated with the client IP& port again so data not sent the client (my code is like if IP not present in “Locklist” then not sending data). 11.我尝试将数据发送到客户端,但是“ Locklist”没有再次使用客户端IP和端口进行更新,因此数据没有发送给客户端(我的代码就像如果IP在“ Locklist”中不存在,那么就不发送数据)。 Is there any solutions? 有什么解决办法吗?

Following is my code: 以下是我的代码:

try
for Count := 0 to frmtcpserver.IdTCPServer1.Contexts.LockList.Count - 1
do
  begin
  if TIdContext(frmtcpserver.IdTCPServer1.Contexts.LockList.Items[Count]).Binding.PeerIP = Destination_IP then
  begin
    DestinationIPIdx := Count;
  end;
end;
frmtcpserver.IdTCPServer1.Contexts.UnlockList;
if DestinationIPIdx > -1 then
begin
  // sending data here
  TIdContext(frmtcpserver.IdTCPServer1.Contexts.LockList.Items[DestinationIPIdx])
          .Connection.IOHandler.Write(TempBuf, NoofBytesToSend,0);
end;
end;
on E: EidException do
begin
  TIdContext(frmtcpserver.IdTCPServer1.Contexts.LockList.Items[DestinationIPIdx]).Connection.Disconnect;
  frmtcpserver.IdTCPServer1.Contexts.LockList.Delete(DestinationIPIdx);
end;

You are calling Contexts.LockList() WAY too many times. 您多次调用Contexts.LockList() The contexts list is protected by a critical section. 上下文列表受关键部分保护。 The calls to LockList() and UnlockList() MUST be balanced or you will deadlock the server, preventing clients from connecting and disconnecting. LockList()UnlockList()的调用必须保持平衡,否则您将使服务器死锁,从而阻止客户端连接和断开连接。

LockList() returns the actual list. LockList()返回实际列表。 So, you should lock it once , access its items as needed, and then unlock it once . 因此,您应该将其锁定一次 ,根据需要访问其项目,然后将其解锁一次

Try something more like this instead: 尝试类似这样的方法:

list := frmtcpserver.IdTCPServer1.Contexts.LockList;
try
  for i := 0 to list.Count - 1 do
  begin
    ctx := TIdContext(list[i]);
    if ctx.Binding.PeerIP = Destination_IP then
    begin
      // sending data here
      try
        ctx.Connection.IOHandler.Write(TempBuf, NoofBytesToSend, 0);
      except
        on E: EIdException do
        begin
          ctx.Connection.Disconnect;
        end;
      end;
      break;
    end;
  end;
finally
  frmtcpserver.IdTCPServer1.Contexts.UnlockList;
end;

That being said, if the server's OnExecute event is communicating back and forth with the client then it is generally not safe to directly send data to a client from outside of the client's OnExecute event, like you are doing. 话虽如此,如果服务器的OnExecute事件正在与客户端进行来回通信,那么像您所做的OnExecute ,通常通常不安全地直接从客户端的OnExecute事件之外向客户端发送数据。 You risk corrupting the communications. 您有破坏通讯的风险。 It is safer to give each client context its own thread-safe queue of outgoing data, and then use the OnExecute event to send that data when it is safe to do so. 为每个客户端上下文提供其自己的线程安全的传出数据队列,然后在OnExecute下使用OnExecute事件发送该数据,这是更安全的方法。 For example: 例如:

type
  TMyContext = class(TIdServerContext)
  public
    Queue: TThreadList;
    ...
    constructor Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TThreadList = nil); override;
    destructor Destroy; override;
  end;

  PIdBytes := ^TIdBytes;

constructor TMyContext.Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TThreadList = nil);
begin
  inherited;
  Queue := TThreadList.Create;
end;

destructor TMyContext.Destroy;
var
  list: TList;
  I: integer;
begin
  list := Queue.LockList;
  try
    for i := 0 to list.Count-1 do
    begin
      PIdBytes(list[i])^ := nil;
      Dispose(list[i]);
    end;
  finally
    Queue.UnlockList;
  end;
  Queue.Free;
  inherited;
end;

procedure TFrmTcpServer.FormCreate(Sender: TObject);
begin
  IdTCPServer1.ContextClass := TMyContext;
end;

procedure TFrmTcpServer.IdTCPServer1Execute(AContext: TIdContext);
var
  Queue: TList;
  tmpList: TList;
  i: integer;
begin
  ...
  tmpList := nil;
  try
    Queue := TMyContext(AContext).Queue.LockList;
    try
      if Queue.Count > 0 then
      begin
        tmpList := TList.Create;
        tmpList.Assign(Queue);
        Queue.Clear;
      end;
    finally
      TMyContext(AContext).Queue.UnlockList;
    end;
    if tmpList <> nil then
    begin
      for i := 0 to tmpList.Count-1 do
      begin
        AContext.Connection.IOHandler.Write(PIdBytes(tmpList[i])^);
      end;
    end;
  finally
    if tmpList <> nil then
    begin
      for i := 0 to tmpList.Count-1 do
      begin
        PIdBytes(tmpList[i])^ := nil;
        Dispose(tmpList[i]);
      end;
    end;
    tmpList.Free;
  end;
  ...
end;

var
  list: TList;
  ctx: TIdContext;
  I: integer;
  data: PIdBytes;
begin
  list := IdTCPServer1.Contexts.LockList;
  try
    for i := 0 to list.Count - 1 do
    begin
      ctx := TIdContext(list[i]);
      if ctx.Binding.PeerIP = Destination_IP then
      begin
        New(data);
        try
          data^ := Copy(TempBuf, 0, NoofBytesToSend);
          TMyContext(ctx).Queue.Add(data);
        except
          data^ := nil;
          Dispose(data);
        end;
        break;
      end;
    end;
  end;
finally
  IdTCPServer1.Contexts.UnlockList;
end;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM