UNIX的网络通信初步.ppt

上传人:小飞机 文档编号:6522532 上传时间:2023-11-08 格式:PPT 页数:107 大小:427.50KB
返回 下载 相关 举报
UNIX的网络通信初步.ppt_第1页
第1页 / 共107页
UNIX的网络通信初步.ppt_第2页
第2页 / 共107页
UNIX的网络通信初步.ppt_第3页
第3页 / 共107页
UNIX的网络通信初步.ppt_第4页
第4页 / 共107页
UNIX的网络通信初步.ppt_第5页
第5页 / 共107页
点击查看更多>>
资源描述

《UNIX的网络通信初步.ppt》由会员分享,可在线阅读,更多相关《UNIX的网络通信初步.ppt(107页珍藏版)》请在三一办公上搜索。

1、第六章 UNIX的网络通信初步,(1)UNIX操作系统为进程通信提供了相应设施,如管道(pipe)、命名管道(named pipe)和软中断信号(signal),消息(message)、共享存储区(shared memory)和信号量(semaphore)等,但这些都只限于用在本机进程间的通信。(2)为了实现计算机全面联网与信息的异地处理,需要为用户构建Client/Server 应用的通讯结构,通过网络接口编程,以解决不同主机进程间的通信问题。,6.1 网络接口,在UNIX系统中,网络接口有两类:一类是源自BSD UNIX的Sockets(套接口),另一类是UNIX System V 的TL

2、I(Transmission Layer Interface)。TLI是根据工业标准“ISO传输服务定义(ISO 8072)”实现的,由于SVR3只包括了流以及TLI构建模块而并没有任何的如TCP/IP之类的协议,因此TLI具有与协议无关性,关键技术是定义了一组对许多传输协议公共的服务。目前TLI的修正版XTL在UNIX系统中仍然得到广泛的使用。Socket API是基于各种传输协议之上的,目前已经成为网络编程的既成事实标准。基于Sockets API的通用性,本章只讨论Sockets API的应用。,目前最通用的提供远程进程间通信的API是伯克利套接字(Berkeley socket)接口。

3、所谓的套接字是一种抽象数据结构,用以创建一条在没有相关联的进程间发送、接收消息的通道(连接点)。这些进程在通信前各自建立一个Socket,并通过对Socket的读写操作实现通信功能。当使用基于套接字的连接时,服务器端进程创建一个套接字,并把它映射到一个本地地址上,然后等待(监听)客户端的请求。客户端进程也创建自己的套接字,并确定服务器端的具体位置(比如主机名,端口号等)。依靠传输连接方式的应答,客户端进程就可以开始发送和接收数据,而不用管是否接收到服务器进程的正式确认(应答)。,每个套接字都有其类型和一个与之相连的进程。当应用程序创建套接字时,套接字系统调用返回句柄,即所谓套接字描述字,它和文

4、件描述字是有所区别的:当文件描述字,由open命令创建时,它被耦合到特定的文件或设备;当套接字描述字由Socket命令创建时,它并不被耦合到任何位置。当套接字用作面向连接的网络传输接口时,应用程序可用bind命令将套接字明确地耦合到一个地址。当套接字用作无连接的网络传输接口时,应用程序可以在用sendto命令发送数据报时动态地提供地址。,6.1.1 套接口的类型,UNIX 提供下列四种类型的socket:数据流套接字(SOCK_STREAM),它提供双向的、面向连接的、可靠的、有序的并且不重复的无记录边界数据流。一对相连的流Socket提供几乎类似于管道的接口。流式socket针对于TCP服务

5、应用,如文件传送协议(FTP)。数据流套接字采用TCP协议,这是个有连接的协议,在数据正式传输前必须建立连接,此连接是个稳定的双向线路,可以保证提供无错误的传送管道,因为只要封包在传送过程发生错误损毁、次序错乱或送错,TCP将会察觉问题并要求重新发送数据,因此适合在需要大量的数据传输并要求完全正确的状况时使用。,6.1.1 套接口的类型,数据报套接字(SOCK_DGRAM),它也支持双向数据流,但数据以独立包形式被发送,无可靠性保证、无序、数据可能丢失或重复。数据报socket提供一个无连接服务,对应于无连接的UDP服务应用,如网络文件系统(NFS)、组播通信。数据报套接字采用UDP协议,这是

