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
此选项与
ConnectEx
、WSAConnectByList
、和WSAConnectByName
函数一起使用。此选项在建立连接后更新套接字的属性。 如果要在连接的套接字上使用getpeername
、getsockname
、getsockopt
、setsockopt
或shutdown
函数,则应设置此选项。
试试这个:
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 通过connect
、connectEx
、WSAConnectByName
、WSAConnectByList
、通过侦听套接字连接或任何其他可能用于连接套接字的机制连接,您希望您的相同代码能够工作。
connect()
和 accept()
确保套接字在退出前处于正确状态。对于ConnectEx()
、WSAConnectByList()
、WSAConnectByName()
和AcceptEx()
,必须在它们退出后单独更新状态。负责建立连接的人有责任确保套接字处于正确状态,然后再将SOCKET
交给其他人以上是关于getpeername() 总是失败,错误代码为 WSAENOTCONN的主要内容,如果未能解决你的问题,请参考以下文章
为啥我会收到“从 '[String]' 转换为不相关的类型 'String' 总是失败”错误?
利用 getsockname 和 getpeername 来获取某一个链接的本地地址和远端地址
IPv6 的 getaddrinfo 始终失败,错误代码为 11268096