网络程序设计第4章文档格式.docx

上传人:b****2 文档编号:15286388 上传时间:2022-10-29 格式:DOCX 页数:29 大小:51.49KB
下载 相关 举报
网络程序设计第4章文档格式.docx_第1页
第1页 / 共29页
网络程序设计第4章文档格式.docx_第2页
第2页 / 共29页
网络程序设计第4章文档格式.docx_第3页
第3页 / 共29页
网络程序设计第4章文档格式.docx_第4页
第4页 / 共29页
网络程序设计第4章文档格式.docx_第5页
第5页 / 共29页
点击查看更多>>
下载资源
资源描述

网络程序设计第4章文档格式.docx

《网络程序设计第4章文档格式.docx》由会员分享,可在线阅读,更多相关《网络程序设计第4章文档格式.docx(29页珍藏版)》请在冰豆网上搜索。

网络程序设计第4章文档格式.docx

应用进程的数据在沿协议栈向下传递时,以某种形式复制到内核的缓存中,当数据链路层把数据传出去后这个副本就被丢弃。

当应用进程调用write时,内核中的UDP从应用进程的缓存中复制所有数据到内存缓存中。

UDP简单地加上它的8字节首部以构成PDU并把PDU传递给IP。

IP再往下传递。

如果UDP应用进程发送一个大的数据报,它比TCP应用更有可能在底层被分片,因为TCP会把应用进程数据划分成MSS大小的块,但UDP却没有这种机制。

从写UDP插口的write调用成功地返回表示数据报或所有报文段已被加入到链路层的输出队列。

如果输出队列中没有足够的空间来存放数据报或它的某些报文段,UDP将返回错误ENOBUFS给应用进程。

需要说明的是,有些UDP的实现并不返回这种错误,在这种实现中,甚至数据报还没有发送就被丢弃的情况应用进程也不知道。

因此,使用UDP通信时,应用进程发送的数据不能太长。

4-2基本UDP插口编程

4-2-1UDP编程模式

图4-1给出了典型的UDP客户-服务器程序所使用的插口函数。

对UDP应用而言,如果从通信的角度来看,客户服务器的概念是比较模糊的:

任何时候,发送数据的一方可以认为是客户,接收数据的一方是服务器,而不像TCP应用,发起连接的一方为客户,而接收连接的一方称为服务器,自始至终都保持这种关系。

当然,可以从应用功能的角度,将提供服务的一方称为服务器,而将申请服务的一方称为客户。

图4-1UDP客户-服务器程序的一般通信过程

基于UDP的客户与服务器在通信之前不需要建立连接。

在数据传送完成之后,需要关闭插口,释放网络资源。

在大多数情况下,UDP客户和服务器方调用的插口函数是一样的,按时间顺序如下:

(1)打开一个插口(socket)。

(2)将插口绑定到指定地址和端口上(bind)。

(3)进行数据交换(sendto,recvfrom)。

(4)数据交换完成,关闭连接(close)。

在某些情况下,UDP通信两方中一方可以不绑定指定地址和端口,而是由内核自动分配,但至少有一方是要显示绑定地址和端口的。

而且,不绑定地址的一方必须首先向绑定地址的一方(服务器)发送数据,绑定地址和端口的一方从接收到的数据报中获取发送方的地址和端口,用于向没有显式绑定地址和端口的一方发送数据。

但大多数情况下,UDP应用都显式地将插口绑定到指定地址和端口上。

通常情况下,UDP客户-服务器间数据交换使用的系统调用为sendto和recvfrom。

这两个调用均可指定或返回对方的地址。

如果recvfrom的参数源地址参数from是空指针,则相应的源地址长度参数addrlen也必须是空指针,这表示我们并不关心发送数据方的协议地址。

这两个函数也可用于TCP,但一般不需要这么使用。

我们在第2章中已详细讨论了这两个函数。

另外,尽管UDP是面向无连接的通信,但是UDP应用也可以调用connect()函数。

但是,此处的connect()的功能并不是向对方发起连接请求(启动三次握手过程),内核只是记录connect()调用中指定的对方的IP地址和端口号,并立即返回给调用进程。

在这种情况下,UDP应用使用的数据交换函数就不再是sendto和recvfrom,而是使用TCP应用常用的write(或send)和read(或recv)。

我们将在4-2-4节中详细讨论这种情况。

图4-1只显示了一个简单的UDP客户-服务器的处理过程。

实际的网络应用程序可能要比此过程复杂得多。

如,需要改变插口行为时需要调用setsockopt(),需要获取插口信息时需要调用getsockopt()等等,最多时可能要用到第2章中介绍的所有与插口编程有关的函数。

下面我们就从这个最简单的客户-服务器模式开始介绍如何实现利用UDP的通信。