6、个无连接的协议,发送主机直接将封包送至目的主机,无需事先建立连接。因为避免了建立连接所需的高代价,采用数据报方式效率较高,但数据报方式自身不能处理数据传输过程出现的错误,因此使用数据报方式的应用程序必须自己处理这些问题。一般在比较简单的网络应用程序中使用数据报方式。,6.1.1 套接口的类型,原始套接字(SOCK_RAW),它提供对支持socket概念的基本通信协议的访问。该接口允许用户访问支持套接字抽象的底层通信协议,如IP、ICMP直接访问,常用于检验新的协议实现或访问现有服务中配置的新设备。顺序报套接字:这种类型的套接字类似数据流套接字,不同的是其传送的数据具有记录边界。,6.1.2 套

7、接口支持的协议,1套接字协议簇(family)(1)AF-UNIX:UNIX Domain协议,在该域中创建的套接字只能为同在一个主机的进程所用;(2)AF-INET:Internet协议,这个域里的套接字允许在不同主机上的不相关的进程间进行通讯;2套接字协议(Protocol)(1)TCP协议(传输控制协议):负责保证两台主机之间传输的分组到达目的地,保证分组以正确的顺序(确切的说,分组按正确的顺序重新编排)并无差错地到达目的地。当分组在两台主机间的路径上丢失时,TCP协议确保重传丢失的分组;(2)UDP协议(用户数据报协议):类似TCP协议,但是它是不可靠的。UDP不对分组进行检查、重新排

8、序和重传;,6.1.2 套接口支持的协议,6.1.3 套接口地址结构,大多数套接口函数都需要一个指向套接口地址结构的指针作参数,而在实际应用中各协议的地址结构是不同的,例如IPv4是32位的,而IPv6则是128的。由于早期定义的原因,UNIX的系统函数都只支持通用的地址结构而无法区分特定协议的地址结构,因此在调用这些函数时必须将指向具体协议的套接口地址结构的指针类型转换成指向通用套接口地址结构的类型。如serv是IPv4的地址格式,可以通过(struct sockaddr*)&serv来转换成通用地址格式。,通用套接口地址结构:(在头文件中定义)structsockaddr u_char s

9、a_len;/*地址总长度*/u_shortsa_family;/*协议族,AF_xxx*/charsa_data14;/*具体协议地址*/;其中:sa_family为套接字协议簇类型;sa_data中存储具体的协议地址,不同的协议簇有不同的地址结构,如TCP/IP协议的套接字地址结构是在文件中定义的sockaddr_in结构,这个结构定义如下:,IPv4套接口地址结构:(在头文件中定义)structsockaddr u_char sin_len;/*32位的IPv4地址总长度*/u_shortsin_family;/*协议族,AF_INET*/u_shortsin_port;/*协议端口号*

10、/charsin_zero8;/*保留,置0*/;要注意的是,套接口的地址结构是按网络字节顺序而不是按主机存储字节顺序来存储的(网络字节是从高到低的顺序,而主机是从低到高的顺序),因此,在Internet上传输数据时就需要进行转换,否则就会出现数据不一致。下面是几个常用的字节顺序转换函数:htonl():把32位值从主机字节序转换成网络字节序 ntohl():把32位值从网络字节序转换成主机字节序,通用套接口地址结构 IPv4套接口地址结构,长度 AF_INIET16位端口号 sin_portsin_zero8,长度 AF_INIET16位端口号 sin_portsa_data16,6.2 网

11、络通信的系统函数,6.2.1 建立网络连接的数据结构 一个用户为了执行网络I/O操作,它首先要调用函数socket()。这个函数陷入内核后执行sys_socket()系统调用,后者为用户创建一个名为socket的数据结构,并对其进行一些简单的初始化工作,然后返回一个小的正整数,代表这个socket结构。这个小的正整数与文件描述符功能类似,所以把它称作套接字描述符。,6.2.1 建立网络连接的数据结构,struct socket socket_state state;unsigned long flags;struct proto_ops*ops;struct inode*inode;struc

