【问题标题】:getpeername() always fails with error code WSAENOTCONNgetpeername() 总是失败,错误代码为 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, {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.

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

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

阅读奖励

【问题讨论】:

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

标签: sockets tcp network-programming


【解决方案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),
  {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 参数的变量中返回的信息相同。

【讨论】:

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