特爱听

Chapter 16 -1 

网络编程

11.4 UDP套接口编程

 

UDP 工作流程:

 

 

基本函数介绍:

 

 

 

 

 

EG

Server 

#include<stdio.h>

#include<sys/types.h>

#include<netinet/in.h>

#include<errno.h>

#include<string.h>

#define SERVER_PORT 8888

#define MAX_MSG_SIZE 1024

 

int main(void)

{

 

int sockfd  , addrlen, n ;

struct sockaddr_in addr  ;

char msg[MAX_MSG_SIZE] ;

 

sockfd = socket(AF_INET,SOCK_DGRAM,0);

if(sockfd<0)

{

fprintf(stderr,"Socket Error :%s\n",strerror(errno));

return 1;

}

 

addrlen =sizeof(struct sockaddr_in);

// bzero =sizeof(&addr , addrlen);

    bzero(&addr,addrlen);

 

/*fill server sockadd structure*/

addr.sin_family = AF_INET;

addr.sin_addr.s_addr = htonl(INADDR_ANY);

addr.sin_port = htons(SERVER_PORT);

 

if(bind(sockfd , (struct sockaddr *)(&addr),addrlen)<0)

{

fprintf(stderr,"Bind Error :%s \n",strerror(errno));

return 1;

}

/*read the data from netwok and print the data  */

while(1)

{

bzero(msg , MAX_MSG_SIZE );

n = recvfrom(sockfd , msg , sizeof(msg),0,(struct sockaddr *)(&addr),&addrlen);

 

fprintf(stderr,"Recieve messagee from client :%s \n",msg);

 

/* Read the date terminate input by user, sent to the network*/

 

fgets(msg,MAX_MSG_SIZE,stdin);

 

printf("Server endpoint input message :%s \n",msg);

sendto(sockfd,msg,n,0,(struct sockaddr*)(&addr),addrlen);

}

close(sockfd);

return 0 ;

}

Client

#include<stdio.h>

#include<sys/types.h>

#include<sys/socket.h>

#include<netinet/in.h>

#include<errno.h>

#include<stdio.h>

#include<unistd.h>

#define MAX_BUF_SIZE 1024

int main( int argc ,char *argv [])

{

 

int sockfd  ,port ,addrlen , n ;

char buf[MAX_BUF_SIZE] ;

 

struct sockaddr_in addr ;

 

if(argc !=3 )

{

fprintf(stderr,"Usage :%s sever_ip server_port \n",argv[0]);

return 1 ;

}

 

if( (port = atoi(argv[2]))<0 )

{

 

fprintf(stderr,"Usage :%s server_ip server_port \a\n",argv[0]);

return 1;

 

}

 

sockfd=socket(AF_INET,SOCK_DGRAM,0);

 

if(sockfd <0 ) {

 

fprintf(stderr,"Socket Error:%s\n",strerror(errno));

return 1 ;

}

 

addrlen = sizeof(struct sockaddr_in);

 

bzero(&addr,addrlen);

 

addr.sin_family = AF_INET ;

addr.sin_port = htons(port);

/**/

if(inet_aton(argv[1],&addr.sin_addr)<0)

{

    fprintf(stderr,"Ip Error :%s\n",strerror(errno));

    return 1 ;

}

 

while(1)

{

            bzero(buf,MAX_BUF_SIZE);

            fgets(buf,MAX_BUF_SIZE,stdin);

            sendto(sockfd , buf , strlen(buf),0,(struct sockaddr *)(&addr),addrlen);

            printf("Client endpoint input message :%s\n",buf);

 

            n = recvfrom(sockfd, buf , strlen(buf),0,(struct sockaddr*)(&addr),addrlen);

            fprintf(stdout,"Recervie message from server :%s\n",buf);

 

    }

 

    close (sockfd);

    return 0 ;

}

 

11.5 原始套接口编程

需要注意的是,原始套接口只能够由有root权限的用户创建。相比于TCPUDP套接口,原始套接口具有以下功能

n .使用原始套接口可以读/ICMP(互联网控制消息协议)ICMPv6分组,如ping

  就使用原始套接口发送ICMP应答请求。

n .使用原始套接口可以读/写特殊的IP数据包,内核不处理这些数据包的IP协议字段,

而出错的诊断将依靠协议字段的意义。

n .利用原始套接口通过设置IP_HDRINCL套接口选项可以构造自己的IP头部。

 

 

基于原始套接口编程的相关系统调用与TCPUDP套接口相同,比如函数socke(),

bind(), connect()等都能使用,下面简单介绍原始套接口的创建之后,给出一个其体的实例来

说明它的使用方法。

 

 

 

 

ICMP协议简介 

 要直正了解ping命令实现原理,就要了解ping命令所使用到的TCP/IP协议。ICMP

(Internet Control Message,网际控制报文协议)是为网关和目标主机而提供的一种差错控制机制,使它们在遇到差错时能把错误报告给报文源发送方。ICMP协议是IP层的一个协议,但是由于差错报告在发送给报文源发送方时可能也要经过若干子网,因此牵涉到路由选择等问题,所以ICMP报文需通过IP协议来发送。ICMP数据报的数据发送前需要两级封装:

首先添加ICMP报头形成ICMP报文,再添加IP报头形成IP数据报,封装过程如图11.10所示。

 