12、t fasync_struct*fasync_list;struct file*file;struct spck*sk;wait_queue_head_t wait;short type;unsigned char passcred;unsigned char tli;其中:state描述该套接字的状态信息;flags表示连接时的一些控制信息;ops指向oroto_ops 结构类型的指针;inode是指向inode 结构类型的指针;fasync_list存在异步进行处理的不同文件的指针;sk指向socket结构的指针;wait表示等待在该socket 结构上的人物列表;type表示数据包的类型

13、。,6.2.2 网络连接的建立和关闭,1 建立一个套接字描述符 函数格式如下:int socket(int family,int type,int protocol);socket()调用中有三个参数,第一个参数family是一个整数型的量,指定协议簇;第二个参数是type,表示套接字的类型;第三个参数protocol用来表示在指定协议族中使用哪种特定协议,大多情况下,该参数被设为0,让系统自己去选择基于协议族的协议。Socket()调用成功则返回一个正整数,即套接字描述符,用以标识该套接字;如果调用失败,会返回1,并设置全局变量errno为相应的错误类型。,Sockek()函数的执行流程,S

14、ocket(),Sys_socketcall(),Sys_socket(),Socket_create(),Socket_alloc(),Inet_creat(),Sk_alloc(),Sock_alloc()用来创建一个socket 结构,Sk_alloc()用来创建一个sock 结构,系统调用接口,Sock_create(.)sock_alloc();inet_creat();,例1:创建一个套接字对#include#include#include#include#include main(void)int sock2,cpid,i;/*套接字对*/static char bufBUF_S

