2013-06-20 46 views
1

在我的程序中我在libevent事件循环中注册了一个EV_READ事件,用于connfd。 时触发这个事件中,我使用getpeername得到对方的IP /端口地址getpeername在什么条件下返回IP:PORT 0.0.0.0:012

socklen_t socklen; 
struct sockaddr_in client_addr; 
socklen = sizeof(client_addr); 
retval = getpeername(connfd, (struct sockaddr *)&client_addr, &socklen); 
if(retval == -1) perror("getpeername error!\n"); 

,但有时,它返回0.0.0.0:0

,然后我意识到错误是Transport endpoint is not connected

但是recv(connfd,buf,..)返回1273,这意味着它接收到1273个字节。 如果连接结束,recv()如何获取字节? 以及如何触发事件?

谢谢!

+0

如果'getpeername()'成功,它永远不会报告0.0.0.0。 'getpeername()'返回一个错误代码?你显示的代码没有检查。 –

+0

传输端点没有连接 – user1944267

+0

难道这是因为你的代码假设它必须是套接字家族有时是'AF_INET6'而不是'AF_INET'?将'struct sockaddr_in6'解释为'struct sockaddr'会导致无意义的结果,可能为0.0.0.0。 – Celada

回答

1

由于@Celada指出,getpeername()作品在struct sockaddr *其大小,在你的情况下,struct sockaddr_in的一个,而不是大到足以容纳约IPv6地址信息。

这个struct sockaddr将被实现为struct sockaddr_instruct sockaddr_in6,这取决于套接字是处理IPv4还是IPv6地址。

你是分配 a struct sockaddr_in它是16字节(仅用于IPv4地址的空间)。一个IPv6需要一个struct sockaddr_in6这是28个字节,并且存储它的IP地址之后的16个第一字节。因此将struct sockaddr_in读为struct sockaddr_in6

下一部分解释了为什么您可能会读取0.0.0.0:0。如果您对解决方案更感兴趣,请直接转到最后一节。


你为什么要用你当前的代码读取一些归零的IP地址?

下面是这些结构的定义:

struct sockaddr_in { 
    short   sin_family; // e.g. AF_INET, AF_INET6 <-- 2 bytes 
    unsigned short sin_port;  // e.g. htons(3490) <-- 2 bytes 
    struct in_addr sin_addr;  // (only contains IPv4 address) <-- 4 bytes 
    char    sin_zero[8]; // zero this if you want to <-- 8 bytes 
}; 

struct sockaddr_in6 { 
    u_int16_t  sin6_family; // address family, AF_INET6 <-- 2 bytes 
    u_int16_t  sin6_port;  // port number, Network Byte Order <-- 2 bytes 
    u_int32_t  sin6_flowinfo; // IPv6 flow information <-- 4 bytes 
    struct in6_addr sin6_addr;  // IPv6 address <-- 16 bytes 
    u_int32_t  sin6_scope_id; // Scope ID <-- 4 bytes 
}; 

来源:Beej's Guide to Network Programming's struct sockaddr and pals page

因此,当你尝试从struct sockaddr_ingetpeername()struct sockaddr_in6信息填充它读取的IP地址,你实际上是阅读struct sockaddr_in6sin6_flowinfo部分。该流量信息通常为零,除非连接明确标记了流量ID。 这就是为什么你会读取归零IP地址。


如何提供一个通用的存储,以便getpeername()作品在这两种情况下,无需提前英特尔对等的连接类型(确实)的可能性?

这就是struct sockaddr_storage在哪里踢!

这是您应该用来初始化空间以提供getpeername()的类型,并将其转换为struct sockaddr *。它的设计目的是保存的任何信息连接类型。

你的代码应该是:

socklen_t socklen; 
struct sockaddr_storage client_addr; 
socklen = sizeof(client_addr); 
retval = getpeername(connfd, (struct sockaddr *)&client_addr, &socklen); 
if(retval == -1) perror("getpeername error!\n"); 

要使用结构,你应该再转换回要么struct sockaddr_instruct sockaddr_in6,这取决于你struct sockaddr_storagess_family字段的值。

if (addr.ss_family == AF_INET) { 
    struct sockaddr_in *s = (struct sockaddr_in *)&addr; 
    port = ntohs(s->sin_port); 
    inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof ipstr); 
} else if(addr.ss_family == AF_INET6) { 
    struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr; 
    port = ntohs(s->sin6_port); 
    inet_ntop(AF_INET6, &s->sin6_addr, ipstr, sizeof ipstr); 
} 

源(修改):Beej's Guide to Network Programming's getpeername() page

呀,因为IP地址长度从IPv4到IPv6的不同,没有泛型函数这样做对你来说,返回一个IP地址...因为你仍然需要知道您是否收到32位或128位,从而再次检查地址系列!