getpeername() 总是失败,错误代码为 WSAENOTCONN

Posted

技术标签:

【中文标题】getpeername() 总是失败,错误代码为 WSAENOTCONN【英文标题】:getpeername() always fails with error code WSAENOTCONN 【发布时间】:2020-03-08 19:08:18 【问题描述】:

我正在尝试使用getpeerinfo 来确保我可以在连接后获取对等信息。

它失败了:

WSAENOTCONN (10057)

套接字未连接。

由于未连接套接字并且(使用 sendto 在数据报套接字上发送时)未提供地址,因此不允许发送或接收数据的请求。

基本流程是:

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

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

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

    bRes := WSAConnectByNameW(hSocket, PChar(nodeName), PChar(serviceName),
            varlocalAddressLength, varlocalAddress,
            varremoteAddressLength, varremoteAddress,
            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, varname, varnameLen);
    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.

我们知道连接已连接,因为:

我们刚刚连接 我们做了thething 来检查套接字是否已连接

阅读奖励

getpeername() always fails with error code WSAENOTCONN for a socket connected by using ConnectEx() function while the socket is connected

【问题讨论】:

“连接后您的连接似乎已重置”,引用您的引文。 我也很好奇为什么你认为你已经拥有了 peername。 对于我没有它的情况(不是每个套接字都会用 WSAConnectByName 打开,甚至由我连接)。还有那个 MSFT 论坛的人没有尝试过,只是猜测。 1.您刚刚连接的事实并不能证明它仍然连接。 2. 你做了什么“事情”来检查它是否仍然连接?您的链接中没有提到。唯一有效的测试是成功的recv()。 3. 有人可能在猜测这一事实并不意味着他们会错。 我仍然连接的事实证明我仍然连接。事情就是你引用的引文中的事情。对你起作用吗;你试过了吗? Sobs in sockets api 【参考方案1】:

答案在 WinSock 文档中。

WSAConnectByNameW() function

WSAConnectByName 函数返回TRUE 时,套接字s 处于连接套接字的默认状态。 在套接字上设置 SO_UPDATE_CONNECT_CONTEXT 之前,套接字s 不会启用先前设置的属性或选项。使用setsockopt 函数设置 SO_UPDATE_CONNECT_CONTEXT 选项。

所以,当WSAConnectByNameW() 返回 TRUE 时,getpeername() 失败并显示WSAENOTCONN,因为您没有调用setsockopt(SO_UPDATE_CONNECT_CONTEXT) 将套接字置于正确状态。 SOL_SOCKET Socket Options 文档对此进行了说明:

SO_UPDATE_CONNECT_CONTEXT

此选项与ConnectExWSAConnectByListWSAConnectByName函数一起使用。此选项在建立连接后更新套接字的属性。 如果要在连接的套接字上使用getpeernamegetsocknamegetsockoptsetsockoptshutdown 函数,则应设置此选项。

试试这个:

bRes := WSAConnectByNameW(hSocket, PChar(nodeName), PChar(serviceName),
  varlocalAddressLength, varlocalAddress,
  varremoteAddressLength, varremoteAddress,
  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 参数的变量中返回的信息相同。

【讨论】:

哇,不错的收获。即使读过我也不会/没有明白其中的含义。我以为只是 WSAAsyncSelect 应用于套接字的标志。还有其他没有人打电话给WSAConnectByName的例子——我只知道我有一个Socket句柄;并且不是连接它的人。如果 socked 通过connectconnectExWSAConnectByNameWSAConnectByList、通过侦听套接字连接或任何其他可能用于连接套接字的机制连接,您希望您的相同代码能够工作。 connect()accept() 确保套接字在退出前处于正确状态。对于ConnectEx()WSAConnectByList()WSAConnectByName()AcceptEx(),必须在它们退出后单独更新状态。负责建立连接的人有责任确保套接字处于正确状态,然后再将SOCKET 交给其他人

以上是关于getpeername() 总是失败,错误代码为 WSAENOTCONN的主要内容,如果未能解决你的问题,请参考以下文章

为啥我会收到“从 '[String]' 转换为不相关的类型 'String' 总是失败”错误?

CreateFile 失败,错误代码为 2,而文件存在

利用 getsockname 和 getpeername 来获取某一个链接的本地地址和远端地址

IPv6 的 getaddrinfo 始终失败,错误代码为 11268096

套接字之 getsockname && getpeername

VC 为啥我获取客户端ip地址总是:204.204.204.204