15、Z;/*消息的临时缓冲区*/if(socketpair(PF_UNIX,SOCK_TREAM,0,sock)0)perror(“Generation error”);exit(1);switch(cpid=(int)fork()case-1:perror(“Back fork”);exit(2);case 0:/*子进程*/close(sock1);for(i=0;i 10;i+=2)sleep(1);sprintf(buf,“c:%dn”,i);write(sock0,buf,sizeof(buf);read(sock0,buf,BUF_SZ);printf(“c%s”,buf);close

16、(sock0);break;,default:/*父进程*/close(sock0);for(i=0;i 10;i+=2)sleep(1);read(sock1,buf,BUF_SZ);printf(“p%s”,buf);sprintf(buf,“p:%dn”,i);write(sock1,buf,sizeof(buf);close(sock1);return 0;,6.2.2 网络连接的建立和关闭,2 指定本机地址及端口 bind()intbind(int sockfd,structsockaddr*my_addr,int addrlen);/*0成功;-1出错*/其中:参数sockfd是调

17、用socket()返回的套接字,参数my_addr是通用地址结构指针,参数addrlen是该结构的长度,常被设置为sizeof(structsockaddr)。该函数为套接字分配一个本地协议地址,对于IP协议来说是IP地址和TCP或UDP端口号的组合。bind()调用在UNIX域中用来联系套接字和它的名字(一个文件名),在因特网域用来将本地地址和套接字绑定在一起,包括IP地址和端口号。它是依据第二个参数的值的不同而不同的;套接字和本地地址的绑定采用组合的方式,如下表,注:INADDR-ANY在UNIX系统中被映射为0的常量,6.2.2 网络连接的建立与关闭,使用bind函数时,可以用下面的赋值

18、实现自动获得本机IP地址和随机获取一个没有被占用的端口号:my_addr.sin_port=0;/*系统随机选择一个未被使用的端口号*/my_addr.sin_addr.s_addr=INADDR_ANY;/*填入本机IP地址*/,6.2.2 网络连接的建立与关闭,3 客户启动连接connect()intconnect(intsockfd,structsockaddr*serv_addr,intaddrlen);/*返回:0成功;-1出错*/sockfd是调用socket()返回的套接字;serv_addr是包含远端服务器IP地址和端口号的通用地址结构的指针;addrlen是远端地址结构的长度

19、。TCP客户用Connect函数建立一个与远端TCP服务器的连接,正是connect激发了TCP三次握手的连接过程。由于协议族总被包含在套接字地址结构的前两个字节中,并通过socket()调用与某个协议族相关。因此bind()和connect()无须协议作为参数。,connect(),用户空间,核心空间,sys_socketcall(),sys_connect(),inet_stream_coonect(),tcp_v4_connect(),ip_route_output(),ip_route_output_key(),ip_route_connect(),ip_output(),ip_rou

20、te_optput_slow(),系统调用,传输层(TCP协议),网络层(IPv4或IPv6),6.2.2 网络连接的建立与关闭,4 监听连接listen()intlisten(intsockfd,intbacklog);/*返回:0成功;-1出错*/sockfd是调用socket()建立的套接字,它是一个socket调用成功后的返回值;backlog是指定在请求队列中允许的最大请求数,一般大于5的均设为5。进入的连接请求将在队列中等待accept()调用建立与客户的连接。该函数仅被TCP服务器调用,它总是使套接口处于被动的监听模式,并为该套接口建立一个输入数据队列,将到达的服务请求保存在此队

21、列中,直到程序处理它们。如果一个服务请求到来时,输入队列已满,该套接口将拒绝连接请求并显示出错信息。listen()调用将一个尚未连接的主动套接字转换成为一个被动套接字,使其可以接收连接请求,因为由socket()调用所创建的套接字(主动套接字)只可以用来进行主动连接,不能接收连接的请求。,5 服务器接受连接accept()intaccept(intsockfd,structsockaddr*addr,int*addrlen);/*返回:0成功;-1出错*/accept()调用从倾听套接字的连接队列中接收第一个连接,生成一个新的套接字来完成客户机的要求,原来的套接字继续监视网络,等待用户的连接

22、。accept()调用的第一个参数sockfd是用来标识从哪个套接字中接收连接的,addr是一个指向客户方套接字地址结构的变量指针,该变量用来接收提出连接请求服务的客户的协议地址,指明某台主机从某个端口发出该请求;addr的确切格式由套接字创建时建立的地址族决定。addrten通常为一个指向值为sizeof(struct socka ddr)的整型指针变量,指明客户方套接字地址结构的长度(字节数)。accept()只用于TCP服务器,且在调用前应该先调用过listen()。当listen()侦听到有连接请求到达时,accept()调用将请求连接队列上的第一个客户的套接字地址及长度放入addr和

23、addrlen,并与该客户在sockfd建立连接。,6.2.2 网络连接的建立与关闭,6 关闭套接字close()close(intsockfd);参数sockfd是待关闭的套接字。close()是标准的关闭函数,在TCP服务中激发该套接字的连接关闭。函数功能只是对该套接字作“关闭标识”表明不可用,而连接的另一方还在试图发送排队的数据。只有当对方发现通信的套接字已不可用,自己也调用close()关闭本机的套接字,才真正地结束数据的发送。,6.2.3 发送数据,UNIX内核为用户提供的发送数据的系统调用有5个,它们分别是:1)write():与文件系统中的write()完全一致;2)writev

24、():与write功能相似,所不同的是writev可以在一次函数调用中写多个缓冲区(集中写)3)send():面向连接的发送数据过程(TCP);4)sendto():面向无连接的发送数据过程(UDP)5)sendmsg():直接使用msghdr 结构发送数据。在功能上可以代替以上四个输出函数。,1 发送数据的系统调用接口,write(),writev(),send(),sendto(),sendmsg(),sys_write(),sys_writev(),sys_socketcall(),sock_write(),sock_write(),sys_send(),sys_sendmsg(),sy

25、s_sendto(),sock_sendmsg(),inet_sendmsg(),系统调用,应用层,BSD 套接字接口,INET 套接字(传输层),2.从INTE协议层到IP层,inet_sendmsg(),tcp_sendmsg(),tcp_sendmsg()tcp_transmit_skb(),ip_queue_xmit(),网络层,BSD 套接字接口,INET 套接字(传输层),2.IP层到硬件层的数据发送过程,ip_queue_xmit2(),ip_output(),ip_finish_output(),ip_queue_xmit(),net/core/dev.c,IP层,dev_qu

