WinSock: сервер не получает данные в той же последовательности, что и клиент - PullRequest
2 голосов
/ 29 апреля 2019

Мне нужно отправить данные с Клиента на Сервер в определенной последовательности, где Сервер может получать эти данные также по той же последовательности, отправленной Клиентом. В приведенном ниже коде существует проблема, связанная с тем, что данные (то есть байт управления, 1 ) принимаются как данные следующих данных.

Ex:

Вкл. Клиент У меня есть следующий фрагмент, который отправляет 1 (Connection._Input)

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

Этот байт, отправленный выше, на Сервер , правильный получен для переменной Check, но вместо этого получен на dwC.

См:

enter image description here

Как можно решить?

Вот полный код:

Клиент:

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.

Сервер:

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.

1 Ответ

1 голос
/ 29 апреля 2019

У вас есть несоответствие размера данных в клиенте против сервера.

type
  Connection = (Desktop, _Input);

Размер по умолчанию для перечисления в Delphi: byte. Это само по себе нормально, но вы обрабатываете их по-разному на клиенте и на сервере.

Вы отправляете с клиента, используя функцию SendInt(), которая преобразуется в целое число.

На стороне сервера вы получаете его как SizeOf(_connection), который является только байтом. Из-за порядка байтов 1 остается в буфере и позже считывается в dwC.

Вы можете исправить ошибку, установив Minimum enum size в опциях проекта на doubleword или отправив в виде байта.


Редактировать после комментария

Действительно, у вас также есть другая ошибка или, возможно, недоразумение.

От клиента, которого вы отправляете

SendInt(Sock, Ord(Connection._Input))

который получен сервером как

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

Тогда вы пишете это как

Writeln(BoolToStr(Check)); 

и консоль показывает «-1». Но это не ошибка, это задокументировано:

System.SysUtils.BoolToStr

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

Возможно, вы хотите показать это как значение перечисления.

...