为什么服务器套接字给出的端口号与绑定的端口号不同?

4

我正在用C语言编写一个小型的socket程序。在服务器端,我使用socket()系统调用创建了一个套接字描述符,然后将该套接字绑定到一个端口上。然后,我尝试获取描述符的IP地址/端口号,但它给出的端口号与绑定的端口号不同。我正在尝试使用getsockname()方法来获取IP/端口,是否正确使用此方法?请帮助我。

#define SERVER_ADDR "127.0.0.1"
#define SERVER_PORT "9090" // actual port no I am binding
#define QUEUE_LENGTH 10

int main()
{
struct addrinfo hints, *servinfo, *temp;

memset(&hints,0,sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;

// here I am passing SERVER_PORT == 9090
int status = getaddrinfo(SERVER_ADDR,SERVER_PORT,&hints,&servinfo);
if(status != 0)
{
printf("Server: getaddrinfo() errored with code %d\n",status);
return;
}

int sfd = -1;
for(temp = servinfo; temp != NULL; temp = servinfo->ai_next)
{
sfd = socket(temp->ai_family,temp->ai_socktype,temp->ai_protocol);
if(sfd == -1)
{
  printf("Server: Socket error with code %d\n",sfd);
  continue;
}

status = bind(sfd,temp->ai_addr,temp->ai_addrlen);
if(status == -1)
{
  printf("Server: Bind error with code %d\n",status);
  continue;
}
printf("Server: Bind Successful\n");

// un necessary code goes here
struct sockaddr_in server_address;
char ipv4[INET_ADDRSTRLEN];
int addr_size = sizeof(server_address);
// i am using below method to get the port no from socket descriptor
getsockname(sfd, (struct sockaddr *)&server_address, &addr_size);

// I am expecting below will print 9090. but it prints different port no why ? 
printf("Server Port: %d\n",server_address.sin_port);
printf("Port from getsddrinfo: %d\n",( (struct sockaddr_in *)temp->ai_addr)->sin_port);

inet_ntop(AF_INET, &(server_address.sin_addr),ipv4,INET_ADDRSTRLEN);
printf("Server IP Address: %s\n",ipv4);
// un necessary code ends here
break;
}

if(temp == NULL)
{
printf("Server: Failed to bind\n");
return;
}
status = listen(sfd,QUEUE_LENGTH);
if(status == -1)
{
printf("Server: Listening failed\n");
return;
}
printf("Server: waiting for coneections...\n");

while(1)
{
printf("Server: Main loop, will wait for client to connect...\n");
struct sockaddr client_address;
int addr_length = sizeof client_address;
// accepting client
int new_sfd = accept(sfd,&client_address,&addr_length); 
}
printf("Server: Done!\n"); 
}

输出结果如下:

Server: Bind Successful
Server Port: 33315 --> why this different from one I have binded (9090)
Port from getsddrinfo: 33315 --> why this different from one I have binded (9090)
Server IP Address: 127.0.0.1
Server: waiting for coneections...
Server: Main loop, will wait for client to connect...

这个程序相关的内容应该翻译成:这个for(temp = servinfo; temp != NULL; temp = servinfo->ai_next)是不是应该改成for(temp = servinfo; temp != NULL; temp = temp->ai_next) - alk
您说得对,即使修改后,我仍然得到了不同的端口号。谢谢。 - Dhanaraj Durairaj
请适当缩进您的代码,这样阅读起来会更容易。 - alk
抱歉,我是 Stack Overflow 的新手,这是我的第一个问题,未来我会做得更好。谢谢 alk :-) - Dhanaraj Durairaj
2个回答

8
struct sockaddr 的十进制成员以网络字节顺序返回。因此,在使用和打印这些值之前,您需要使用 ntoh 函数族将这些值转换为主机字节顺序。

我将print语句更改为printf("Server Port: %d\n",ntohs(server_address.sin_port));,现在我得到了正确的端口号9090。非常感谢您的帮助! - Dhanaraj Durairaj
请务必接受答案,这样 alk 就能得到相应的积分。 - Remy Lebeau
@Remy Lebeau:根据alk的回答,我只在我的程序中进行了更改。我不明白你的意思? - Dhanaraj Durairaj
如果alk的回答解决了你的问题,那么请将其标记为被接受的答案,这样他就能获得相应的声望分数。 - Remy Lebeau

1
hints.ai_socktype = SOCK_STREAM;之后添加hints.sin_port = htons( 9090 );

在 OP 的上下文中,在“hints”中设置端口毫无意义,因为它将被“getaddrinfo()”覆盖。我假设“getaddrinfo()”已经使用正确的字节顺序(网络)填充了'hints'的成员。 - alk
抱歉,我在想getservbyname()函数,不好意思。 - David Ranieri
hints是struct addrinfo类型的,它没有直接的成员sin_port。正如alk所说,getaddrinfo()将以网络字节顺序填充servinfo的成员。可能像这样:((struct sockaddr_in *)servinfo->ai_addr)->sin_port = htons(9090);。我理解得对吗? - Dhanaraj Durairaj
你是对的,sin_port是struct sockaddr_in的直接成员。 - David Ranieri
getaddrinfo() 接受主机和服务(对于IP4,即主机名或其IP地址和端口号,均以ASCII表示)来返回一个或多个已填充的 struct addrinfo,以便传递给 bind()(服务器端)或 connect()(客户端)。 - alk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接