26、eue_xmit(),neigh_resolve_output_(),ip_finish_output2(),net/ipv4/ip_et/ipv4/ip_et/ipv4/ip_et/ipv4/ip_et/ipv4/ip_et/core/neighbour.c,数据链路与硬件层,4.硬件层的数据发送过程,dev_queue_xmit(),ei_start_xmit(),qdisc_run(),ei_start_xmit(),物理设备,硬件层,net/pkt_sched.h,硬件上有队列吗?,以太网卡(例如NE2000),6.2.4 接收数据,UNIX内核为用户提供的接收数据的系统调用也是5个,

27、它们分别是:1)read():与文件系统中的read()完全一致;2)readv():与read功能相似,所不同的是readv可以在一次函数调用中读多个缓冲区;3)recv():面向连接的接收数据过程(TCP);4)recvfrom():面向无连接的接收数据过程(UDP)5)recvmsg():直接使用msghdr 结构来接收数据。在功能上可以代替以上四个输出函数。,1 接收数据的系统调用接口,read(),readv(),recv(),recvfrom(),recvmsg(),sys_read(),sys_readv(),sys_socketcall(),sock_read(),sock_r

28、eadv(),sys_recv(),sys_recvmsg(),sys_recvfrom(),sock_recvmsg(),inet_recvmsg(),系统调用,应用层,BSD 套接字接口,INET 套接字(传输层),2.硬件层接收数据分析,netif_rx(),ei_recieve(),ei_interrupt(),ip_rev(),net/core/dev.cdrivers/net/8390.cdrivers/net/8309.c,硬件层,net_rx_action(),IP层,物理设备,NE网卡,向队列写入数据,Backing接收数据队列,从队列中读出数据,2.硬件层接收数据分析,3.

29、从IP层接收数据,tcp_v4_rcv(),ip_local_deliver_finish(),ip_local_deliver(),tcp_rev_established(),INET层,net_rx_action(),ip_rev(),ip_rcv_finish(),net/ipv4/tcp_et/ipv4/ip_et/ipv4/ip_et/ipv4/input.c,硬件层,tcp_v4_do_rcv(),receive_queue队列,IP层,4.从INTE层接收数据,6.3 套接字编程方法,1 面向连接的数据流套接字时序步骤 流套接字的服务器进程和客户机进程在进行通信前必须建立一条连接

30、,其中,初始化连接的是客户端进程,接收连接的进程是服务器端的进程。建立连接和通信的主要步骤如下:(1)服务进程首先调用Socket()创建一个流套接字;(2)服务进程调用bind()公开服务器地址,将服务器地址与套接字绑定在一起;(3)服务进程调用listen()将套接字转换成倾听套接字,此时该套接字可以接收来自客户机的请求;(4)通过accept()阻塞服务进程,此时该服务器进入一个无限循环,等待客户进程建立连接;(5)客户进程也通过Socket()创建一个流套接字,然后调用connect()与服务进程建立连接;(6)当客户进程的连接请求到达服务器后,服务进程进程被唤醒,生成一个新的套接字,

31、服务进程用这个新的套接字按预先定义的协议调用read()和write()进行通信,处理客户进程的要求;而服务进程最早生成的套接字则继续用于监听网络上的服务请求;(7)处理完成后,服务进程和客户进程调用close()关闭这个连接和套接字;,服务器,Socket()、bind()、listen(),accept(),read(),write(),read(),close(),阻塞,等待客户机连接请求,coonect(),write(),read(),socket(),close(),客户,建立连接(TCP三路握手),发送数据请求,接收数据,通信结束、关闭连接,面向连接的数据流套接字通信模型,2 面

