收藏 分享(赏)

第八章Socket程序设计.ppt

上传人:tangtianxu1 文档编号:3003197 上传时间:2018-10-01 格式:PPT 页数:35 大小:541.50KB
下载 相关 举报
第八章Socket程序设计.ppt_第1页
第1页 / 共35页
第八章Socket程序设计.ppt_第2页
第2页 / 共35页
第八章Socket程序设计.ppt_第3页
第3页 / 共35页
第八章Socket程序设计.ppt_第4页
第4页 / 共35页
第八章Socket程序设计.ppt_第5页
第5页 / 共35页
点击查看更多>>
资源描述

1、第八章 Socket程序设计,Socket 概述,网络协议规定了两台计算机之间进行数据交换的共同规则,包括交换数据的格式和动作序列。 UNIX中传输层和传输层以下的协议在操作系统内核中实现,那么就必须规定一种应用程序使用内核的这些网络功能的方法。 UNIX访问网络也用文件描述符引用一个特殊文件的方法。 目前,应用程序与网络之间接口有socket和TLI(Tansport Layer Interface),2,TCP和UDP,TCP/IP对应用程序提供的服务主要有两种:一种是面向连接的可靠的数据流传输TCP,另一种是无连接不可靠数据报传输UDP。应用程序员在使用TCP/IP网络编写通信程序之前,

2、应当首先在TCP和UDP协议之间作出选择,它们决定了由系统提供的通信可靠性。,3,TCP和UDP,TCP提供了完全可靠的通信服务,它能够自动地重传;计算校验和以保证数据的正确性;TCP协议保证数据在接收端按在发送端发送的次序接收,不会出现后发送的数据先到达的情况;能自动地扔掉那些重复传输的数据;它提供了流量控制机制,保证发送者发送的数据不要太快以至于接收者来不及处理它们,甚至还考虑了不以过快的速度发送数据以防整个网络拥堵。当由于某种原因TCP通信无法进行时,会通过一定的手段通知应用程序。使用UDP,所有上述TCP的可靠机制都没有。由应用程序想办法解决可靠性问题,这些问题包括:错报、重报、丢报、

3、乱序和流量控制。,4,TCP和UDP,选择UDP协议的程序,应当进行周密的测试。否则,一个在时延短误码率低的本地局域网上调试好的程序在时延长差错率高,或者时延和误码率经常变化的广域网上可能会出现问题。或者,在数据量较小时,运行正常,但数据量增大时运行不稳定。 选择UDP协议通信的程序,必须利用一些技术,以保证数据的可靠传输,或者是高层的软件在UDP不可靠的服务面前也能够正常工作。相对来说,要比直接使用TCP难度更大。使用UDP协议的典型应用有域名服务DNS和简单文件传送协议TFTP,简单网管协议SNMP,以及路由信息协议RIP。需要使用广播(broadcast)或者组播(multicast)功

4、能时,只能选择UDP。,5,TCP通信,socket给出的编程接口,无论使用TCP还是UDP协议,都是一种client/server风格的软件结构。事实上,UNIX设计的socket机制,不仅仅是面向TCP/IP通信协议的,而是面向所有的网络通信,包括其他的通信协议栈。client/server结构的协议软件包括客户端软件和服务器端软件。,6,TCP通信,文件传送协议FTP为例,UNIX提供的ftp命令就是客户端软件,在提供文件传送服务的远程计算机上,运行服务器端软件。这些服务器端的软件,在UNIX中由守护进程inetd控制,当TCP连接到达时,inetd创建ftpd服务进程负责与客户端软件的

5、ftp通信,以完成文件传送操作,文件传送结束后,ftpd进程结束。,7,TCP通信,为了进行类比,先看一下文件操作的模式。访问文件有一组函数,而且这些函数调用的先后顺序也有一定的规则,先open()得到文件描述符fd,然后可以执行read()和write()访问文件内容,另有一些可以施加在fd上的函数,例如:lseek()定位文件指针,fstat()获得文件的状态等等。使用管道时,就不再用open()获得文件描述符,而是用pipe()一次获得两个文件描述符。,8,TCP通信,使用socket的情况类似,先用socket()创建一个文件描述符,在这个文件描述符上,先施行一些connect(),b

6、ind(),listen()等操作控制建立TCP连接,然后才能使用read()和write()收发数据。 通信过程中,可以使用fcntl(),setsockopt()和getpeername()等函数,获得一些通信的状态,或者设置一些与通信有关的参数。 最终,用close()关闭连接。这些函数调用的先后顺序,也遵循一定的规则。,9,TCP通信,TCP客户端程序客户端程序首先和远程的服务器端程序建立连接,然后,从标准输入读取一个字符串,将该字符串发送到服务器。 client.c基本上分几部分:创建socket文件描述符,建立连接,发送数据,最后关闭文件描述符,也就关闭了网络连接。 connect

