【发布时间】:2017-10-21 06:20:14
【问题描述】:
我编写了如下代码。此代码应该能够检索 PASSIVE_SOCKET(服务器绑定其套接字的位置)和 CONNECTION_SOCKET(客户端已连接到的位置)的 IP 地址。
result_t print_socket_address(int sockfd, socket_type_t socket_type) {
char *ip_address; // address (passive) socket was binded to
int port; // port (passive) socket was binded to
switch(socket_type)
{
case PASSIVE_SOCKET:
if(get_current_address_and_port(sockfd, &ip_address, &port) == FAILURE) {
fprintf(stderr, "get_current_address_and_port: faild!\n");
free(ip_address);
return FAILURE;
}
printf("Created passive socket %d binded to %s:%d\n", sockfd, ip_address, port);
break;
case CONNECTION_SOCKET:
if(get_peer_address_and_port(sockfd, &ip_address, &port) == FAILURE) {
fprintf(stderr, "get_peer_address_and_port: faild!\n");
free(ip_address);
return FAILURE;
}
printf("Socket %d connected to %s:%d\n", sockfd, ip_address, port);
break;
default:
fprintf(stderr, "Incorrect socket type!\n");
free(ip_address);
return FAILURE;
}
free(ip_address);
return SUCCESS;
}
/**
* function retrieves current ip address and port
* socket is bound to for given socket file descriptor
*/
result_t get_current_address_and_port(int sockfd, char **ip_address, int *port) {
struct sockaddr sockaddr;
socklen_t sockaddrlen = sizeof(sockaddr);
if(getsockname(sockfd, &sockaddr, &sockaddrlen) < 0) {
fprintf(stderr, "getsockname: %s\n", strerror(errno));
return FAILURE;
}
sockaddr.sa_family = AF_INET6;
return get_address_and_port_from_sockaddr(&sockaddr, ip_address, port);
}
/**
* function retrieves peer ip address and port
* socket is connected to for given socket file descriptor
*/
result_t get_peer_address_and_port(int sockfd, char **ip_address, int *port) {
struct sockaddr sockaddr;
socklen_t sockaddrlen = sizeof(sockaddr);
if(getpeername(sockfd, &sockaddr, &sockaddrlen) < 0) {
fprintf(stderr, "getpeername: %s\n", strerror(errno));
return FAILURE;
}
return get_address_and_port_from_sockaddr(&sockaddr, ip_address, port);
}
/**
* function unwrap ip address and port from addrinfo structure
*/
result_t get_address_and_port_from_addrinfo(const struct addrinfo *addrinfo, char **ip_address, int *port) {
return get_address_and_port_from_sockaddr((struct sockaddr *)addrinfo->ai_addr, ip_address, port);
}
/**
* function unwrap ip address and port from sockaddr structure
*/
result_t get_address_and_port_from_sockaddr(const struct sockaddr *sockaddr, char **ip_address, int *port) {
*ip_address = (char *) malloc(INET6_ADDRSTRLEN * sizeof(char));
// converting network address to presentation address
if(inet_ntop(sockaddr->sa_family, get_in_addr(sockaddr), *ip_address, INET6_ADDRSTRLEN * sizeof(char)) == NULL) {
fprintf(stderr, "inet_ntop: %s\n", strerror(errno));
return FAILURE;
}
// converting network port to host port
*port = ntohs(get_in_port(sockaddr));
return SUCCESS;
}
/**
* function unwrap in_addr or in6_addr structure from
* sockaddr structure depending on address family
* AF_INET or AF_INET6
*/
void *get_in_addr(const struct sockaddr *sa) {
if( sa->sa_family == AF_INET) // IPv4 address
return &(((struct sockaddr_in*)sa)->sin_addr);
// else IPv6 address
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
/**
* function unwrap in_port from sockaddr structure
* depending on address family AF_INET or AF_INET6
*/
in_port_t get_in_port(const struct sockaddr *sa)
{
if( sa->sa_family == AF_INET ) // IPv4 address
return (((struct sockaddr_in*)sa)->sin_port);
// else IPv6 address
return (((struct sockaddr_in6*)sa)->sin6_port);
}
但是当我使用这段代码时,我得到了一些奇怪的行为。返回我的服务器绑定套接字的 IP 地址,例如:
创建绑定到 ::ba54:431c:f9:55401 的被动套接字 4
55401 是端口号,它是正确的。但是这个 ::ba54:431c:f9 是什么?我想这可能是一些 IPv6。但为什么?我的电脑在局域网的IP地址是192.168.8.102! 此外,当我尝试通过客户端程序连接此服务器时,我必须使用 192.168.8.102 IP 地址进行连接,否则使用此 ::ba54:431c:f9 会出现“找不到路由”之类的错误?当客户端使用 192.168.8.102 IP 地址与服务器连接时,它会打印它连接到的计算机的 IP 地址和端口号,我得到另一个像这样的 ODD IP 地址:
Socket 3 连接到 ::3300:5208:6d16:9c88:55401
所以这里只有端口号匹配,IP地址不正确! 在与服务器连接之前在本地绑定时,客户端套接字绑定的 IP 地址与它所连接的计算机的 IP 地址相同(它在我的 LAN 网络中是物理上不同的计算机),即 ::3300:5208:6d16:9c88:52040 ,其中 52040 是客户端绑定其套接字的端口。
我什至尝试将服务器计算机 192.168.8.1 的 IP 地址转换为 IPv6,但我得到的是这样的:0:0:0:0:0:ffff:c0a8:866 和这个地址在客户端程序中使用时连接到服务器工作正常!但是使用上述功能的客户端会打印出这个完全不同的 IP 地址,例如:::::3300:5208:6d16:9c88:52040
那么我应该如何将这个函数写成:
启用服务器打印它所绑定的 IP 地址和端口号(通常 IPv6 或 IPv4 可以连接到它选择的任何地址/任何端口)
使客户端能够打印其绑定和正在连接的 IP 地址和端口号(通常可以根据需要使用 IPv4 或 IPv6)。
我想在服务器程序中显示 IP 地址和端口号,然后我可以在客户端程序中使用它们来连接两者。现在我需要猜测它应该是我在网络首选项中找到的计算机地址,并假设端口号是正确的,然后尝试连接。
【问题讨论】:
-
你想用这段代码实现什么:
sockaddr.sa_family = AF_INET6;?看起来像您将获得的地址系列更改为 IPv6,这可能解释了您返回的意外 IPv6 地址。
标签: c sockets networking