32、向连接的数据流套接字的典型编程方法,(1)服务器一方main(void)if(创建一个流套接字返回值0)出错提示;退出;if(命名套接字返回值0)出错提示;退出;if(监听连接请求返回值0)出错提示;退出;for(;)新的套接描述符=取得第一个连接请求返回代码;if(新的套接描述符0)出错提示;退出;接收数据信息;处理请求;将应答发送给客户机;关闭套接字;,(2)客户一方 main(void)if(创建一个流套接字返回值0)出错提示;退出;if(连接服务器返回值0)出错提示;退出;for(;)发送数据信息;接收服务器方应答;关闭套接字;,6.4 无连接的数据流套接字的编程方法,1 无连接的数据

33、流套接字时序步骤 数据报套接字的服务器进程和客户机进程在进行通信前不用建立连接。通信的主要步骤如下:(1)服务进程首先调用Socket()创建一个数据报套接字;(2)服务进程调用bind()将服务器地址绑定在在这个套接字上;(3)通过recvfrom()阻塞服务进程,等待客户进程发来的请求;(4)客户机首先调用Socket()创建一个数据报套接字;(5)客户机进程调用bind()将客户机地址绑定在在此套接字上;(6)调用sendto(),客户机进程向服务进程发出请求;(7)服务进程接到客户机数据报后被唤醒,执行完客户机请求后调用sendto()将处理结果返回给客户机;(8)客户机调用recvf

34、rom()接收服务进程返回的请求处理结果;(9)服务进程和客户进程调用close()撤消套接字;,2 非连接的数据流套接字的通信模型,3 非连接的数据流套接字的典型编程方法,(1)服务器一方 main(void)if(创建一个数据报套接字返回值0)出错提示;退出;if(命名套接字返回值0)出错提示;退出;for(;)接收客户机数据报(请求);处理请求;将数据(结果)发送给客户机;关闭套接字;,3 非连接的数据流套接字的典型编程方法,(1)客户端一方 main(void)if(创建一个数据报套接字返回值0)出错提示;退出;if(命名该套接字返回值0)出错提示;退出;for(;)发送数据报(请求)

35、给服务器;接收服务器应答;关闭套接字;,6.5 基于客户/服务器模式的网络编程,1 客户/服务器的工作流程(1)必须使用标准函数库实现系统调用,以陷入内核获得完成用户任务所需要的系统软、硬件资源;(2)系统调用都被内核入口点system_call函数截获,该函数根据所传递的参数确定应该执行哪些系统调用,并通过检查系统调用表,以确定相应的服务例程。最后把控制权转给该服务例程;(3)该服务过程立即和相关的内核代码模块建立联系。这些模块可能进一步需要和其他内核模块或者底层硬件通讯;(4)当系统调用结束后,结果按照相同的路径反方向返回。核心把控制交给用户程序;(5)如果该实例有某些错误的操作,如用户堆

36、栈溢出,处理器将引发一个异常通知内核,有内核执行相应的处理程序来处理异常事件。,客户端,服务器端,解析域名生成服务器请求 计算并显示服务内容和图像,Buffet allocted,Socket buffer,客户程序,系统调用,Connect()recv()send()fputs(),TCP/IP协议,内核,NIC缓冲,网卡,解析域名生成服务器请求 计算并显示服务内容和图像,Buffet allocted,Socket buffer,TCP/IP协议,NIC缓冲,accept()recv()send()open()read(),文件系统RAMorDisk,服务器程序,内核,网卡,HTTPTCP

37、IP,网络协议,Internet,操作系统支持Web服务器的工作流程,1 客户/服务器的工作流程,在客户端,如果运行的是Web测览器,当向Web浏览器的“地址”窗口处输入http:/.或者“WWW.”等形式的网址并回车,就是由Web浏览器向web服务器发出的一次客户请求。该请求经过解析后,通过系统调用由用户态转为核心态执行。在核心态操作系统中的TCP/IP协议代码和网卡驱动程序控制网卡把请求发送到相应的网络上,等待Web服务器响应。当服务响应返回时,由网卡接收,并通过内核传送给客户程序。在服务器端,内核通过网卡从网络上接收Web请求,并通过系统调用传递给Web服务器。Web服务器根据此服务请求

