sockaddr
BOOL client(UINT uPort, LPCTSTR lpIpaddr) {
SOCKET sok = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
int Ret = 0;
if (socket == INvalid_SOCKET)
return false;
sockaddr_in server = {0};
server.sin_family = AF_INET;
server.sin_port = htons(uPort);
server.sin_addr.s_addr = inet_addr(lpIpAddr);
//这里的(sockaddr *)&server,把sockaddr_in地址强制转成sockaddr类型地址
if ((Ret = connect(sok, (sockaddr *)&server, sizeof(server))) != SOCKET_ERROR) {
...;
}
closesocket(sok);
return TRUE;
}
这是一个客户端,可以看到在connect中,把sockaddr_in类型地址转成了sockaddr类型地址,这是为何? 首先来看一下connect函数的原型:
离一下题, 可以看到第二个形参是一个指向sockaddr的常量指针,可能会有疑惑为何不写成这样:
connect(sok, (const sockaddr *)&server, sizeof(server));
这样不是更符合吗? 但因为加了const后的指针可以接受非const类型的变量,反过来不行(非const类型指针不能指向const类型变量)。所以这里是可以的,当然你这么写也没问题。
言归正传,从API语法来看,这样子没毛病,但让人困惑的是sockaddr与sockaddr_in这样直接强转没问题吗?里面结构的内容是什么,在这之前先把关于windows版本的一些预定义量给出:
#define _WIN32_WINNT_WIN6 0x0600 // Windows Vista
#define _WIN32_WINNT_VISTA 0x0600 // Windows Vista
#define _WIN32_WINNT_WS08 0x0600 // Windows Server 2008
#define _WIN32_WINNT_LONGHORN 0x0600 // Windows Vista
接下来看一下这两个结构中的内容:
// 这边是sockaddr_in结构
// IPv4 Socket address, Internet style
//
typedef struct sockaddr_in {
#if(_WIN32_WINNT < 0x0600)
short sin_family;
#else //(_WIN32_WINNT < 0x0600)
ADDRESS_FAMILY sin_family; //typedef USHORT ADDRESS_FAMILY
#endif //(_WIN32_WINNT < 0x0600)
USHORT sin_port;
IN_ADDR sin_addr; //typedef in_addr IN_ADDR
CHAR sin_zero[8];
} SOCKADDR_IN, *PSOCKADDR_IN;
// 这里是in_addr结构
// IPv4 Internet address
// This is an 'on-wire' format structure.
//
typedef struct in_addr {
union {
struct { UCHAR s_b1,s_b2,s_b3,s_b4; } S_un_b;
struct { USHORT s_w1,s_w2; } S_un_w;
ULONG S_addr;
} S_un;
#define s_addr S_un.S_addr /* can be used for most tcp & ip code */
#define s_host S_un.S_un_b.s_b2 // host on imp
#define s_net S_un.S_un_b.s_b1 // network
#define s_imp S_un.S_un_w.s_w2 // imp
#define s_impno S_un.S_un_b.s_b4 // imp #
#define s_lh S_un.S_un_b.s_b3 // logical host
} IN_ADDR, *PIN_ADDR, FAR *LPIN_ADDR;
首先看一下sockaddr_in结构,这个结构中首先是预定义选择语句。
显然根据我们上面的预定义值我们可以知道会进入#else分支,但那没什么影响只是short是否是无符号数的区别。这个地方我们一般都填AF_INET。
接着就是sin_port, sin_addr, 注意到最后一个字段是sin_zero[8],这8个字节实际上没有任何作用,只是为了填充并且与sockaddr对齐。
关于sin_addr,它是in_addr结构类型
这个类型中只有一个联合类型,请注意第一个预定义也就是:
#define s_addr S_un.S_addr /* can be used for most tcp & ip code */
//这一步简化了下面那条语句
//这里本来应该是:
server.sin_addr.S_un.S_addr = inet_addr(lpIpAddr);
//由于那条预定义语句简化成这样
server.sin_addr.s_addr = inet_addr(lpIpAddr);
也就是说in_addr中只使用了ULONG S_addr这个字段,可以把它想像成这样:
typedef struct in_addr {
union {
ULONG S_addr;
} S_un;
#define s_addr S_un.S_addr /* can be used for most tcp & ip code */
} IN_ADDR, *PIN_ADDR, FAR *LPIN_ADDR;
因为其他变量基本没有使用到,当然只能那么想而已。
再来看一下sockaddr结构的定义:
//
// Structure used to store most addresses.
//
typedef struct sockaddr {
#if (_WIN32_WINNT < 0x0600)
u_short sa_family;
#else
ADDRESS_FAMILY sa_family; // Address family.
#endif //(_WIN32_WINNT < 0x0600)
CHAR sa_data[14]; // Up to 14 bytes of direct address.
} SOCKADDR, *PSOCKADDR, FAR *LPSOCKADDR;
那条预定义选择语句与sockaddr_in一模一样,而sockaddr结构的后面部分显然比sockaddr_in结构简单许多, 它只有一个14字节的char数组。
也就是说,sockaddr与sockaddr_in的结构大小完全相同,而唯一的区别就是sa_family字段的后面部分,但是他们在内存中完全对齐。这也是为何sockaddr与sockaddr_in能够互相转换的原因。
完
相关阅读
struct sockaddr和struct sockaddr_in这两个结构体用来处理网络通信的地址。 一、sockaddr sockaddr在头文件#include <sys/so