简体   繁体   中英

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

I am using TIdTCPServer in my application. Here hardware acting as tcp client. Client establishes the connection with SYN command (observed in wire shark which is a tool). 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

2.Data sending & receiving are working fine

3.Hardware power off happened

4.In command prompt by using “netstat” i observed there is connection established for the disconnected IP & port number.

5.I send some data (in wire shark it displayed 6 times retransmission)

6.After this in “command prompt” corresponded connection established data not appeared

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.)

8.Hardware powered on & send SYN with Seq No = 0

9.In wire shark SYN ACK with Seq No like 45678452 sent to hardware

10.After that in command prompt connection establishment was observed

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). 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. 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() returns the actual list. 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. 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. 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;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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