7、调用,建立一个TCP连接。执行connect调用,内核的TCP协议软件开始3次握手,并等待对方计算机的应答。,10,TCP通信,TCP服务器程序 服务器端程序也一样需要用socket()调用创建一个文件描述符,然后,执行bind()调用。 执行了socket()调用之后,内核中为这一文件描述符记录的本地端点名和远地端点名都为空,IP地址和端口号都是0。服务器端的程序,必须把本地端点设置为一个约定好的TCP端口。 系统调用bind()完成这项任务,bind调用的目的是指定本地的一个“端点名”。这里的程序本地端点名中的端口号,使用常数PORT_NO,需要事先转换为网络字节顺序。,11,TCP通信,

8、TCP服务器程序 listen()调用是通知内核,以后凡是到达本地主机的TCP连接,如果端点名和这个文件描述符的本地端点名相同,就转交到这个文件描述符上。 一般面向连接的通信的服务器端程序需要listen(),而且listen()调用安排在程序的bind()调用之后。 accept()在文件描述符admin_sock上等待一个远程主机发送来的连接请求,,12,TCP通信,socket()系统调用创建一个socket,13,#include #include int socket (int domain, int type, int protocol);成功返回socket文件描述符; 出错返回

9、-1,TCP通信,socket()系统调用创建一个socket,14,TCP通信,socket()系统调用创建一个socketprotocol参数通常情况下为0,用来表明默认的domain和socket类型。SOCK_STREAM这种socket类型的话,默认为AF_INET的IPPROTO_TCP ;SOCK_DGRAM这种socket类型的话,默认为AF_INET的IPPROTO_UDP ;,15,TCP通信,connet()系统调用建立连接,16,#include #include int connect (int sockfd, const struct sockaddr *addr,

10、 socklen_t len);成功返回0; 出错返回-1,connect()的第一个参数决定了协议类型,有了“端点名”,connect()才知道连接的对方是谁。为了描述一个连接的“端点名”,不同协议栈需要不同的描述方法。,TCP通信,connet()系统调用建立连接,17,struct sockaddr short sin_family; /* AF_INET */u_short sin_port; /* port number */struct in_addr sin_addr; /* IP address */char sin_zero8; /* unused */ ; struct i

11、n_addr u_long s_addr; ;,sin_family必须为AF_INET,sin_port是TCP端口号,sin_addr是IP地址。这两个参数都需要按网络字节顺序存放,所以分别使用了htonl和htons。,TCP通信,connet()系统调用建立连接,18,对于TCP来说,指定了通信的对端,在本地和远地主机之间建立一个连接。所连接对端的端点名放在name缓冲区中,namelen为存放端点名的缓冲区的有效数据长度。这个系统调用可能会导致进程睡眠等待连接建立成功。对于UDP来说,connect并不和远程主机之间建立任何连接,而是仅仅将通信对端的“端点名”记录到内核的sockfd

12、对应的数据结构中。当然,这也不会导致进程睡眠。随后的write(sockfd,.)将使用这个sockfd的对端的端点名记录的IP地址和端口号,作为每个UDP报的目的地址。,TCP通信,bind()系统调用指定本地端口,19,#include #include int bind (int sockfd, const struct sockaddr *addr, socklen_t len);成功返回0; 出错返回-1,bind用于指定通信的本地“端点名”。在服务器端的程序中,这个调用是必需的,指定一个约定的端口号作为本地的端口号。随后的listen才使得内核的TCP协议模块了解哪些到达的连接该转

13、交到这个sockfd。,TCP通信,bind()系统调用指定本地端口 bind()调用的目的是为文件描述符选定一个本地端点名,这个选定的端点名被内核记录下来。 bind()函数并不是服务器端程序专用的。在客户端的程序中,包括TCP和 UDP,可以不指定本地端点名,执行connect调用,或者对UDP来说发送数据时,如果本地端点名还是空,那么系统会自动为本地端点分配一个端点名,包括IP地址和端点号。 客户端程序在connect()前也可以用bind()强制选择本地端点名,即本地IP地址和端口号。本地主机有多个IP地址时,bind()可以为本地端点强制选定其中的一个地址,或者强制选定一个本地端口号

