WinSock: 10038 - WSAENOTSOCK Была предпринята попытка выполнить операцию на чем-то, что не является сокетом - PullRequest
1 голос
/ 24 апреля 2019

Хорошо, у меня есть этот код, где я пытаюсь получить данные от клиента, но бывает, что GetLastError() возвращает:

10038 - WSAENOTSOCK Была предпринята операция с чем-то, что не является сокетом.

Я подозреваю, что эта проблема связана с приведением Pointer к TSocket, уже то, что функция ClientThread() ниже получает сокет через ваш параметр.

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

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

function ClientThread(P: Pointer): Integer;
var
  Buf: array [0 .. SizeOf(Buffer) - 1] of AnsiChar;
  Sock: TSocket;
begin
  Result := 0;
  Writeln('New thread started.' + #13#10);

  Sock := TSocket(P);

  if recv(Sock, Buf, SizeOf(Buffer), 0) <= 0 then //My trouble is here.
  begin
    Writeln(GetLastError);
    closesocket(Sock);
    Result := 0;
    Exit;
  end;

  if not CompareMem(@Buf, @Buffer, SizeOf(Buffer)) then
  begin
    closesocket(Sock);
    Result := 0;
    Exit;
  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#10, [ntohs(_addrIn.sin_port)]));

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

  Result := True;
end;

Usage:

StartServer(1234);

1 Ответ

4 голосов
/ 24 апреля 2019

Вы делаете несколько ошибок.

  • Подпись вашего ClientThread() неверна. Это должно быть определено следующим образом:

    function ClientThread(P: Pointer): DWORD; stdcall;
    

    Без stdcall параметр P не будет правильно передан.

  • вы передаете указатель локальной переменной TSocket в ваши потоки. В вашем ClientThread() вы не разыменовываете этот указатель для доступа к исходному TSocket, что и вызывает ваше сообщение об ошибке.

    Но что более важно, вы повторно используете одну и ту же переменную TSocket для нескольких клиентских потоков. Все ваши темы указывают на один и тот же физический TSocket. Не используйте оператор @, передайте копию TSocket каждому клиентскому потоку. К счастью, TSocket это просто UINT, его значение будет соответствовать как есть внутри указателя.

    И вам нужно закрыть это TSocket до завершения потока. Вы не звоните closesocket(), если CompareMem() вернет истину.

  • вы пропускаете потоки, так как вы никогда не закрываете THandle, возвращаемое CreateThread().

Сказав это, попробуйте вместо этого:

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

function ClientThread(P: Pointer): DWORD; stdcall;
var
  Buf: array [0 .. SizeOf(Buffer) - 1] of AnsiChar;
  Sock: TSocket;
  Ret, NumRead: integer;
begin
  Result := 0;
  Sock := TSocket(P);
  try
    WriteLn('New thread started.');
    NumRead := 0;
    repeat
      Ret := recv(Sock, @Buf[NumRead], SizeOf(Buffer)-NumRead, 0);
      if Ret <= 0 then
      begin
        if Ret = SOCKET_ERROR then
        begin
          Ret := WSAGetLastError;
          Writeln('recv error: ', Ret);
        end;
        Exit;
      end;
      Inc(NumRead, Ret);
    until NumRead = Sizeof(Buffer);

    if not CompareMem(@Buf, @Buffer, SizeOf(Buffer)) then
    begin
      WriteLn('Buf does not match Buffer');
      Exit;
    end;

    WriteLn('Buf matches Buffer');
  finally
    closesocket(Sock);
  end;
end;

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

  Ret := WSAStartup(MakeWord(2, 2), _wsdata);
  if Ret <> 0 then
  begin
    WriteLn('WSAStartup error: ', Ret);
    Exit;
  end;

  try
    serverSocket := socket(AF_INET, SOCK_STREAM, 0);
    if serverSocket = INVALID_SOCKET then
    begin
      Ret := WSAGetLastError;
      WriteLn('socket error: ', Ret);
      Exit;
    end;

    try
      _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
      begin
        Ret := WSAGetLastError;
        WriteLn('bind error: ', Ret);
        Exit;
      end;

      if listen(serverSocket, SOMAXCONN) = SOCKET_ERROR then
      begin
        Ret := WSAGetLastError;
        WriteLn('listen error: ', Ret);
        Exit;
      end;

      addrSize := SizeOf(_addrIn);
      getsockname(serverSocket, _addrIn, addrSize);
      WriteLn('Listening on port ', ntohs(_addrIn.sin_port));

      while True do
      begin
        addrSize := SizeOf(_addr);
        S := accept(serverSocket, @_addr, @addrSize);
        if S <> INVALID_SOCKET then
        begin
          WriteLn('Client connected.');
          h := CreateThread(nil, 0, @ClientThread, Pointer(S), 0, tid);
          if h = 0 then
          begin
            Ret := GetLastError;
            closesocket(S);
            WriteLn('CreateThread error: ', Ret);
          end;
        end;
      end;
    finally
      closesocket(serverSocket);
    end;
  finally
    WSACleanup;
  end;

  Result := True;
end;
...