SocketAddr类
SocketAddr类定义在lib/SocketAddr.hpp中,实现在lib/SocketAddr.cpp中。SocketAddr类封装了网络通信中经常用到的地址结构以及在这些结构上进行的操作。地址解析也是在SocketAddr的成员函数中完成的。
首先讨论一下Socket编程中用于表示网络地址的数据结构。
网络通信中的端点地址可以一般化的表示为 (地址族,该族中的端点地址)。Socket接口系统中用来表示通用的网络地址的数据结构是sockaddr:
struct sockaddr { /* struct to hold an address */
u_char sa_len /* total length */
u_short sa_family; /* type of address */
char sa_data[14]; /* value of address */
};
其中sa_family表示地址所属的地址族,TCP/IP协议的地址族用常量AF_INET表示,而UNIX命名管道的地址族用常量AF_UNIX表示。
使用Socket的每个协议族都精确定义了自己的网络端点地址,并在头文件中提供了相应的结构声明。用来表示TCP/IP地址的数据结构如下:
struct sockaddr_in {
u_char sin_len; /* total length */
u_short sin_family; /* type of address */
u_short sin_port; /* protocol port number */
struct in_addr sin_addr; /* IP address */
char sin_zero[8]; /* unused (set to zero) */
}
其 中,sin_len和sin_family和sockaddr机构中的sa_len以及sa_family表示相同的数据。结构sockaddr_in将 sockaddr中通用的端点地址sa_data(14字节长)针对TCP/IP的地址结构作了细化,分为8bit的端口地址sin_port和 32bit的IP地址。在Linux系统中,结构in_addr如下定义:
struct in_addr {
unsigned long s_addr; /* IP address */
}
可见,结构in_addr仅有一个成员,表示一个32bit的数据,即IP地址。对于通用地址结构中的其余bit,填充0。
Socket接口中的很多函数都是为通用的网络地址结构设计的,例如,我们既可以用bind函数将一个socket绑定到一个TCP/IP的端口 上,也可以用bind函数将一个socket绑定到一个UNIX命名管道上。因此,像bind, connect, recvfrom, sendto等函数都要求一个sockaddr结构作为指名地址的参数。这时,我们就要使用强制类型转换把表示IP地址的sockaddr_in结构转换 为sockaddr结构进行函数调用。但实际上,sockaddr和sockaddr_in结构表示的均是同一地址。它们在内存中对应的区域是重合的。
SockedAddr类的功能比较单一,成员变量mAddress就是SocketAddr的实例所表示的TCP/IP端口地址(包括IP地址和 TCP/UDP端口号)。类声明mAddress为iperf_sockaddr类型的变量,而在文件/lib/headers.h中,有
typedef sockaddr_in iperf_sockaddr
因此,iperf_sockaddr实际上就是sockaddr_in类型的变量。SockedAddr的成员函数都是对mAddress进行读取或修改的操作的。比较复杂的成员函数是setHostname,它完成了地址解析的过程,源代码如下(已将不相关部分删除)。
/* -------------------------------------------------------------------
* Resolve the hostname address and fill it in.
* ------------------------------------------------------------------- */
void SocketAddr::setHostname( const char* inHostname ) {
assert( inHostname != NULL );
mIsIPv6 = false;
mAddress.sin_family = AF_INET;
// first try just converting dotted decimal
// on Windows gethostbyname doesn't understand dotted decimal
int rc = inet_pton( AF_INET, inHostname, (unsigned char*)&(mAddress.sin_addr) );
if ( rc == 0 ) {
struct hostent *hostP = gethostbyname( inHostname );
if ( hostP == NULL ) {
/* this is the same as herror() but works on more systems */
const char* format;
switch ( h_errno ) {
case HOST_NOT_FOUND:
format = "%s: Unknown host/n";
break;
case NO_ADDRESS:
format = "%s: No address associated with name/n";
break;
case NO_RECOVERY:
format = "%s: Unknown server error/n";
break;
case TRY_AGAIN:
format = "%s: Host name lookup failure/n";
break;
default:
format = "%s: Unknown resolver error/n";
break;
}
fprintf( stderr, format, inHostname );