本章将介绍IPV4地址协议编程.
SOCKADDR_IN结构体
Winsock中,将IP地址和端口号指定到sockaddr_in数据结构中:
typedef struct sockaddr_in {
SHORT sin_family; //使用IP地址族时需要指定为AF_INET
USHORT sin_port; //端口号
IN_ADDR sin_addr; //IP地址
CHAR sin_zero[8]; //用于补齐,使结构体大小与SOCKADDR相等
} SOCKADDR_IN, *PSOCKADDR_IN;
另一种数据结构:
typedef struct sockaddr {
u_short sa_family;
CHAR sa_data[14]; //14字节的协议地址
} SOCKADDR, *PSOCKADDR, FAR *LPSOCKADDR;
SOCKADDR_IN 与 SOCKADDR 的区别:
SOCKADDR_IN中的sin_zero[8]用于将其大小补到与SOCKADDR相等,可以用bzero()或memset()函数将其置为零,两者可以混用,只需要在使用时用指针进行转换时即可.
指向sockaddr_in 的指针和指向sockaddr的指针可以相互转换,这意味着如果一个函数所需参数类型是sockaddr时,你可以在函数调用的时候将一个指向 sockaddr_in的指针显示转换为指向sockaddr的指针;或者相反。
你只要记住,填值的时候使用sockaddr_in结构,而作为函数的参数传入的时候转换成sockaddr结构就行了,毕竟都是16个字符长。
//IP地址结构体
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
#define s_net S_un.S_un_b.s_b1
#define s_imp S_un.S_un_w.s_w2
#define s_impno S_un.S_un_b.s_b4
#define s_lh S_un.S_un_b.s_b3
} IN_ADDR, *PIN_ADDR, FAR *LPIN_ADDR;
可以简单地认为该结构体是一个四字节的地址,无非是使用联合使之能够被更方便的使用.
大多数情况下使用 s_addr ,即联合中的最后一种形式来赋值.
可以使用下列函数方便地将带点的IP地址转换为4字节形式:
unsigned long inet_addr(
const char FAR * cp; //参数cp为不带结束符用点隔开的地址字符串,比如 "127.0.0.1" .
);
字节顺序:
不同机器的字节顺序不同,有大端小端两种.
大端字节序:高字节数据存放在内存低地址处,低字节数据存放在内存高地址处.
小端字节序:低字节数据存放在内存低地址处,高字节数据存放在内存高地址处.
在网络编程中,存在主机字节序(host-byte order)和 网络字节序(network-byte order).
当一个IP地址和端口号以多字节形式存在于计算机中时,它们是主机字节序.
当存在于网络中时,它们是网络字节序.
网络标准规定网络中的多字节值必须以大端字节序存在,因此需要做相应转换.
接下来的几个函数用于字节序的转换:
主机字节序转换为网络字节序:
u_long htonl(u_long hostlong); //unsigned long形字节转换,返回值为转换结果
int WSAHtonl(
SOCKET s,
u_long hostlong, //主机字节序的 4 字节数据
u_long FAR *lpnetlong //转换结果保存到lpnetlong指向的内存中
);
u_short htons(u_short hostshort); //unsigned short形字节转换,返回值为转换结果
int WSAHtons(
SOCKET s,
u_short hostshort, //主机字节序的 2 字节数据
u_short FAR *lpnetshort //转换结果保存到lpnetlong指向的内存中
);
相反的,网络字节序转换为主机字节序:
u_long ntohl(u_long netlong);
WSANtohl(
SOCKET s,
u_long netlong,
u_long FAR *lphostlong
);
u_short ntohs(u_short netshort);
WSANtohs(
SOCKET s,
u_short netshort,
u_short FAR *lphostshort
);
综上,我们已经知道如何填写一个 IPV4 地址了,代码如下:
SOCKADDR_IN InternetAddr; //存放IPV4地址的数据结构
INT nPortID=5150; //端口号,这里随意指定为5150
InternetAddr.sin_family=AF_INET; //使用IP地址族时需要指定为AF_INET
InternetAddr.sin_addr.s_addr=inet_addr("136.149.3.29"); //将IP地址"136.149.3.29"转换为4字节形式
InternetAddr.sin_port=htons(nPortID); //将端口号从主机字节序转换为网络字节序,这里参数做了隐式转换int->unsigned short
一般数字形式的IP不容易记忆,因此有一系列跟IP地址获取相关的函数,这在后面将会详细介绍.
现在你已经有了IPV4地址协议的基本概念,你已经可以通过创建一个socket来写一个通讯了.