TcpClient 在连接和写入时在线程执行时给出错误
TcpClient giving errors at thread execute at connect and write
我在与 TIdTCPServer
的笔记本电脑连接上遇到问题。问题是,它连接正常,发送命令,但是当它尝试再次发送时,出现套接字错误 10053 或 10004 或 10054。
同样的代码在其他电脑上运行正常,只是在这台笔记本电脑上出现了这个错误。
我在线程中使用连接,代码如下:
type
TThreadCon = class(TThread)
private
TCPClient : TIdTCPClient;
protected
procedure Execute; override;
public
constructor Create;
destructor Destroy; override;
end;
procedure DJWRZORLBS(millisecs: Integer);
var
tick : dword;
AnEvent : THandle;
begin
AnEvent := CreateEvent(nil, False, False, nil);
try
tick := GetTickCount + dword(millisecs);
while (millisecs > 0) and (MsgWaitForMultipleObjects(1, AnEvent, False, millisecs, QS_ALLINPUT) <> WAIT_TIMEOUT) do begin
Application.ProcessMessages;
if Application.Terminated then Exit;
millisecs := tick - GetTickcount;
end;
finally
CloseHandle(AnEvent);
end;
end;
constructor TThreadCon.Create;
begin
inherited Create(True);
TCPClient := TIdTCPClient.Create(Nil);
TCPClient.ReadTimeout := 3*60000;
TCPClient.ConnectTimeout := 3*60000;
TCPClient.Port := StrToInt(PortaPS);
TCPClient.Host := Host;
TCPClient.IPVersion := Id_IPv4;
TCPClient.UseNagle := True;
TCPClient.ReuseSocket := rsOSDependent;
end;
procedure TThreadCon.Execute;
begin
while True do
begin
//Sleep(2500);
try
if not TCPClient.Connected then
begin
TCPClient.Connect;
if TCPClient.Connected then
begin
Attempts:= 0;
WriteLn(Format('[%s] Connected to server. [%d]', [TimeToStr(Now), Attempts]));
TCPClient.IOHandler.WriteLn('connect');
if rt = nil then rt := TReadingThread.Create(TCPClient);
end;
end
else
begin
LastPing:= GetTickCount;
try
TCPClient.IOHandler.WriteLn('Ping');
except
on E: Exception do
begin
WriteLn(Format('[%s] Error while trying send ping: %s', [TimeToStr(Now), E.Message]));
end;
end;
WriteLn(Format('[%s] Ping send, Last Ping [%d]', [TimeToStr(Now), GetTickCount-LastPing]));
end;
except
on E: Exception do
begin
Inc(Attempts);
TCPClient.Disconnect(False);
if TCPClient.IOHandler <> nil then TCPClient.IOHandler.InputBuffer.Clear;
WriteLn(Format('[%s] Failed to connect, error: %s [%d]', [TimeToStr(Now), E.Message, Attempts]));
end;
end;
DJWRZORLBS(5000);
end;
end;
下面是发生问题的控制台日志。它连接到服务器,然后当线程 运行 再次出现时,它应该发送 Ping
的地方开始出现问题,并且由于某些原因在某些情况下它总是显示为连接在每个线程 运行,比如 TCPClient.Connected
未连接。
这是正常运行的计算机上的正常日志:
[21:44:59] Connected to server. [0]
[21:45:04] Ping send, Last Ping [0]
[21:45:09] Ping send, Last Ping [0]
如果我关闭服务器,等待几秒钟然后重新打开,它显示如下:
[21:45:54] Failed to connect, error: Socket Error # 10054
Connection reset by peer. [1]
[21:46:01] Failed to connect, error: Socket Error # 10061
Connection refused. [2]
[21:46:08] Failed to connect, error: Socket Error # 10061
Connection refused. [3]
[21:46:14] Connected to server. [0]
[21:46:19] Ping send, Last Ping [0]
对我来说,这就是它应该如何正常工作。
这是什么原因造成的?服务器有问题?但是如果是在服务器上,为什么其他机器可以正常运行?
一些网络设置?如果是,我该如何解决?
在内部,Connected
执行读取操作,这在您的情况下不是一件好事,因为如果 Connect()
成功,您有另一个线程同时从同一个套接字读取。这两个线程将争夺对套接字的访问权限并将数据放入其 IOHandler.InputBuffer
.
无论如何,Connected
returns True
如果InputBuffer
中有任何未读数据,即使底层套接字失败。
您的 TThreadCon
结构不是很好。我建议重组它以完全消除使用 Connected
的需要(和 DJWRZORLBS()
,因为 TThreadCon
没有需要抽取的消息队列)。更好的设计是让线程循环连接直到成功,然后循环发送 ping,然后断开连接,并根据需要重复。
试试像这样的东西:
type
TThreadCon = class(TThread)
private
FTermEvent: TEvent;
protected
procedure Execute; override;
procedure DoTerminate; override;
procedure TerminatedSet; override;
public
constructor Create; reintroduce;
destructor Destroy; override;
end;
constructor TThreadCon.Create;
begin
inherited Create(True);
FTermEvent := TEvent.Create;
end;
destructor TThreadCon.Destroy;
begin
FTermEvent.Free;
inherited;
end;
procedure TThreadCon.TerminatedSet;
begin
FTermEvent.SetEvent;
end;
procedure TThreadCon.Execute;
var
TCPClient: TIdTCPClient;
rt: TReadingThread;
Attempts: Integer;
begin
TCPClient := TIdTCPClient.Create(nil);
try
TCPClient.ReadTimeout := 3*60000;
TCPClient.ConnectTimeout := 3*60000;
TCPClient.Port := StrToInt(PortaPS);
TCPClient.Host := Host;
TCPClient.IPVersion := Id_IPv4;
TCPClient.UseNagle := True;
TCPClient.ReuseSocket := rsOSDependent;
Attempts := 0;
while not Terminated do
begin
if TCPClient.IOHandler <> nil then
TCPClient.IOHandler.InputBuffer.Clear;
try
TCPClient.Connect;
try
TCPClient.IOHandler.WriteLn('connect');
except
TCPClient.Disonnect(False);
raise;
end;
except
on E: Exception do
begin
Inc(Attempts);
WriteLn(Format('[%s] Failed to connect, error: %s [%d]', [TimeToStr(Now), E.Message, Attempts]));
if FTermEvent.WaitFor(2500) <> wrTimeout then Exit;
Continue;
end;
end;
Attempts := 0;
WriteLn(Format('[%s] Connected to server.', [TimeToStr(Now)]));
rt := TReadingThread.Create(TCPClient);
try
try
while not Terminated do
begin
LastPing := GetTickCount;
TCPClient.IOHandler.WriteLn('Ping');
WriteLn(Format('[%s] Ping send, Last Ping [%d]', [TimeToStr(Now), GetTickCount-LastPing]));
if FTermEvent.WaitFor(5000) <> wrTimeout then Exit;
end;
except
on E: Exception do
begin
WriteLn(Format('[%s] Error while trying to send ping: %s', [TimeToStr(Now), E.Message]));
end;
end;
finally
rt.Terminate;
try
TCPClient.Disconnect(False);
finally
rt.WaitFor;
rt.Free;
end;
end;
end;
finally
TCPClient.Free;
end;
end;
procedure TThreadCon.DoTerminate;
begin
if FatalException <> nil then
WriteLn(Format('[%s] Fatal Error: %s', [TimeToStr(Now), Exception(E).Message]));
inherited;
end;
我在与 TIdTCPServer
的笔记本电脑连接上遇到问题。问题是,它连接正常,发送命令,但是当它尝试再次发送时,出现套接字错误 10053 或 10004 或 10054。
同样的代码在其他电脑上运行正常,只是在这台笔记本电脑上出现了这个错误。
我在线程中使用连接,代码如下:
type
TThreadCon = class(TThread)
private
TCPClient : TIdTCPClient;
protected
procedure Execute; override;
public
constructor Create;
destructor Destroy; override;
end;
procedure DJWRZORLBS(millisecs: Integer);
var
tick : dword;
AnEvent : THandle;
begin
AnEvent := CreateEvent(nil, False, False, nil);
try
tick := GetTickCount + dword(millisecs);
while (millisecs > 0) and (MsgWaitForMultipleObjects(1, AnEvent, False, millisecs, QS_ALLINPUT) <> WAIT_TIMEOUT) do begin
Application.ProcessMessages;
if Application.Terminated then Exit;
millisecs := tick - GetTickcount;
end;
finally
CloseHandle(AnEvent);
end;
end;
constructor TThreadCon.Create;
begin
inherited Create(True);
TCPClient := TIdTCPClient.Create(Nil);
TCPClient.ReadTimeout := 3*60000;
TCPClient.ConnectTimeout := 3*60000;
TCPClient.Port := StrToInt(PortaPS);
TCPClient.Host := Host;
TCPClient.IPVersion := Id_IPv4;
TCPClient.UseNagle := True;
TCPClient.ReuseSocket := rsOSDependent;
end;
procedure TThreadCon.Execute;
begin
while True do
begin
//Sleep(2500);
try
if not TCPClient.Connected then
begin
TCPClient.Connect;
if TCPClient.Connected then
begin
Attempts:= 0;
WriteLn(Format('[%s] Connected to server. [%d]', [TimeToStr(Now), Attempts]));
TCPClient.IOHandler.WriteLn('connect');
if rt = nil then rt := TReadingThread.Create(TCPClient);
end;
end
else
begin
LastPing:= GetTickCount;
try
TCPClient.IOHandler.WriteLn('Ping');
except
on E: Exception do
begin
WriteLn(Format('[%s] Error while trying send ping: %s', [TimeToStr(Now), E.Message]));
end;
end;
WriteLn(Format('[%s] Ping send, Last Ping [%d]', [TimeToStr(Now), GetTickCount-LastPing]));
end;
except
on E: Exception do
begin
Inc(Attempts);
TCPClient.Disconnect(False);
if TCPClient.IOHandler <> nil then TCPClient.IOHandler.InputBuffer.Clear;
WriteLn(Format('[%s] Failed to connect, error: %s [%d]', [TimeToStr(Now), E.Message, Attempts]));
end;
end;
DJWRZORLBS(5000);
end;
end;
下面是发生问题的控制台日志。它连接到服务器,然后当线程 运行 再次出现时,它应该发送 Ping
的地方开始出现问题,并且由于某些原因在某些情况下它总是显示为连接在每个线程 运行,比如 TCPClient.Connected
未连接。
这是正常运行的计算机上的正常日志:
[21:44:59] Connected to server. [0]
[21:45:04] Ping send, Last Ping [0]
[21:45:09] Ping send, Last Ping [0]
如果我关闭服务器,等待几秒钟然后重新打开,它显示如下:
[21:45:54] Failed to connect, error: Socket Error # 10054
Connection reset by peer. [1]
[21:46:01] Failed to connect, error: Socket Error # 10061
Connection refused. [2]
[21:46:08] Failed to connect, error: Socket Error # 10061
Connection refused. [3]
[21:46:14] Connected to server. [0]
[21:46:19] Ping send, Last Ping [0]
对我来说,这就是它应该如何正常工作。
这是什么原因造成的?服务器有问题?但是如果是在服务器上,为什么其他机器可以正常运行?
一些网络设置?如果是,我该如何解决?
在内部,Connected
执行读取操作,这在您的情况下不是一件好事,因为如果 Connect()
成功,您有另一个线程同时从同一个套接字读取。这两个线程将争夺对套接字的访问权限并将数据放入其 IOHandler.InputBuffer
.
无论如何,Connected
returns True
如果InputBuffer
中有任何未读数据,即使底层套接字失败。
您的 TThreadCon
结构不是很好。我建议重组它以完全消除使用 Connected
的需要(和 DJWRZORLBS()
,因为 TThreadCon
没有需要抽取的消息队列)。更好的设计是让线程循环连接直到成功,然后循环发送 ping,然后断开连接,并根据需要重复。
试试像这样的东西:
type
TThreadCon = class(TThread)
private
FTermEvent: TEvent;
protected
procedure Execute; override;
procedure DoTerminate; override;
procedure TerminatedSet; override;
public
constructor Create; reintroduce;
destructor Destroy; override;
end;
constructor TThreadCon.Create;
begin
inherited Create(True);
FTermEvent := TEvent.Create;
end;
destructor TThreadCon.Destroy;
begin
FTermEvent.Free;
inherited;
end;
procedure TThreadCon.TerminatedSet;
begin
FTermEvent.SetEvent;
end;
procedure TThreadCon.Execute;
var
TCPClient: TIdTCPClient;
rt: TReadingThread;
Attempts: Integer;
begin
TCPClient := TIdTCPClient.Create(nil);
try
TCPClient.ReadTimeout := 3*60000;
TCPClient.ConnectTimeout := 3*60000;
TCPClient.Port := StrToInt(PortaPS);
TCPClient.Host := Host;
TCPClient.IPVersion := Id_IPv4;
TCPClient.UseNagle := True;
TCPClient.ReuseSocket := rsOSDependent;
Attempts := 0;
while not Terminated do
begin
if TCPClient.IOHandler <> nil then
TCPClient.IOHandler.InputBuffer.Clear;
try
TCPClient.Connect;
try
TCPClient.IOHandler.WriteLn('connect');
except
TCPClient.Disonnect(False);
raise;
end;
except
on E: Exception do
begin
Inc(Attempts);
WriteLn(Format('[%s] Failed to connect, error: %s [%d]', [TimeToStr(Now), E.Message, Attempts]));
if FTermEvent.WaitFor(2500) <> wrTimeout then Exit;
Continue;
end;
end;
Attempts := 0;
WriteLn(Format('[%s] Connected to server.', [TimeToStr(Now)]));
rt := TReadingThread.Create(TCPClient);
try
try
while not Terminated do
begin
LastPing := GetTickCount;
TCPClient.IOHandler.WriteLn('Ping');
WriteLn(Format('[%s] Ping send, Last Ping [%d]', [TimeToStr(Now), GetTickCount-LastPing]));
if FTermEvent.WaitFor(5000) <> wrTimeout then Exit;
end;
except
on E: Exception do
begin
WriteLn(Format('[%s] Error while trying to send ping: %s', [TimeToStr(Now), E.Message]));
end;
end;
finally
rt.Terminate;
try
TCPClient.Disconnect(False);
finally
rt.WaitFor;
rt.Free;
end;
end;
end;
finally
TCPClient.Free;
end;
end;
procedure TThreadCon.DoTerminate;
begin
if FatalException <> nil then
WriteLn(Format('[%s] Fatal Error: %s', [TimeToStr(Now), Exception(E).Message]));
inherited;
end;