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

时间:2017-07-28 17:54:19

标签: delphi tcp

我在我的应用程序中使用TIdTCPServer。这里硬件充当tcp客户端。客户端使用SYN命令建立连接(在wire shark中观察,这是一种工具)。我的应用程序有多个客户端,所以每个客户端连接到我的服第一次连接,数据发送&收到罚款。但是当硬件断电时发生时,我的服务器无法将数据发送到硬件,直到应用程序重新启动。以下是对此的观察:

1.当第一次客户端连接SYN并收到Seq No = 0时,带有Seq No = 1的SYN ACK从服务器发送到客户端

2.数据发送&收到工作正常

3.发生硬件电源关闭

4.在命令提示符下使用“netstat”我观察到已建立连接断开的IP&端口号。

5.我发送一些数据(在线鲨中显示6次重传)

6.此后在“命令提示符”对应连接建立的数据未出现

7.我现在向客户发送数据"连接已关闭" IdTCPServer引发的异常(在此异常之后,在on中,我在代码中使用connection.disconnect关闭了连接,并从IdTCPServer的Locklist中删除了该特定客户端。)

8.硬件开机&使用Seq No = 0发送SYN

9.在线鲨SYN ACK中,Seq No,如45678452发送到硬件

10.之后在命令提示符中建立了连接建立

11.我尝试向客户端发送数据,但“Locklist”未使用客户端IP&更新再次端口所以数据没有发送到客户端(我的代码就像是“如果IP不存在于”锁定列表“然后不发送数据)。   有没有解决方案?

以下是我的代码:

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;

1 个答案:

答案 0 :(得分:1)

您正在多次致电Contexts.LockList()。上下文列表受临界区保护。对LockList()UnlockList()的调用必须保持平衡,否则您将使服务器死锁,从而阻止客户端连接和断开连接。

LockList()返回实际列表。因此,您应该将其锁定一次,根据需要访问其项目,然后将其解锁

尝试更像这样的东西:

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;

话虽如此,如果服务器的OnExecute事件与客户端来回通信,那么从客户端外部直接向客户端发送数据通常是不安全的。 OnExecute事件,就像你在做的那样。您冒着破坏通信的风险。为每个客户端上下文提供其自己的传出数据的线程安全队列更安全,然后使用OnExecute事件在安全的情况下发送该数据。例如:

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;
相关问题