Get Local Port used by Socket

2019-03-03 17:51发布

问题:

I need to get the local port used by a (client) socket.

It was my understanding that Windows Sockets performs an implicit bind function call, therefore getsockname() after sendto() should provide the assigned port. However, it always sets 0 as the port number. Am I missing something?

ex:

if (sendto(sockfd, ...) != SOCKET_ERROR)
    printf("Sent\n");

if (getsockname(sockfd, (struct sockaddr*)&sin, &sinlen) != SOCKET_ERROR)
    printf("port = %u\n", ntohs(sin.sin_port);
else
    printf("Error");

//result: Sent, port = 0

回答1:

Problem solved with a restart of the computer. Still unknown as to the actual cause, but at this point I'm just happy it's working.

If anyone has an idea for fixing the issue without a restart (for future readers), feel free to post.



回答2:

The only ambiguity I can see in your example code is what size you assigned to sinlen before calling. (you do not show it) If you are using winsock, it should be defined, and assigned int sinlen = sizeof(sin);

I used this code on my system, and it returns a non-zero value for the port I am connecting through:

struct sockaddr_in sin;
int len = sizeof(sin);
if (getsockname(sock, (struct sockaddr *)&sin, &len) == -1)
    //handle error
else
    printf("port number %d\n", ntohs(sin.sin_port));    

By the way, The ntohs function function returns the value in host byte order. If [ sin.sin_port ] is already in host byte order, then this function will reverse it. It is up to [your] application to determine if the byte order must be reversed. [text in brackets are my emphasis]

In answer to comment question ( getsockname() ):

The function prototype for getsockname():

int getsockname(
  _In_    SOCKET          s,
  _Out_   struct sockaddr *name,
  _Inout_ int             *namelen  //int, not socklen_t
);

For more discussion on socklen_t

Edit (address possible approach to re-setting sockets without rebooting PC.)
If winsock API calls cease to work predictably, you can re-start sockets without rebooting the PC by using WSAStartup and WSACleanup (see code example at bottom of link for WSAStartup)



回答3:

You say you want to know the LOCAL port, but your line

sendto(sockfd, ...)

implies sockfd is the REMOTE descriptor. Your later code may therefore give you info about the REMOTE port, not the LOCAL one. 'sockets' are not both ends, meaning one connection. A socket is one end, meaning the IP and port number of one end of the connection. The first parameter of your getsockname() is not a reference or a pointer, it is therefore not an output from the function, but an input. You're telling the function to use the same socket descriptor that you just sent to, ie. the remote one.

Formatting error. ntohs() returns unsigned short so the format should be %hu, not %u or %d. If you grab too many bytes they are not the port.

Answer. After using sendto() try using gethostname() then getaddrinfo() on the name that comes back. Note: the addrinfo structures you get back will give you struct sockaddr pointers which you will need to re-cast to struct sockaddr_in pointers to access the local port number.



回答4:

To find the local port number the kernel dreamed up when you issued a sendto() function perhaps you could write a routine to parse the output from the (gnu linux) commands 'ss' or 'netstat'. (Not sure if these are POSIX compatible.) Or maybe you could access /proc/net if you have the privilege.