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

#delphi #winsock #delphi-10-seattle

#delphi #winsock #delphi-10-Сиэтл

Вопрос:

Ну, у меня есть этот код, в котором я пытаюсь получить данные от клиента, но случается, что 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;
  

Использование:

 StartServer(1234);
  

Комментарии:

1. ThreadProc — это stdcall. — Sock:= TSocket(P); -> Sock:= TSocket(P ^);

2. @SertacAkyuz, stdcall используется только для передачи параметра или stdcall является обязательным в любом случае?

3. @BrowJr stdcall является обязательным в этом случае, потому что это то, что CreateThread() требуется. Есть ли причина, по которой вы используете CreateThread() напрямую, а не TThread нет? Или с использованием Winsock напрямую вместо существующей оболочки, например TServerSocket или TIdTCPServer ?

Ответ №1:

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

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

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

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

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

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

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

  • вы пропускаете потоки, так как вы никогда не закрываете 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;
  

Комментарии:

1. В строке Ret := recv(Sock, @Buf[NumRead], SizeOf(Buffer)-NumRead, 0); я получаю сообщение об ошибке «Постоянный объект не может быть передан в качестве параметра var»

2. @icinemagr в этой строке нет «постоянного объекта», если только вы не передаете Buffer вместо Buf . Кроме того, использование @ ожидает, что 2-й параметр будет указателем (как это определено в реальном API), но если это var вместо этого в вашей системе, тогда удалите @ : Ret := recv(Sock, Buf[NumRead], SizeOf(Buffer)-NumRead, 0);