38、执行相应的服务过程,并由内核把结果放到网络上传送给客户。Web浏览器和Web服务器使用URL和URL Connection进行网络通信,这是一种较高层次的网络通信,为的是访问Internet上的资源。通常,用户也常常需要编写一个由操作系统提供的接口客户/服务器应用程序,由套接口实现的客户服务器应用程序是低级别的网络通信。因此,从程序员的观点来看,操作系统所提供的系统调用定义了应用程序和协议栈之间的接口。应用程序无论使用哪种通信协议进行网络通信,都必须申请同操作系统交互才能得到服务。,#include#include#include#include#include#include#include

39、#includeint main(int argc,char*argv)int sockfd,numb,port=8000;struct sockaddr_in s;struct hostent*host;char buf100;if(argc!=2)fprintf(stderr,usage:%s hostnamen,argv0);exit(1);if(!(host=gethostbyname(argv1)perror(error in resolving hostname);exit(1);sockfd=socket(AF_INET,SOCK_STREAM,0);if(sockfd0)per

40、ror(socket);exit(1);,bzero(,在这个例子中,客户程序向服务器发出请求,建立与服务器的 TCP连接,并发送服务请求字符串“Give me the first file of presidents offic”。在client1.c客户程序中,先调用socket()函数创建一个套接口,然后调用connect()函数数建立与主机的连接。当connect()函数返回时,再通过调用send()函数向服务器发出请求或数据。接下来执行recv()函数等待接收服务器发回的响应。下面的程序清单给出的是服务器程序server1.c。服务器程序创建一个永久套接口来侦听服务请求。当接收到客户

41、的服务请求时,它将创建一个临时套接口,传回一个字符串,然后将关闭临时套接口,循环等待下一个客户请求的到来。为了不影响前台其他程序的运行,可以把服务器程序写成一个守护进程(daemon),守护进程启动后一直运行在后台,监听客户的请求,从不退出,除非显式地将它关闭。,#include#include#include#include#include#include#include#include#include#include int port=8000;void init_daemon(void)pid_t pid;int i;if(pid=fork()=-1)/*生成第1个子进程*/exit(1

42、);/*fork失败退出*/if(pid 0)exit(0);/*父进程退出,使shell成为前台进程*/setsid();/*第1子进程成为新会话和新进程组的领头进程的同时也失去控制终端*/*第1子进程执行下面的操作*/for(i=0;iNOFILE;+i)close(i);/*关闭已打开的文件描述符*/chdir(/rundir);/*改变当前运行的目录*/umask(0);/*改变文件创建掩码*/return;,int main(int ac,char*av)int sockfd,tsockfd,addr_size;char buf100;struct sockaddr_in s,p;c