14、。,20,TCP通信,listen()监听到达的连接请求,21,#include #include int listen (int sockfd, int back_log);成功返回0; 出错返回-1,TCP通信,listen()监听到达的连接请求 一个已经执行过bind() 指定了本地“端点名”的文件描述符,执行了listen()后才开始监听有没有连接请求到达。 listen()只用在面向连接的网络协议对应的文件描述符。backlog指明最多可以排队多少个连接请求。 每次accept()就从这个队列里取走一个连接请求,创建新的文件描述符。 在accept()之后,下次accept()之前的

15、时间段到达的连接请求就排队等候,backlog指定允许的最大排队数。此参数常指定为5,是目前允许的最大值。,22,TCP通信,accept()系统调用指定本地端口,23,#include #include int accpet (int sockfd, const struct sockaddr *addr, socklen_t len);成功返回文件描述符; 出错返回-1,在一个面向连接的服务器中,先用socket创建一个套接字,再用bind指定一个本地“端点名”,然后执行listen后就开始监听。,TCP通信,accept()系统调用指定本地端口 accept()就是取得排队在套接字上的队

16、列的第一个连接请求,并建立起一个新的文件描述符,函数返回值就是这个文件描述符。 如果addr和len不为0,对端的端点名返回在addr中。len用于传入存放端点名的缓冲区addr的大小,函数调用返回时,len存放已记录到addr中的远端主机的端点名的有效长度。前面的程序例子中给出了这后两个参数的用法。 如果accept()执行时,sockfd上尚没有连接请求到达,那么accept()调用被阻塞,进程处于睡眠状态,等待连接请求的到达。这一系统调用可以被信号打断。,24,网络字节次序,不同的计算机厂商在计算机内部存储整数的方法会有些不同。 例如:一个4B的整数,占用内存的连续的4个存储单元,有的厂

17、商将这个整数的低位字节放在最低地址处,x86系列CPU就是这种安排,这种安排叫Little Endian;而有的厂商正好相反,高位字节放在4B内存的低地址处,如PowerPC和Sparc,它们的字节顺序安排叫Big Endian。 网络通信时,总是从内存的低地址开始传输连续的若干字节,因此,网络软件为了保证各计算机之间的互联性,要求所有的数据按统一的字节顺序传输,这就是规定的网络字节顺序。网络字节顺序的规定与x86的主机字节顺序相反。,25,网络字节次序,在UNIX中提供了htons,htonl两个库函数,分别将短整数和长整数从主机字节次序转换到网络字节次序。相应地,ntohs()和ntohl

18、()把网络字节顺序转换到主机字节顺序。在socket的网络系统调用和库函数的结构体参数中的整数,一般也要求按网络字节顺序排列。为了源程序的可移植性,即使所用的UNIX中主机字节顺序与网络字节顺序正好吻合,也不要省略掉所必须的htons()、htonl()、ntohs()和ntohl()。,26,read/write与TCP通信的时序,当调用read时,如果TCP连接上事先已经有到达了的数据,提前到达的数据会被放在内核里这个TCP连接对应的接收缓冲区内,那么read立即返回,返回值为实际的字节数;否则,如果TCP连接上没有数据到达,那么read就被阻塞,进程处于睡眠状态,直到收到数据为止。 当调

19、用write时,如果TCP连接上发送忙,那么write就被阻塞,进程处于睡眠状态,直到能够把数据传给发送缓冲区为止。write调用没有返回1,系统调用正确返回后,并不能保证数据已经正确地发送到了对方。 下面以一个具体的TCP通信实例描述write和read在什么时刻返回。,27,read/write与TCP通信的时序,主机A通过TCP连接向主机B发送数据,用write();主机B接收数据用read()。最左侧为时间编号。t0: 主机B开始read(),read处于阻塞状态。 t1: 主机A调用write(),主机A发送缓冲区有空闲,将数据复制至发送缓冲区。 t2: 主机A将数据发往主机B。 t

20、3: 主机B收到数据后,验证校验和正确,则进程被叫醒,read()返回读到的数据。 t4: 主机B向主机A发ACK,ACK途中丢失。 t5: 主机A超时自动重发数据。 t6: 主机B收到重复的数据后扔掉,回送ACK。 t7: 主机A收到ACK,将发送缓冲区的数据清除。,28,read/write与TCP通信的时序,对于read()调用来说,在调用参数中指明了期望读到的字节数。那么,当系统收不到数据时,read()会等待,而不是立刻返回0,只要收到了数据,read()就立即返回,返回值为实际收到的字节数。 即使实际收到的字节数远远小于它所期望读到的字节数nbyte,read()也不再等下去。 系

