Search code examples
delphiwinsockdelphi-10-seattle

WinSock: 10038 - WSAENOTSOCK An operation was attempted on something that is not a socket


Well i have this code where i'm trying receive data from client but happens that GetLastError() is returning:

10038 - WSAENOTSOCK An operation was attempted on something that is not a socket.

I suspect that this trouble is related with the casting Pointer to TSocket, already that ClientThread() function below receives the socket througth your parameter.

How this can be solved?

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);

Solution

  • You are making several mistakes.

    • The signature of your ClientThread() is wrong. It must be defined like this instead:

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

      Without the stdcall, the P parameter will not be passed in correctly.

    • you are passing a pointer to a local TSocket variable to your threads. In your ClientThread(), you are not dereferencing that pointer to access the original TSocket, which is what is causing your error message.

      But more importantly, you are reusing the same TSocket variable for multiple client threads. All of your threads are pointing to the same physical TSocket. Don't use the @ operator, pass a copy of the TSocket to each client thread. Fortunately, TSocket is just a UINT, its value will fit as-is inside of a pointer.

      And you need to close that TSocket before the thread exits. You are not calling closesocket() if CompareMem() returns true.

    • you are leaking threads, as you never close the THandle returned by CreateThread().

    With that said, try this instead:

    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;