Search code examples
socketsdelphiwinsockdelphi-10-seattlerecv

WinSock: Server not receive data on same sequence that Client sent


I need send data from Client to Server in a determinated sequence, where the Server can receive these data also on same sequence sent by Client. On code below exists a problem that a data (that is a byte of control, 1) is received like a data of next data.

Ex:

On Client i have the following piece that send 1 (Connection._Input)

if SendInt(Sock, Ord(Connection._Input)) <= 0 then
      Exit;

This byte sent above, on Server the correct is be received on Check variable, but instead is received on dwC.

See:

enter image description here

How can solve it?

Here is the complete code:

Client:

program _Client;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  Windows,
  WinSock,
  SysUtils;

type
  Connection = (Desktop, _Input);

const
  SendBuf: array [0 .. 9] of AnsiChar = ('A', 'B', 'C', 'D', 'E', 'F', 'G',
    'H', 'I', #0);

function SendInt(S: TSocket; I: Integer): Integer;
begin
  Result := send(S, I, SizeOf(I), 0);
end;

function ConnectServer: TSocket;
var
  Wsa: WSAData;
  Client: sockaddr_in;
  S: TSocket;
  Rslt: Integer;
begin
  S := INVALID_SOCKET;
  try
    Rslt := WSAStartup(MakeWord(2, 2), Wsa);
    if Rslt = NO_ERROR then
    begin
      S := socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
      if S <> INVALID_SOCKET then
      begin
        Client.sin_family := AF_INET;
        Client.sin_addr.s_addr := inet_addr('192.168.15.6');
        Client.sin_port := htons(5099);
        if connect(S, Client, SizeOf(Client)) <> SOCKET_ERROR then
          Writeln('Connected successfully!');
      end;
    end;
  except
    Writeln(SysErrorMessage(WSAGetLastError));
  end;
  Result := S;
end;

function DesktopThread(P: Pointer): DWORD; stdcall;
var
  Sock: TSocket;
  dwC, dwD, dwE, dwF, dwG: DWORD;
  A, B: Integer;
begin
  Result := 0;
  Sock := ConnectServer;

  if send(Sock, SendBuf, SizeOf(SendBuf), 0) <= 0 then
    Exit;

  if SendInt(Sock, Ord(Connection.Desktop)) <= 0 then
    Exit;

  dwC := 111;
  dwD := 222;
  dwE := 333;
  dwF := 444;
  dwG := 555;

  repeat
    if recv(Sock, A, SizeOf(A), 0) <= 0 then
      Exit;

    if recv(Sock, B, SizeOf(B), 0) <= 0 then
      Exit;

    if SendInt(Sock, Ord(Connection._Input)) <= 0 then
      Exit;

    if SendInt(Sock, dwC) <= 0 then
      Exit;

    if SendInt(Sock, dwD) <= 0 then
      Exit;

    if SendInt(Sock, dwE) <= 0 then
      Exit;

    if SendInt(Sock, dwF) <= 0 then
      Exit;

    if SendInt(Sock, dwG) <= 0 then
      Exit;

    // Writeln(Format('%s', [SysErrorMessage(WSAGetLastError)]));

    Writeln(Format('dwC: %d, dwD: %d, dwE: %d, dwF: %d, dwG: %d',
      [dwC, dwD, dwE, dwF, dwG]));

  until True;
end;

var
  ThrId: Cardinal;

begin
  try
    CreateThread(nil, 0, @DesktopThread, nil, 0, ThrId);
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;

  Readln;

end.

Server:

program _Server;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  Windows,
  WinSock,
  SysUtils;

type
  Connection = (Desktop, Input, _End);

const
  Buffer: array [0 .. 9] of AnsiChar = ('A', 'B', 'C', 'D', 'E', 'F', 'G',
    'H', 'I', #0);

function SendInt(S: TSocket; I: Integer): Integer;
begin
  Result := send(S, I, SizeOf(I), 0);
end;

function ClientThread(P: Pointer): DWORD; stdcall;
var
  Buf: array [0 .. SizeOf(Buffer) - 1] of AnsiChar;
  Sock: TSocket;
  Check: BOOL;
  A, B: Integer;
  _connection: Connection;
  dwC, dwD, dwE, dwF, dwG: DWORD;
begin
  Result := 0;
  Sock := TSocket(P);

  if recv(Sock, Buf, SizeOf(Buffer), 0) <= 0 then
  begin
    closesocket(Sock);
    Result := 0;
    Exit;
  end;

  if not CompareMem(@Buf, @Buffer, SizeOf(Buffer)) then
  begin
    closesocket(Sock);
    Result := 0;
    Exit;
  end;

  if recv(Sock, _connection, SizeOf(_connection), 0) <= 0 then
  begin
    closesocket(Sock);
    Result := 0;
    Exit;
  end;

  if _connection = Connection.Desktop then
  begin
    A := 666;
    B := 777;
    repeat

      if SendInt(Sock, A) <= 0 then
        Exit;

      if SendInt(Sock, B) <= 0 then
        Exit;

      if recv(Sock, Check, SizeOf(Check), 0) <= 0 then
        Exit;

      Writeln(BoolToStr(Check));

      { if not Check then
        continue; }

      if recv(Sock, dwC, SizeOf(dwC), 0) <= 0 then
        Exit;

      if recv(Sock, dwD, SizeOf(dwD), 0) <= 0 then
        Exit;

      if recv(Sock, dwE, SizeOf(dwE), 0) <= 0 then
        Exit;

      if recv(Sock, dwF, SizeOf(dwF), 0) <= 0 then
        Exit;

      if recv(Sock, dwG, SizeOf(dwG), 0) <= 0 then
        Exit;

      // Writeln(Format('%s', [SysErrorMessage(WSAGetLastError)]));

      Writeln(Format('dwC: %d, dwD: %d, dwE: %d, dwF: %d, dwG: %d',
        [dwC, dwD, dwE, dwF, dwG]));

    until True;
  end;
end;

function StartServer(Port: Integer): Boolean;
var
  _wsdata: WSAData;
  serverSocket, S: TSocket;
  _addrIn, _addr: sockaddr_in;
  addrSize: Integer;
  tid: Cardinal;
begin
  Result := False;

  if WSAStartup(MakeWord(2, 2), _wsdata) <> 0 then
    Exit;

  serverSocket := socket(AF_INET, SOCK_STREAM, 0);

  if serverSocket = INVALID_SOCKET then
    Exit;

  _addrIn.sin_family := AF_INET;
  _addrIn.sin_addr.S_addr := INADDR_ANY;
  _addrIn.sin_port := htons(Port);

  if bind(serverSocket, _addrIn, SizeOf(_addrIn)) = SOCKET_ERROR then
    Exit;

  if listen(serverSocket, SOMAXCONN) = SOCKET_ERROR then
    Exit;

  addrSize := SizeOf(_addrIn);
  getsockname(serverSocket, _addrIn, addrSize);

  Writeln(Format('Listening on port %d.' + #13, [ntohs(_addrIn.sin_port)]));

  while True do
  begin
    S := accept(serverSocket, @_addr, @addrSize);
    CreateThread(nil, 0, @ClientThread, Pointer(S), 0, tid);
  end;

  Result := True;
end;

begin
  try
    if not StartServer(5099) then
      Writeln(SysErrorMessage(WSAGetLastError));
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;

end.

Solution

  • You have a misalignment of data size in client vs server.

    type
      Connection = (Desktop, _Input);
    

    Default size of an enumeration in Delphi is byte. This is in itself OK, but you handle these differently in the client and the server.

    You send from the client using your SendInt() function which converts to integer.

    On the server side you receive it as a SizeOf(_connection) which is only a byte. Because of the byte order, the 1 remains in the buffer and is later read into dwC.

    You can correct the error either by setting Minimum enum size in project options to doubleword or by sending as byte.


    Edit after comment

    Indeed you also have another error, or maybe misunderstanding.

    From the client you send

    SendInt(Sock, Ord(Connection._Input))
    

    which is received by the server as

    var
      Check: BOOL;
    ....
      recv(Sock, Check, SizeOf(Check), 0) , 
    

    Then you write it out as

    Writeln(BoolToStr(Check)); 
    

    and the console shows '-1'. But that is not an error, it is documented:

    System.SysUtils.BoolToStr

    Value of B  Value of UseBoolStrs  Value of returned string  
      true        false                 '-1' 
    

    Perhaps you want to show it as the enum value instead.