牵着老婆满街逛

严以律己,宽以待人. 三思而后行.
GMail/GTalk: yanglinbo#google.com;
MSN/Email: tx7do#yahoo.com.cn;
QQ: 3 0 3 3 9 6 9 2 0 .

处理Linux网络编程中的IP地址

作者:曹元其 来源:开放系统世界——赛迪网
     Linux网络服务能力非常强大,它的TCP/IP代码是最高级的。Linux的网络实现是模仿FreeBSD的,它支持FreeBSD的带有扩展的Sockets(套接字)和TCP/IP协议。它支持两个主机间的网络连接和Sockets通讯模型,实现了两种类型的Sockets:BSD Sockets和INET Sockets。它为不同的通信模型和服务质量提供了两种传输协议,即不可靠的、基于消息的UDP传输协议和可靠的、基于流的传输协议TCP,并且都是在IP网络协议上实现的。INET sockets是在以上两个协议及IP协议之上实现的。它们之间的关系见图1所示。

图1 Linux网络层

Socket在网络编程中的实现

套接字是网络通信的基本构件,它提供了不同主机间进程双向通信的端点。如同电话一样,只有当一方拨通另一方的电话时,双方才能建立对话,套接字就好比是双方的电话。通过Sockets编程,程序可以跳过复杂的网络底层协议和结构,直接编制与平台无关的应用程序。随着Internet的广泛应用,Sockets已逐渐成为网络编程的通用接口。

套接字存在于特定的通信域(即地址族)中,只有隶属于同一地址族的套接字才能建立对话。Linux支持AF_INET(IPv4协议)、AF_INET6(IPv6协议)和AF_LOCAL(Unix域协议)。

Linux支持以下的socket families或domain:

◆ Unix domain sockets;

◆ INET TneIntemet address family supports communications via;

◆ TCP/IP protocols;

◆ Amateur radio X.25;

◆ Novel IPX;

◆ Appletalk DDP;

◆ X.25。

套接口就是网络进程的ID。网络通信也是一种进程的通信,两个网络进程通信时首先要确定各自所在网络节点的网络地址(IP地址)。网络地址可以确定进程所在的计算机,一台计算机上可能同时有多个网络进程。为了区别不同的进程,套接口中还需要端口号(Port)信息。在一台计算机中,一个端口一次只能分配给一个进程。所以在一台计算机中,端口号和进程可以惟一确定整个Intemet中的一个网络进程。可以认为,套接口=网络地址+端口号。

Linux网络数据结构

在网络实际传送的数据中,有两种字节排列顺序:重要的字节在前面,或者不重要的字节在前面。前一种叫网络字节顺序(Network Byte Order,NBO),有些机器在内部是按照这个顺序储存数据的。当某数据必须按照NBO顺序时,那么要调用函数(例如htons())将它从本机字节顺序(Host Byte Order,HBO)转换过来,否则传送过去的数据将使对方机器不可读。这点对于网络数据传送来说是非常关键的。

在网络中第一个被创造的结构类型是sockaddr。这个数据结构是为许多类型的套接口储存地址信息。它的定义如下:

																struct sockaddr{
  unsigned short  sa_family; /*这个是地址族,通常是AF-xxxx的形式*/
  char  sa_data[14];     /*14字节的地址信息*/
  };
														

上面代码中,sa_famdly是“AF_INET”,表示它使用的是Internet地址族;sa_data用于为套接口储存目标地址和端口信息。

为了解决struct sockaddr,创造了一个并列的结构struct sockadd_in(“in”代表“Internet”),如下所示:

																struct sockaddr_in{
short int   sin_family;  /*地址族信息,通常是AF-xxxx的形式*/
unsigned short    int sin_port; /*端口信息*/
struct in_addr    sin_addr;     /*网络地址*/
unsigned char     sin_zero[8];  /*补位用的0*/
}
														

上面这个数据结构可以轻松处理套接口地址的基本元素。需要解释的是,sin_zero被加入到这个结构中主要是为了保证struct sockaddr的数据长度和struct sockaddr_in的一样,这样在使用标准函数时,就可以使用统一的数据接口。需要注意的是,应该使用函数bzero()将sin_zero全部置零。最后,sin_port和sin_addb必须是网络字节顺序(Network Byte Order)。如果声明“inadd”是数据结构stmct sockaddr_in的实例,那么inadd.sinadd.s_addr就储存了4个字节的IP地址(网络字节顺序)。

另一个常用到的是unsigned类型。它比上面介绍的struct sockaddr_in或struct sockaddr用得更普遍。对于变量类型unsigned,可以使用的两种类型是short(两个字节)和long(四个字节)。假设想将short从本机字节顺序转换为网络字节顺序,需用“h”表示本机(host),用“to”表示进行转换,然后用“n”表示网络,用“s”表示short,那么就是h-to-n-s或者htons()(“Host to Network Short”)。

考虑到对不同机器的可移值性,这样的转换是必需的。我们对“n”、“h”、“s”和“l”这几个字母进行组合,就可以得到Linux下的全部转换函数。

IP地址在Linux网络中的处理方法

假设使用struct sockaddr_in ina,想将IP地址“164.112.175.124”储存到其中,那么所要做的是调用函数inet_addr(),转换上面“数字 + 句点”格式的IP地址到unsigned long中。这个工作可以这样来做:

																ina.sin_addr.s_addr=inet_addr(”164.112.175.124”);
														

inet_addr()返回的地址已经是按照网络字节顺序的,不用调用htonl()。在发生错误的时候inet_addr()返回-1。调用后,需使用正确的错误检查,比如说当IP地址为255.255.255.255的时候,返回的就是(unsigned)-1。因为这是个广播地址,你的程序必需能够将这类错误捕获出来。

你现在就可以转换字符串形式的IP地址为1ong了。若有一个数据结构struct in_addr,按照“数字+句点”格式打印时,你要用函数inet_ntoa()(ntoa意思是network to ascⅡ),如下所示:

																printf(“%s”,inet_ntoa(ina.sin_addr));
														

这样就可以打印IP地址。注意:函数inet—ntoa()的参数是struct in_addr,而不是long,它返回的是一个指向字符的指针。

在inet_ntoa内储存了字符数组,因此它每次调用inet_ntoa()的时候将覆盖以前的内容。

例如:

																Char a1,  *a2;
......
a1=inet_ntoa(ina1.sin_addr); /*假设地址是;164.112.175.124*/
a2=inet_ntoa(ina2.sin_addr);/*假设地址是:202.112.58.200*/
printf(“address 1:%s\n”,a1);
printf(“address 2:%s\n”,a2);
														

上面运行结果是:

																address l:202.112.58.200
address 2:202.112.58.200
														

如果想保存地址,那么可用strcpy()保存到自己的字符数组中。

以上介绍了Linux网络编程的基础知识和对网络IP地址处理的一些技巧。如果能够将其同Linux下众多的小工具整合在一起的话,那么所开发出来的程序的功能已经不亚于一些专业的软件了。

posted on 2006-04-29 17:36 杨粼波 阅读(281) 评论(0)  编辑 收藏 引用 所属分类: 文章收藏


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理