WinSock: 10038 - WSAENOTSOCK 尝试对非套接字的对象进行操作

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

好吧,我有这段代码,我正在尝试从客户端接收数据,但碰巧 GetLastError() 正在返回:

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

我怀疑这个问题与将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);

你犯了几个错误。

  • ClientThread()的签名有误。它必须这样定义:

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

    如果没有stdcallP参数将无法正确传入。

  • 您正在将 指针 传递给您的线程的本地 TSocket 变量。在您的 ClientThread() 中,您没有取消引用该指针以访问原始 TSocket,这是导致错误消息的原因。

    但更重要的是,您正在为多个客户端线程重用相同的 TSocket 变量。您的所有线程都指向同一个物理 TSocket。不要使用 @ 运算符,将 TSocketcopy 传递给每个客户端线程。幸运的是,TSocket 只是一个 UINT,它的值将按原样适合指针内部。

    并且您需要在线程退出前关闭 TSocket。如果 CompareMem() returns 为真,则您不会调用 closesocket()

  • 您正在泄漏线程,因为您从未关闭 CreateThread() 返回的 THandle

话虽如此,试试这个:

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;