4-2-2实例

本节用一个具体的例子来介绍UDP插口编程。

我们重写第3章中的测试TCP性能的客户-服务器程序,改为测试UDP性能的客户-服务器程序。

我们仍使用第3章中的头文件common.h,但要增加一个宏定义:

#defineCLIENT_PORT3000/*客户进程端口号*/

下面给出客户程序。

程序4-1阻塞方式下的UDP客户程序udp_client.c。

1#include"

common.h"

2

3charbuff[MAXBUFSIZE];

/*发送和接收缓存*/

4intsend_size[5]={10,100,1000,10000,100000};

/*发送数据大小*/

5

6main(argc,argv)

7intargc;

8char*argv[];

9{

10intsockfd=0;

/*插口描述符*/

11structsockaddr_insrcaddr,destaddr;

/*源地址,目的地址*/

12intalen;

/*地址长度*/

13intslen,rlen,len;

/*发送和接收长度变量*/

14intrdt;

/*记录RoundTrip时间*/

15inti,j;

16structtimevaltv1,tv2;

17

18if(argc<

2)

19{

20printf("

使用方法:

udp_client<

server_IP_address>

\n"

);

21exit(0);

22}

23

24/*产生UDP插口*/

25if((sockfd=socket(AF_INET,SOCK_DGRAM,0))<

0)

26{

27printf("

产生插口失败,退出\n"

28exit

(1);

29}

30alen=sizeof(structsockaddr_in);

/*地址结构长度*/

31/*由系统自动绑定一个IP地址*/

32bzero((char*)&

srcaddr,alen);

33srcaddr.sin_family=AF_INET;

34srcaddr.sin_addr.s_addr=htonl(INADDR_ANY);

35srcaddr.sin_port=htons(CLIENT_PORT);

36/*绑定插口到本地的指定地址*/

37if(bind(sockfd,(structsockaddr*)&

srcaddr,alen)<

38{

39printf("

绑定插口失败,退出\n"

40exit

(2);

41}

42

43bzero((char*)&

destaddr,alen);

44destaddr.sin_family=AF_INET;

45destaddr.sin_port=htons(SERVER_PORT);

46destaddr.sin_addr.s_addr=inet_addr(argv[1]);

47

48for(i=0;

i<

5;

i++)/*测试不同大小的报文的RoundTrip时间*/

49{

50slen=send_size[i];

/*本次测试数据大小*/

51gettimeofday(&

tv1,(structtimezone*)0);

52

53for(j=0;

j<

50;

j++)/*每一种大小测试50次*/

54{

55if((rlen=sendto(sockfd,buff,slen,0,

56(structsockaddr*)&

destaddr,sizeof(destaddr)))<

57{

58printf("

发送失败,程序退出,j=%derrno=%d\n"

j,errno);

59close(sockfd);

60exit

(1);

61}

62bzero((char*)&

srcaddr,sizeof(srcaddr));

63if((rlen=recvfrom(sockfd,buff,slen,0,

64(structsockaddr*)&

srcaddr,&

alen))<

0)

65{

66printf("

接收失败,程序退出,errno=%d\n"

errno);

67close(sockfd);

68exit

(2);

69}

70}

71gettimeofday(&

tv2,(structtimezone*)0);

72rdt=((tv2.tv_sec-tv1.tv_sec)*1000000+

73tv2.tv_usec-tv1.tv_usec)/50;

74printf("

报文大小=%d,RoundTripTime=%d微秒\n"

slen,rdt);

75}

76close(sockfd);

77}

第25行打开一个UDP插口,插口类型为SOCK_DGRAM。

第32—41行将插口绑定到端口CLIENT_PORT上。

如上一节所述,客户进程也可以不显式绑定到指定地址和端口,但要求通信由客户进程先发起。

第43—46行填写目的地址结构,用于sendto函数。

第46行中使用了地址转换函数inet_addr(),而没有使用inet_aton()。

因为在Solaris2.5中,不支持函数inet_aton()。

第55—61行发送指定长度的数据到目的地址。

如果发送失败,则进程退出。

如果是非阻塞方式,则需要改写错误处理部分。

如果在sendto调用中不指定目的地址(没有对UDP插口调用connect()的情况下),则返回的错误代码取决于实现:

在4.4BSD中,将返回EDESTADDRREQ,而Posix.1g中则返回ENOTCONN。

第62—69行接收数据报。

第62行初始化数据源地址结构变量,如果要从指定源地址接收数据报,则需要进一步设置该变量中的相关成员(地址和端口号)。

此处的设置表明可以接收从任何主机上的任意UDP端口上发送来的数据报。

下面给出UDP服务器程序。

程序

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 医药卫生 > 基础医学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1