I'm trying to use getpeerinfo
to ensure i can get peer info after connecting.
It fails with:
WSAENOTCONN
(10057)Socket is not connected.
A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using sendto) no address was supplied.
The basic flow is:
WSAStartup
socket()
connect()
getpeerinfo()
What am i doing wrong?
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.
We know the connection is connected because:
The answer is in the WinSock documentation.
When the
WSAConnectByName
function returns TRUE, the sockets
is in the default state for a connected socket. The sockets
does not enable previously set properties or options until SO_UPDATE_CONNECT_CONTEXT is set on the socket. Use the setsockopt function to set the SO_UPDATE_CONNECT_CONTEXT option.
So, when WSAConnectByNameW()
returns TRUE, getpeername()
fails with WSAENOTCONN
because you are not calling setsockopt(SO_UPDATE_CONNECT_CONTEXT)
to put the socket into the proper state. This is clarified in the SOL_SOCKET Socket Options documentation:
SO_UPDATE_CONNECT_CONTEXT
This option is used with the
ConnectEx
,WSAConnectByList
, andWSAConnectByName
functions. This option updates the properties of the socket after the connection is established. This option should be set if thegetpeername
,getsockname
,getsockopt
,setsockopt
, orshutdown
functions are to be used on the connected socket.
Try this:
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;
...
That being said, there is no need to use getpeername()
in your example, because it returns the same info that WSAConnectByNameW()
already returns in the variable you are passing to its RemoteAddress
parameter.