43、har temp_buf256;init_daemon();sockfd=socket(AF_INET,SOCK_STREAM,0);if(sockfd=-1)perror(socket building failure);exit(1);bzero(,for(;)tsockfd=accept(sockfd,(struct sockaddr*),在这个服务器程序的例子中,首先执行socket()系统调用,让内核创建一个永久的接口,以监听服务器请求。然后调用bind()函数将刚创建的套接口与众所周知的端口号8000连在一起。接着调用listen()函数将永久套接口变成一个被动套接口,开始监听来自

44、客户机的连接请求。当函数返回时,程序将进入一个无限循环,并阻塞在accept()系统调用上,等待连接请求的接入。一旦有接入请求,accePt()马上返回一个临时套接口供服务器读取用户的请求数据。然后将用户需要的数据返回给客户。读取用户数据的系统调用是 recv()函数,发送数据的系统调用是send()函数。网络上数据的传输方向是双向的。一个流向是把数据从图l6a的用户层传输到网卡,另一个流向是把数据从网卡传输到用户层。无论流向哪一方,中间都要穿过内核层。下面给出编译并运行这两个程序的操作过程:$gcc-o ciient1 clientl.c$gcc-o server1 server1.c 操作

45、如下:$./server1$./client1 localhost get connected!(回车)Received=Here is the first file of the presidents office,3 客户/服务器程序的编程实现流程,下图是基本的面向连接的client/sever系统调用流程。在客户端编程的基本步骤是:创建套接字;利用创建的套接字向服务器发出连接请求,启动TCP的三次握手;如果连接成功,双方互操作开始。服务器端通信软件的步骤是:创建套接字;在建立的套接字中绑定本机IP及一个周知的端口号;在该套接字进行监听;如收到连接请求,则启动与客户的连接;如果连接成功,双

46、方互操作开始。,3 TCP连接的建立和终止过程,TCP传输控制协议提供客户与服务器的连接。一个TCP客户与一个TCP服务器可以建立一个可靠的连接,并通过这个连接通路与服务器交换数据,通信结束后终止连接。(1)建立TCP连接的过程 建立一个TCP连接,必须首先启动服务器。服务器程序通过调用函数socket()、bind()和listen(),事先做好接收客户的连接请求的准备。这个过程称为被动打开。稍后启动客户机程序,它调用函数socket()创建一个用于连接的套接口,然后调用函数connect()执行连接请求,从而引发了客户机与服务器之间一个TCP连接的建立过程,调用函数connect()的过程

47、称为主动打开。下图给出了建立TCP连接的示意图。,客户机,socket(),accept()(阻塞)Accept()(返回),socket()bind()listen(),connect()(阻塞)Connect()(返回),read()(阻塞),服务器,SYN B,SYN D ackB+1,ack D+1,建立TCP连接的三次握手,3 TCP连接的建立和终止过程,建立一个TCP连接需要三路握手信号:(l)客户机通过调用connect()执行,首先向服务器发送一个SYN(同步序列号标志)分节。这里所说的分节是TCP传递给IP的数据单元。一个SYN分节一般不携带数据,它只含有一个IP头部和一个T

48、CP头部,还含有一些TCP的选项。这些选项告诉服务器在本次连接中发送数据的初始序列号。图1中给出的客户机初始序列号是B。(2)服务器在收到客户发来的SYN分节以后,必须给客户机一个回答信号,确认客户的SYN分节。与此同时,服务器也要向客户机发送一个SYN分节,通知客户机服务器将在这次连接中发送数据的初始序列号D。(3)在收到服务器发来的SYN分节和回答信号后,客户也必须回答确认服务器的SYN分节。由于SYN只占一个字节的序列号空间,所以客户、服务器通信双方的ACK回答的确认序列号都是由所接收到对方的序列号加1得到。,2 TCP连接的建立和终止过程,(2)终止 TCP连接的过程用户可以显式地调用

49、close()函数来关闭一个TCP连接。关闭一个TCP连接需要4个分节。关闭连接也有主动关闭和被动关闭之分。先调用close()函数的一端称为主动关闭。当close执行时,发送一个FIN(完成标志)分节,通知对方数据发送完毕。FIN是finish flap的缩写,它也是TCP分节之一。接收FIN分节的另一端称为被动关闭。FIN分节作为文件结束符由另一端的read()函数来接收。因为接收到FIN分节就意味着另一端的进程在此连接上再也不会接收到数据了,所以read()返回0。但这个FIN分节是由被动关闭一端的TCP来确认。read()函数返回后,应用进程也将调用close()函数来关闭相应的套接口

50、。close()函数执行时,也会向对方发送一个FIN分节,并由主动关闭一端的TCP对其进行确认。图2给出关闭套接口时分组的交换过程,总共需要交换4个分组。,客户机,close(),close(),read()(阻塞),服务器,FIN X,ack X-1,TCP关闭连接时的分组交换过程,FIN Z,ack Z-1,3 TCP连接的建立和终止过程,从TCP连接的建立和终止过程可以看出,如果在TCP协议的一次通信中,只发送一个单一分节的请求和接收一个单一分节的应答,则需要7个分节的额外开销。如果使用UDP,只有“请求”和“应答”两个分组需要交换,但是UDP不提供可靠的数据包传送,它把传输层的许多处理

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

当前位置:首页 > 生活休闲 > 在线阅读


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号