getpeername () всегда завершается ошибкой с кодом ошибки WSAENOTCONN - PullRequest
0 голосов
/ 08 марта 2020

Я пытаюсь использовать getpeerinfo, чтобы гарантировать, что я могу получить информацию о партнере после подключения.

Сбой:

WSAENOTCONN (10057)

Разъем не подключен.

Запрос на отправку или получение данных был отклонен, поскольку сокет не подключен и (при отправке на сокет датаграммы с использованием sendto) адрес не был предоставлен.

Основа c поток:

  • WSAStartup
  • socket()
  • connect()
  • getpeerinfo()

Что я делаю не так?

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  Winapi.Winsock2;

procedure Main;
var
    hSocket: TSocket;
    wsData: TWSAData;
    nodeName: string;
    serviceName: string;
    localAddressLength: Cardinal;
    localAddress: TSockAddr;
    remoteAddressLength: Cardinal;
    remoteAddress: TSockAddr;
    name: TSockAddr;
    nameLen: Integer;
    errorCode: Integer;
    bRes: Boolean;
begin
    WSAStartup($0202, {var}wsData);

    hSocket := socket(AF_INET, SOCK_STREAM, 0);

    nodeName := 'stackoverflow.com';
    serviceName := '80';

    bRes := WSAConnectByNameW(hSocket, PChar(nodeName), PChar(serviceName),
            {var}localAddressLength, {var}localAddress,
            {var}remoteAddressLength, {var}remoteAddress,
            nil, nil);
    if not bRes then
    begin
        errorCode := WSAGetLastError;
        RaiseLastOSError(errorCode);
    end;

    //If no error occurs, getpeername returns zero.
    //Otherwise, a value of SOCKET_ERROR is returned,
    //and a specific error code can be retrieved by calling WSAGetLastError.
    nameLen := sizeof(name);
    errorCode := getpeername(hSocket, {var}name, {var}nameLen);
    if errorCode <> 0 then
    begin
        errorCode := WSAGetLastError;
        RaiseLastOSError(errorCode);
    end;
end;

begin
    try
        Main;
    except
        on E: Exception do
            Writeln(E.ClassName, ': ', E.Message);
    end;
end.

Мы знаем, что соединение подключено, потому что:

  • мы только что подключились
  • мы сделали the вещь , чтобы проверить, подключен ли сокет

Бонусное чтение

1 Ответ

2 голосов
/ 10 марта 2020

Ответ содержится в документации WinSock.

WSAConnectByNameW() function

Когда функция WSAConnectByName возвращает TRUE, сокет s находится в состоянии по умолчанию для подключенного сокета. Сокет s не разрешает ранее установленные свойства или параметры, пока для сокета не будет установлен SO_UPDATE_CONNECT_CONTEXT. Используйте функцию setsockopt , чтобы установить параметр SO_UPDATE_CONNECT_CONTEXT.

Итак, когда WSAConnectByNameW() возвращает TRUE, getpeername() завершается неудачно с WSAENOTCONN, потому что вы не вызываете setsockopt(SO_UPDATE_CONNECT_CONTEXT), чтобы перевести сокет в надлежащее состояние. Это поясняется в документации SOL_SOCKET Socket Options :

SO_UPDATE_CONNECT_CONTEXT

Эта опция используется с ConnectEx, WSAConnectByList, и WSAConnectByName функций. Эта опция обновляет свойства сокета после установления соединения. Эта опция должна быть установлена, если на подключенном разъеме должны использоваться функции getpeername, getsockname, getsockopt, setsockopt или shutdown.

Попробуйте это:

bRes := WSAConnectByNameW(hSocket, PChar(nodeName), PChar(serviceName),
  {var}localAddressLength, {var}localAddress,
  {var}remoteAddressLength, {var}remoteAddress,
  nil, nil);
if not bRes then
begin
  errorCode := WSAGetLastError;
  RaiseLastOSError(errorCode);
end;

// ADD THIS..
errorCode := setsockopt(hSocket, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, nil, 0);
if errorCode <> 0 then
begin
  errorCode := WSAGetLastError;
  RaiseLastOSError(errorCode);
end;

...

При этом нет необходимости использовать getpeername() в вашем примере, потому что он возвращает ту же информацию, которую WSAConnectByNameW() уже возвращает в переменной, которую вы передаете его RemoteAddress параметр.

...