IP报头格式

   由于IP层协议是一种点对点的协议,而非端对端的协议,它提供无连接的数据报服务,没有建立端口连接的概念,因此很少使用bindOconnectO函数,若有使用也只是用于设置IP地址.,发送数据使用sendto()函数,接收数据使用recvfromO 函数.IP报头格式如图

 

 

linuxIP报头格式数据

struct ip {

#ifdef _IP_VHL

u_char ip_vhl; /* version << 4 | header length >> 2 */

#else

#if BYTE_ORDER == LITTLE_ENDIAN

u_int ip_hl:4, /* header length */

ip_v:4; /* version */

#endif

#if BYTE_ORDER == BIG_ENDIAN

u_int ip_v:4, /* version */

ip_hl:4; /* header length */

#endif

#endif /* not _IP_VHL */

u_char ip_tos; /* type of service */

u_short ip_len; /* total length */

u_short ip_id; /* identification */

u_short ip_off; /* fragment offset field */

#define IP_RF 0x8000 /* reserved fragment flag */

#define IP_DF 0x4000 /* dont fragment flag */

#define IP_MF 0x2000 /* more fragments flag */

#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */

u_char ip_ttl; /* time to live */

u_char ip_p; /* protocol */

u_short ip_sum; /* checksum */

struct in_addr ip_src,ip_dst; /* source and dest address */

};

 

其中,ping程序只使用以下数据:

IP报头长度IHL ( Internet Header Length):4字节为一个单位来记录IP报头的长

度,是上述IP数据结构的ipee hl变量。

生存时间TTL (Time To Live):以秒为单位,指出Ip数据报能在网络上停留的最长

时间,其值由发送方设定,并在经过路由的每一个节点时减1,当该值为0时,数据报将被丢失,是上述IP数据结构的ip_ttl 变量

 

 

ICMP报头格式

 

 ICMP报文分为两种,一种是错误报告报文,另一种是查询报文。ICMP差错报告报文又有五中:

Ø 终点不可达。

Ø 源站抑制。

Ø 时间超过。

Ø 参数问题。

Ø 改变路由(重定向)

ICMP的查询报文又分为4:

ü 回送请求和回答报文。

ü 时间戳请求和回答报文。

ü 掩码地址请求和回答报文。

ü 路由器询问和通告报文。

 

    ICMP的报头共有8字节,前4个字节采用统一的格式,共有3个字段,即类型、代码

和检验和,长度分别为8位、8位和16位。接着的4个字节的内容与ICMP的类型有关。    ping命令只使用众多ICMP报文中的两种:请求回送(ICMP ECHO)和请求回应

(ICMP ECHOREPLY )。在Linux中的定义如下(<netinet/ip icmp.h>):

#define ICMP_ECHO 0 

#define ICMP_ECHOREPLY 8

这两种ICMP类型报头格式如图:

 

 

linux中,ICMP数据结构的定义如下(取至<netinet/ip_icmp.h>

使用宏定义令表达更简洁,其中ICMP报头为字节,数据报长度最大为64k字节。

 

下面介绍几个在本小节的实例程序中将要用到的概念:

校验和算法:这一算法称为网际校验和算法,把被校验的数据进行16位累加,然后

取反码.若数据字节长度为奇数,则数据尾部补上一个字节的0以凑成偶数。此算法适用于IPv4, ICMPv4, IGMPv4, ICMPv6. UDPTCP校验和,更详细的信息请参考RFC 1071,校验和字段为上述ICMP数据结构的icmp_cksum变量

标识符:用于唯一标识ICMP报文,为上述ICMP数据结构的icmp id宏所指的变量。

顺序号:ping命令的icmp seq便由这里读出,代表ICMP报文的发送顺序,为上迷

ICMP数据结构的icmpee seq宏所指的变量。

:

    在本实例中,ping命令需要显示的信息,包括icmp- seqttI都已有实现的办法,但还

缺少往返时间rtt的算法实现。为了实现这一功能,可利用ICMP数据报携带一个时间戳。使用下列函数生成时间截。

 

#include<sys/time.h>

#include<unistd.h>

Int gettimeofday( struct timeval *tv , void *tz);

 

   返回:若成功则返回0,若失败返回一I。错误代码存于ermo.

   gettimeofday()函数会把目前的时间由tv所指向的结构体timeval返回,当地时区的信息

则放到tz所指向的结构体timezone中。

    其中timeval结构的定义如下(细心的读者会发现,我们在本书的9.2.2小节中已经介绍

过这个结构):

 

 

    timeval结构中,tv_sec为秒数,tv_usec为微秒数.在发送和接收报文时由gettimeofday

分别生成两个timeval结构,两者之差即为往返时间,即ICMP报文发送与接收的时间差,而timeval结构由ICMP数据报携带,tz指针表示时区,一般都不使用,赋NULL值。

    然后,系统自带的ping命令当它接送完所有ICMP报文后,会对所有发送和所有接收的ICMP报文进行统计,从而计算ICMP报文丢失的比率。为达此目的,定义两个全局变最:

接收计数器和发送计数器,用于记录ICMP报文接受和发送数目。丢失数目=发送总数一接

收总数,丢失比率=丢失数目/发送总数。

 

Ping 源代码

 

 

 

posted on 2013-04-08 12:57  laohan_  阅读(243)  评论(0)    收藏  举报

导航