我在我的应用程序中使用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;
答案 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;