1 网络地址的初始化与分配
1.1 将字符串信息转换为网络字节序的整数型
1.2 网络地址初始化
1.3 客户端地址信息初始化
1.4 INADDR_ANY
sockaddr_in中保存地址信息的成员为32位整数型,因此为了分配IP地址,需将其表示为32位整数型数据。
inet.h库中的函数可以帮助我们将字符串形式的IP地址转换成32位整数型数据,这个函数在转换类型的同时会附带进行网络字节序转换。
#include
in_addr_t inet_addr(const char * string);
//成功时返回32位大端序整数型值,失败时返回INADDR_NONE。
以下是一个对这个函数使用的例子:
#include
#include
int main(int argc, char *argv[])
{
char *addr1="127.212.124.78";
char *addr2="127.212.124.256";
unsigned long conv_addr=inet_addr(addr1);
if(conv_addr==INADDR_NONE)
printf("Error occured! \n");
else
printf("网络字节序地址: %#lx \n", conv_addr);
conv_addr=inet_addr(addr2);
if(conv_addr==INADDR_NONE)
printf("Error occureded \n");
else
printf("网络字节序地址: %#lx \n\n", conv_addr);
return 0;
}
运行结果:
从运行结果可以看出,inet_addr函数不仅可以把IP地址转成32位整数型,而且可以检测无效
的IP地址 。另外从输出结果可以验证函数协助完成了转换网络字节序。
接下来就是inet_aton函数了,这个函数与inet_addr函数在功能上完全相同,也将字符串IP地址转换为32位网络字节序整数并返回,但该函数可以直接将字节序传入到in_addr结构体。
#include
int inet_aton(const char * string, struct in_addr * addr);
// 一步成功时返回1(true),失败时返回0(false)
// string 含有需转换的IP地址信息的字符串地址值
// addr 将保存转换结果的in_addr结构体变量的地址值
在编程中若调用的是inet_addr函数,则需将转换后的IP地址带入到sockaddr_in结构体中声明的in_addr结构体变量。而inet_aton函数则不需此过程。(注意!!这个函数在Windows中不存在)
代码例子:
#include
#include
#include
void error_handling(char *message);
int main(int argc, char *argv[])
{
char *addr="127.232.124.79";
struct sockaddr_in addr_inet;
if(!inet_aton(addr, &addr_inet.sin_addr))
error_handling("Conversion error");
else
printf("网络字节序地址: %#x \n", addr_inet.sin_addr.s_addr);
return 0;
}
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
最后,还有一个与inet_aton函数相反的函数,这个函数可以把网络字节序整数型IP地址转换成为字符串形式。
#include
char * inet_ntoa(struct in_addr adr)
//成功时返回转换的字符串地址值,失败时返回-1
该函数将通过参数传入的整数型IP地址转换为字符串格式并返回 。但调用时返回值类型为char指针。返回字符串地址意味着字符串已保存到内存空间,但该函数并未开辟持久的内存空间将数据进行存放 。也就是说,调用完该函数后,应立即将字符串信息复制到其他内存空间。因为若再次调用inet_ntoa函数,则有可能覆盖之前保存的字符串 。若需要长期保存,则应将字符串复制到其他内存空间。下面给出该函数调用示例:
#include
#include
#include
int main(int argc, char *argv[])
{
struct sockaddr_in addr1, addr2;
char *str_ptr;
char str_arr[20];
addr1.sin_addr.s_addr=htonl(0x1020304);
addr2.sin_addr.s_addr=htonl(0x1010101);
str_ptr=inet_ntoa(addr1.sin_addr);
strcpy(str_arr, str_ptr);
printf("点分十进制网络地址1: %s \n", str_ptr);
inet_ntoa(addr2.sin_addr);
printf("点分十进制网络地址2: %s \n", str_ptr);
printf("点分十进制网络地址3: %s \n", str_arr);
return 0;
}
运行结果:
套接字创建过程中常见的网络地址信息初始化流程
上述网络地址信息初始化过程主要针对服务器端而非客户端。给套接字分配IP地址和端口号
主要是为下面这件事做准备 :
“请把进入IP 211 .217.168.13 、9190端口的数据传给我 ! "
而客户端中连接请求如下 :
“请连接到 IP 211.217.168.13、9190端口!”
请求方法不同意味着调用的函数也不同。服务器端的准备工作通过bind 函数完成,而客户端则通过connect 函数完成 。 因此,函数调用前需准备的地址值类型也不同 。服务器端声明sockaddr_in结构体变量,将其初始化为赋予服务器端IP和套接字的端口号,然后调用bind函数;而客户端则声明sockaddr_in结构体, 并初始化为要与之连接的服务器端套接字的IP和端口号,然后调用connect函数。
每次创建服务器端套接字都要输入IP地址会有些繁琐,此时可用以下初始化地址方式。
与之前方式最大的区别在于:利用常数lNADDR ANY分配服务器端的IP地址,则可自动获取运行服务器端的计算机IP地址,不必亲自输入。 若同一计算机中已分配多个IP地址(多宿主 Multi-homed 计算机, 一般路由器属于这一类) ,则只要端口号一致,就可以从不同 IP地址接收数据 。因此,服务器端中优先考虑这种方式。而客户端中除非带有一部分服务器端功能,否则一般不会采用这种方式。