在写C/S模式的客户端服务端时经常遇到:主机序转网络序、网络序转主机序、十进制的IP转网络序、网络序转十进制IP。总是搞混,所以抽出点时间记录一下。
下面的代码是绑定socket的函数。
sockaddr_in addrin;
addrin.sin_family = AF_INET;
addrin.sin_port = htons(nport); //端口号
addrin.sin_addr.s_addr = inet_addr(szip);//ip地址 127.0.0.1
if(SOCKET_ERROR == bind(fd,(const sockaddr*)&addrin,sizeof(addrin)))
{
StopServer();
return false;
}
这里我先给点分十进制、主机序和网络序的关联:
用IP地址127.0.0.1为例:
第一步 127 . 0 . 0 . 1 把IP地址每一部分转换为8位的二进制数。
第二步 01111111 00000000 00000000 00000001 = 2130706433 (主机字节序)
然后把上面的四部分二进制数从右往左按部分重新排列,那就变为:
第三步 00000001 00000000 00000000 01111111 = 16777343 (网络字节序)
如果上面的主机序和网络序的直观转换不明白原理的话,那你应该先了解一下 “ 大小端转换 ”。
大端模式,是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位往低位放;
小端模式,是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中,这种存储模式将地址的高低和数据位权有效地结合起来,高地址部分权值高,低地址部分权值低,和我们的逻辑方法一致。
因为我们的操作系统有一部分是用小端模式的,也有用大端模式的,即主机序可能为大端模式或小端模式,如果两台电脑存储模式不一样,会造成传输接收数据时存储混乱,所以为了保证传输接收数据时存储不至于混乱,TCP/IP规定网络传输数据时采用大端模式,网络字节序采用大端模式。
先放出函数:
htons() -->将unsigned short 类型 从主机序转到网络序 --------此函数常用到处理端口号 addrin.sin_port = htons(nport); //端口号
ntohs() -->将unsigned short 类型 从网络序转到主机序
htonl() -->将unsigned long类型 从主机序转到网络序 --------此函数常用到处理ip
ntohl() -->将unsigned long类型 从网络序转到主机序
inet_addr(”192.0.0.1”) -->将点分十进制IP转为长整型数(u_long类型)
----------它转换出来的长整型数其实是网络序,如果想要得到是主机序可以这样ntohl( inet_addr("192.0.0.1") )
inet_ntoa() -->将长整型数(u_long类型) 转为点分十进制
----------它就是将网络序转为十进制,如果我们只知道主机序的话,可以先将主机序转为网络序,再用主机序转为点分十进制,即为这样:inet_ntoa( htonl(主机序) )
char szbuf[1024] = {0};
hostent *phosttent = NULL;
struct in_addr addr;
if(!gethostname(szbuf,1024)) //获取主机名
{
phosttent = gethostbyname(szbuf); //通过主机名的
if(phosttent->h_addr_list[0]) //返回对应于给定主机名的包含主机名字和地址信息的hostent结构的指针
{
addr.s_addr = *(u_long *) phosttent->h_addr_list[0]; //得到本机的第一个ip(自己的电脑同一时间可能会有好几个ip)
m_IPaddress.SetAddress(ntohl(addr.s_addr) ); //MFC中一个显示IP的控间
}
}