21、统实际已收到的字节数大于nbyte时,read()读取nbyte字节,剩下的数据,下次read()时读取。如果等待过程中发生了通信意外,那么read()会以1返回。 如果在read/write之前,sockfd上就已经出现了错误,例如,通信中断,那么,read()和write()都会马上以1立刻返回。,29,read/write与TCP通信故障,TCP在成功地建立好了连接后,在两台主机之间保持这条连接。 在连接保持期间,如果用户没有数据在这条连接上发送,那么系统会周期性地发送探测数据,这就是所谓的keepalive。 由于TCP连接是“端到端”的,中间需要穿越网络,有很多转发节点,这和直接用电

22、路连接的“点到点”情况不同。“点到点”之间如果没有数据传输,线路就空闲,但是,TCP的“端到端”之间没有数据传输,“端到端”的网络沿途所有节点的线路不见得空闲。所以,这种周期性的探测数据不应当太频繁,以免无谓地增加整个网络的流量。一般默认设置为两个小时。,30,read/write与TCP通信故障,TCP连接建立好之后,一段时间内没有数据传送。这时,如果网络已经发生了故障,TCP两小时一次的keepalive不能迅速察觉,TCP两端的进程也无法知道。这时,如果调用write(),write()在将数据放到发送缓冲区后就成功地返回,返回码不等于1。随后立即还有write(),只要发送缓冲区有空闲

23、,就能成功地返回。 内核中的TCP协议软件负责将数据发送到对方主机,由于网络发生了永久性故障,那么将不可能收到对方的ACK。TCP就试着重发,经多次重发仍没有对方回送的证实后,才断定故障发生。这样,随后再对这一连接进行的read()和write()才会失败,立刻返回1。,31,read/write与TCP通信故障,从write第一次试着发送数据到内核的TCP协议软件多次重发后认定出了故障,这段时间间隔常常为十几分钟。有时在主机向网络发送数据时,网络中的设备会主动以ICMP报文向主机报告“目的不可达”,这时TCP会放弃重试,立即断定网络出了故障,那么,从write()第一次试着发送到断定出了故障

24、的时间间隔值就小得多。 在断定出了故障后,随后的write/read或正在进行中的write/read都会失败,以1返回。 其他原因也有可能导致TCP传输失败,例如:两台主机之间建立好了一条TCP连接之后,其中一台来不及关闭所有TCP连接就突然掉电,不再启动。如果它正在以read()等待已停止运行的主机送来数据,那么它就会执著地等下去,直到keepalive发现了连接断开。,32,read/write与TCP通信故障,在无法确认数据正确到达对方的前提下,write调用就返回,返回码不等于1,但是,数据可能仅仅复制到了内核中的缓冲区。这似乎与TCP承诺的无差错可靠的数据流传输相违背,其实不然,因

25、为只要是从TCP接收到的,就一定是正确的,中间绝没有丢失数据。 在UNIX中的TCP连接上使用write()和read()能带给程序员的服务质量是“发出去的数据不能保证一定会到达对方,但是收到的数据就一定是正确的”。如果发送方需要知道对方是否确实收到了数据,那么,接收方就该通过TCP给一个回执。由于TCP是可靠传输,完全可以一批数据传输完成后给一个回执。 类似的问题是,对方计算机一样也不知道它给出的回执是否确实能到达受信方。,33,read/write与TCP通信故障,在TCP连接一侧的一次write可能会导致对方的多次read,也有可能多次write,只能导致对方的一次read,因此,靠发送

26、端的一次write导致接收端的一次read这样一个对应关系来保持数据记录的边界是靠不住的。 TCP仅仅承诺在连接的两端提供了一个透明的可靠的字节流传输,为了提高传输或者重传的效率,TCP可能会把应用程序多次write的一段段数据,粘接在一起作为一个数据包,或者分解成多个数据包传送。应用程序应设法利用这个可靠字节流传送设计自己的数据表示格式以保持记录边界。例如:每次发送一个数据记录前,先发送两字节长度,接收方依靠这个长度判断一个完整的数据记录是否收全。这种数据流方式的服务和提供数据报方式的UDP不同。,34,read/write与UDP通信,使用UDP通信时,由于UDP提供的是不可靠的数据报服务,write不返回1,一样不能保证数据正确到达了对方。但是,由于UDP提供了数据报服务,write和read保证了记录边界,write一次发送一个数据报,对方read一次可以得到一个完整的数据报,不可能会出现多次的write将数据“粘连”起来的情况。 由于UDP协议本身不支持流量控制功能,所以,无法像TCP一样,在UDP通信两端的write和read实现流量控制功能。,35,

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

当前位置:首页 > 实用文档 > 往来文书

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报