簡體   English   中英

重新連接客戶端后,TIdTCPServer無法發送數據

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

我在我的應用程序中使用TIdTCPServer。 在這里,硬件充當tcp客戶端。 客戶端使用SYN命令建立連接(在“工具鯊魚”中可以看到)。 我的應用程序有多個客戶端,因此每個客戶端都連接到我的服務器。 對於第一次連接,數據發送和接收都很好。 但是,當發生硬件電源開和關時,我的服務器無法將數據發送到硬件,直到應用程序重新啟動。 以下是對此的觀察:

1,當客戶端第一次連接到SYN = 0的SYN時,從服務器向客戶端發送Seq = 1的SYN ACK

2.數據收發正常

3.硬件斷電發生

4.在命令提示符下,通過使用“ netstat”,我觀察到已建立斷開的IP和端口號的連接。

5.我發送一些數據(在鯊魚中它顯示了6次重發)

6.此后在“命令提示符”中對應的連接建立的數據沒有出現

7.我現在將數據發送到客戶端,由IdTCPServer引發“連接已關閉”異常(此異常發生后,除了,我使用代碼中的connection.disconnect關閉了連接,並從IdTCPServer的鎖定列表中刪除了該特定客戶端。)

8,硬件上電並發送SYN且Seq No = 0

9.在線鯊魚SYN ACK中將Seq No之類的45678452發送給硬件

10.之后,在命令提示符下觀察到連接建立

11.我嘗試將數據發送到客戶端,但是“ Locklist”沒有再次使用客戶端IP和端口進行更新,因此數據沒有發送給客戶端(我的代碼就像如果IP在“ Locklist”中不存在,那么就不發送數據)。 有什么解決辦法嗎?

以下是我的代碼:

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;

您多次調用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事件之外向客戶端發送數據。 您有破壞通訊的風險。 為每個客戶端上下文提供其自己的線程安全的傳出數據隊列,然后在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;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM