I'm having a problem on a laptop connection to TIdTCPServer
. The thing is, it connects fine, sends a command, but when it tries to send it again, it gets socket error 10053 or 10004 or 10054.
The same code on other computers works fine, just on this one laptop this error happens.
I'm using a connection in a thread, here is the code:
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;
Below is the Console log where the problem happens. It connects to the server, then when the thread runs again, where it should send Ping
starts the problems, and for some reason in some cases it always shows as connected at every thread run, like TCPClient.Connected
isn't connected.
This is a normal log on a computer that works fine:
[21:44:59] Connected to server. [0]
[21:45:04] Ping send, Last Ping [0]
[21:45:09] Ping send, Last Ping [0]
if I close the server, wait some seconds and reopen, it shows as this:
[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]
For me, it's how it should work correctly.
What can cause this? Some problem on the server? But if it is at the server, why do other machines work fine?
Some network setting? If yes, what can I do to solve it?
CodePudding user response:
Internally, Connected
performs a reading operation, which is not a good thing in your case since you have another thread that reads from the same socket at the same time if Connect()
is successful. The two threads will fight over access to the socket and putting data into its IOHandler.InputBuffer
.
In any case, Connected
returns True
if there is any unread data in the InputBuffer
, even if the underlying socket fails.
Your TThreadCon
is not structured very well. I would suggest restructuring it to eliminate the need for using Connected
at all (and DJWRZORLBS()
, since TThreadCon
does not have a message queue that needs to be pumped). A better design would be to have the thread connect in a loop until successful, then send pings in a loop, then disconnect, and repeat as needed.
Try something more like this:
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;