0
votes

I am creating a socket connection, and using a thread to continuously send data through the socket using the following code:

    if (FSocketSession.Connected) and (Msg.JSONEvent <> '') then
    begin
      FSocketSession.Send(TEncoding.UTF8.GetBytes(Msg.JSONEvent))
      Msg.Free;
    end
    else
    begin
      FSocketSession.Connect;
    end;

This is the FSocketSession.Send call:

function TSocketSession.Send(const ADataToSend: TBytes): Integer;
var
  Stopwatch: TStopwatch;
  SentBytes: Integer;
const
  SleepTimeMS = 10;
begin
  SiMain.TrackMethod(Self, 'Send');
  {$IFDEF USE_SSL}
  if FIsSSL then
  begin
    SiMain.LogInteger('SslState', Ord(FSocket.SslState));
  end;
  {$ENDIF}
  FDataSent := False;
  if FSocket.State = wsConnected then
  begin
    SentBytes := FSocket.Send(@(ADataToSend[Low(ADataToSend)]), Length(ADataToSend));
    SiMain.LogInteger('SentBytes', SentBytes);
    Stopwatch := TStopwatch.StartNew;
  end;
  while (FSocket.State = wsConnected) and (not FDataSent) do
  begin
    FSocket.MessagePump;
    Sleep(SleepTimeMS);
    if (Stopwatch.ElapsedMilliseconds >= FTimeout) then
    begin
      FError := CErrorCommTimeout;
      break;
    end;
  end;

  Result := FError;
end;

I am noticing that on certain PCs, the socket simply stops sending data after a certain period of time (usually it happens after about 1 or 2 minutes) and on other PCs it runs without any issues. Does anyone see something in the code that could cause this to happen? I can provide more info if needed.

1

1 Answers

1
votes

I see a couple of issues with your code.

If JSONEvent is blank, you will call Connect() even if Connected is already true, and you would leak the Msg object. The code should look more like this instead:

if FSocketSession.Connected then
begin
  if Msg.JSONEvent <> '' then
    FSocketSession.Send(TEncoding.UTF8.GetBytes(Msg.JSONEvent));
  Msg.Free;
end
else
begin
  FSocketSession.Connect;
end;

Now, inside of TSocketSession.Send(), you should be calling FSocket.Send() in a loop. A TCP socket is not guaranteed to send as many bytes as you request, it could return fewer bytes. That is why it returns how many bytes were actually sent. You need to account for that:

function TSocketSession.Send(const ADataToSend: TBytes): Integer;
const
  SleepTimeMS = 10;
var
  Stopwatch: TStopwatch;
  SentBytes, BytesToSend: Integer;
  PData: PByte;
begin
  SiMain.TrackMethod(Self, 'Send');
  {$IFDEF USE_SSL}
  if FIsSSL then
  begin
    SiMain.LogInteger('SslState', Ord(FSocket.SslState));
  end;
  {$ENDIF}
  FDataSent := False;
  if FSocket.State = wsConnected then
  begin
    PData := PByte(ADataToSend);
    BytesToSend := Length(ADataToSend);
    while BytesToSend > 0 do
    begin
      SentBytes := FSocket.Send(PData, BytesToSend);
      SiMain.LogInteger('SentBytes', SentBytes);
      if SentBytes <= 0 then
      begin
        // error handling ...
        Result := ...;
        Exit;
      end;
      Inc(PData, SentBytes);
      Dec(BytesToSend, SentBytes);
    end;
    Stopwatch := TStopwatch.StartNew;
  end;
  while (FSocket.State = wsConnected) and (not FDataSent) do
  begin
    FSocket.MessagePump;
    Sleep(SleepTimeMS);
    if (Stopwatch.ElapsedMilliseconds >= FTimeout) then
    begin
      FError := CErrorCommTimeout;
      break;
    end;
  end;

  Result := FError;
end;