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

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

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

ThreadProc — это стандартный вызов. - Носок := TSocket(P); -> Носок := TSocket(P^);

Sertac Akyuz 24.04.2019 04:24

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

BrowJr 24.04.2019 04:30

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

Remy Lebeau 24.04.2019 05:51
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
3
340
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 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;

Другие вопросы по теме