资源描述
,Click to edit Master title style,Click to edit Master text styles,Click to edit Master title style,Click to edit Master text styles,第,9,章 数据的,IO,和复用,主要包含如下内容:,介绍常用的,IO,函数,recv,()/send(),、,readv()/writev,(),、,recvmsg()/sendmsg,(),,并讲解函数的主要应用的场合,例如,recvmsg,(),可以用于接收在多个缓冲区中,可以设置选项。,用几个简单的例子,说明如何使用上述函数进行程序的设计。,介绍常用的几种,IO,模型,以图形式的方法形象的进行了说明。,介绍,select(),和,pselect,(),函数、如何使用这两个函数进行文件描述符读写条件的监视。,简单介绍函数,poll(),和,ppoll,(),的含义、使用和区别。,以简单的例子介绍非阻塞编程的方法。,9.1 IO,函数,Linux,操作系统中的,IO,函数主要有,read(),、,write(),、,recv,(),、,send(),、,recvmsg,(),、,sendmsg,(),、,readv,(),、,writev,(),。本节对上述的主要函数进行介绍,其中的,read(),和,write(),函数在前面已经介绍过。,9.1.1,使用,recv,(),接收数据,函数,recv,(),用于接收数据,函数原型如下。函数,recv,(),从套接字,s,中接收数据放到缓冲区,buf,中,,buf,的长度为,len,,操作的方式由,flags,指定。第一个参数,s,是套接口文件描述符,它是由系统调用,socket(),返回的。第二个参数,buf,是一个指针,指向接收网络数据的缓冲区。第三个参数,len,表示接收缓冲区的大小,以字节为单位。,#include,#include,ssize_t,recv(int,s,void*,buf,size_t,len,int,flags);,9.1.2,使用,send(),发送数据,函数,send(),用于发送数据,函数原型如下。函数,send(),将缓冲区,buf,中大小为,len,的数据通过套接字文件描述符按照,flags,指定的方式发送出去。当,send,函数的返回值小于,len,的时候,表明缓冲区中仍然有部分数据没有成功发送,这时需要重新发送剩余部分的数据。通常的剩余数据发送方法是对原来的,buf,中的数据位置进行偏移,偏移的大小为以发送成功的字节数。,#include,#include,ssize_t,send(int,s,const void*,buf,size_t,len,int,flags);,9.1.2,使用,send(),发送数据,值,含义,EAGAIN/EWOULDBLOCK,套接字定义为非阻塞,而操作采用了阻塞方式,或者定义的超时时间已经达到却没有接收到数据,EBADF,参数,s,不是合法描述符,ECONNREFUSED,远程主机不允许此操作,EFAULT,接收缓冲区指针在此进程之外,EINTR,在发送数据之前接收到中断信号,EINVAL,传递了不合法参数,ENOTCONN,套接字,s,表示流式套接字,此套接字没有连接,ENOTSOCK,参数不是套接字描述符,ECONNRESET,连接断开,EDESTADDRREQ,套接字没有处于连接状态,ENOBUFS,发送缓冲区已满,ENOMEM,没有足够内存,EOPNOTSUPP,设定的发送方式,flag,没有实现,EPIPE,套接字已经关闭,EACCES,套接字不可写,9.1.3,使用,readv,(),接收数据,函数,readv,(),可用于接收多个缓冲区数据,函数原型如下。函数,readv,(),从套接字描述符,s,中读取,count,块数据放到缓冲区向量,vector,中,#include,ssize_t,readv(int,s,const,struct,iovec,*vector,int,count);,值,含义,EAGAIN,套接字定义为非阻塞,而操作采用了阻塞方式,或者定义的超时时间已经达到却没有接收到数据,EBADF,参数,s,不是合法描述符,ECONNREFUSED,远程主机不允许此操作,EFAULT,接收缓冲区指针在此进程之外,EINTR,接收到中断信号,EINVAL,参数,iov_len,超出了,ssize_t,类型的范围,或者,count,参数小于,0,或者大于可允许最大值,ENOTCONN,套接字,s,表示流式套接字,此套接字没有连接,ENOTSOCK,参数不是套接字描述符,9.1.3,使用,readv,(),接收数据,9.1.4,使用,writev,(),发送数据,函数,writev,(),可用于接收多个缓冲区数据,函数原型如下。函数,writev,(),向套接字描述符,s,中写入在向量,vector,中保存的,count,块数据。,#include,ssize_t,writev(int,fd,const,struct,iovec,*vector,int,count);,9.1.5,使用,recvmsg,(),接收数据,函数,recvmsg,(),用于接收数据,与,recv,(),函数、,readv,(),函数相比较,这个函数的使用要复杂一些。,1,函数,recvmsg,(),原型含义,2,地址结构,msghdr,3,函数,recvmsg,(),用户空间与内核空间的交互,9.1.5,使用,recvmsg,(),接收数据,9.1.6,使用,sendmsg,(),发送数据,函数,sendmsg,(),可用于接收多个缓冲区数据,函数原型如下。函数,sendmsg,(),向套接字描述符,s,中按照结构,msg,的设定写入数据,其中操作方式有,flags,指定。,#include,ssize_t,sendmsg(int,s,const,struct,msghdr,*,msg,int,flags);,9.1.7 IO,函数的比较,表,9.8,为上述函数使用时的特点,标记的为具有此种属性。有如下规律,函数,read()/write(),和,readv()/writev,(),可以对所有的文件描述符使用;,recv,()/send(),、,recvfrom()/writeto,(),和,recvmsg/sendmsg,只能操作套接字描述符。,函数,readv()/writev,(),和,recvmsg()/sendmsg,(),可以操作多个缓冲区,,read()/write(),、,recv,()/send(),和,recvfrom()/sendto,(),只能操作单个缓冲区。,函数,recv,()/send(),、,recvfrom()/sendto,(),和,recvmsg()/sendmsg,(),具有可选标志。,函数,recvfrom()/sendto,(),和,recvmsg()/sendmsg,(),可以选择对方的,IP,地址。,函数,recvmsg()/sendmsg,(),有可选择的控制信心,能进行高级操作。,9.1.7 IO,函数的比较,名称,任何描述符,只对套接字描述符,单个缓冲区,多个缓冲区,可选标志,可选对方地址,可选控制信息,read()/write(),readv()/writev,(),recv,()/send(),recvfrom()/writeto,(),recvmsg()/sendmsg,(),9.2,使用,IO,函数的例子,小节,9.1,中对典型的,IO,函数进行了介绍,本小节中针对上述的函数给出程序设计的例子。包括典型的,send()/,recv,(),、,writev()/readv,(),、,sendmsg()/recvmsg,(),三种类型。,9.2.1,客户端处理框架例子,客户端处理程序是一个程序框架,为后面的使用三种类型的收发函数建立基本的架构。,1,客户端程序框架,2,客户端程序框架代码,9.2.1,客户端处理框架例子,9.2.2,服务器端程序框架,服务器端处理程序是一个程序框架,为后面的使用三种类型的收发函数建立基本的架构。函数,process_conn_server,(),是进行服务器端处理的函数,不同的收发函数的实现不同。,9.2.3,使用,recv,(),和,send(),函数,下面的代码是使用,recv,(),和,send(),函数进行网络数据收发时服务器和客户端的实现代码。,1,服务器端的实现代码,2,客户端处理代码,3,信号,SIGINT,处理函数,4,信号,SIGPIPE,的处理函数,9.2.4,使用,readv,(),和,write(),函数,使用如下的代码代替,9.2.1,中的函数,process_conn_server,(),和,process_conn_client,(),,使用,readv,(),和,writev,(),进行读写。,1,服务器端的实现代码,2,客户端处理代码,3,信号,SIGINT,处理函数,4,信号,SIGPIPE,的处理函数,9.2.5,使用,recvmsg,(),和,sendmsg,(),函数,使用如下的代码代替,9.2.1,中的函数,process_conn_server,(),和,process_conn_client,(),,使用,recvmsg,(),和,sendmsg,(),进行读写。,1,服务器端的实现代码,2,客户端处理代码,3,信号,SIGINT,处理函数,4,信号,SIGPIPE,的处理函数,9.3.IO,模型,IO,的方式有阻塞,IO,、非阻塞,IO,模型、,IO,复用、信号驱动、异步,IO,等,本节中以,UDP,为例介绍,IO,的几种模型。,9.3.1,阻塞,IO,模型,阻塞,IO,是最通用的,IO,类型,使用这种模型进行数据接收的时候,在数据没有到之前程序会一直等待。,9.3.2,非阻塞,IO,模型,当把套接字设置成非阻塞的,IO,,则对每次请求,内核都不会阻塞,会立即返回;当没有数据的时候,会返回一个错误。,9.3.3 IO,复用,使用,IO,复用模型可以在等待的时候加入超时的时间,当超时时间没有到达的时候与阻塞的情况一致,而当超时时间到达仍然没有数据接收到,系统会返回,不再等待。,select(),函数按照一定的超时时间轮询,直到需要等待的套接字有数据到来,利用,recvfrom,(),函数,将数据复制到应用层。,9.3.4,信号驱动,IO,模型,信号驱动的,IO,在进程开始的时候注册一个信号处理的回调函数,进程继续执行,当信号发生时,即有了,IO,的时间,这里即有数据到来,利用注册的回调函数将到来的函数用,recvfrom,(),接收到。,9.3.5,异步,IO,模型,异步,IO,与前面的信号驱动,IO,相似,其区别在于信号驱动,IO,当数据到来的时候,使用信号通知注册的信号处理函数,而异步,IO,则在数据复制完成的时候才发送信号通知注册的信号处理函数。,9.4 select(),和,pselect,(),函数,函数,select(),和,pselect,(),用于,IO,复用,它们监视多个文件描述符的集合,判断是否有符合条件的时间发生。,9.4.1 select(),函数,函数,select(),与之前的,recv,(),和,send(),直接操作文件描述符不同。使用,select(),函数可以先对需要操作的文件描述符进行查询,查看是否目标文件描述符可以进行读、写或者错误操作,然后当文件描述符满足操作的条件的时候才进行真正的,IO,操作。,1,函数,select(),简介,2,函数,select(),的例子,值,含义,EBADF,参数,s,不是合法描述符,EINTR,接收到中断信号,EINVAL,传递了不合法参数,ENOMEM,没有足够内存,9.4.2,pselect,(),函数,函数,select(),是用一种超时轮循的方式来查看文件的读写错误可操作性。在,Linux,下,还有一个相似的函数,pselect,(),。,1,函数,pselect,(),简介,2,函数,pselect,(),的例子,9.5 poll(),函数,除了使用,select(),进行文件描述符的监视,还有一组函数也可以完成相似的功能,即函数,poll(),和函数,ppoll,(),。,9.5.1 poll(),函数,函数,poll(),等待某个文件描述符上的某个事件的发生。其原型如下:,#include,int,poll(struct,pollfd,*,fds,nfds_t,nfds,int,timeout);,函数,poll(),监视在,fds,数组指明的一组文件描述符上发生的动作,当满足条件或者超时的时候会退出。,参数,fds,是一个指向结构,pollfd,数组的指针,监视的文件描述符和条件放在里面。,参数,nfds,,是比监视的最大描述符的值大,1,的值。,参数,timeout,,是超时时间,单位为毫秒,当为负值时,表示永远等待。,9.5.1 poll(),函数,值,含义,EBADF,参数,s,不是合法描述符,EINTR,接收到中断信号,EINVAL,传递了不合法参数,ENOMEM,没有足够内存,值,含义,POLLIN,有数据到来,文件描述符可读,POLLPRI,有紧急数据可读,例如带外数据,POLLOUT,文件可写,POLLRDHUP,流式套接字半关闭,POLLERR,错误发生,POLLHUP,关闭,POLLNVAL,非法请求,POLLRDNORM,与,POLLIN,相同,POLLRDBAND,优先数据可读,POLLWRNORM,与,POLLOUT,相同,POLLWRBAND,优先数据可写,9.5.2,ppoll,(),函数,与,select(),和,pselect,(),的情况相似,与,poll(),对应的也存在一个,ppoll,(),函数,其定义如下:,#include,int,ppoll(struct,pollfd,*,fds,nfds_t,nfds,const,struct,timespec,*timeout,const,sigset_t,*,sigmask,);,9.6,非阻塞编程,前面介绍的,IO,程序设计,基本上都是基于阻塞方式的。阻塞方式的读写,在文件没有数据的时候,函数不会返回,而一直等待直到有数据到来。本节介绍文件的非阻塞方式程序设计。,9.6.1,非阻塞方式程序设计介绍,非阻塞方式的操作与阻塞方式的操作最大的不同点是函数的调用立刻返回,不管数据是否成功读取或者成功写入。使用,fcntl,(),将套接字文件描述符按照如下的代码进行设置后,可以进行非阻塞的编程:,fcntl(s,F_SETFL,O_NONBLOCK);,其中的,s,是套接字文件描述符,使用,F_SETFL,命令将套接字,s,设置为非阻塞方式后,再进行读写操作就可以马上返回了。,9.6.2,非阻塞程序设计的例子,函数,accept(),可以使用非阻塞的方式轮询等待客户端的到来,在之前要设置,NON_BLOCK,方式。下面的代码使用了轮询的方式使用,accept(),和,recv,(),函数,当客户端发送“,HELLO”,字符串时,发送“,OK”,响应给客户端并关闭客户端;当客户端发送“,SHUTDOWN”,字符串给服务器时,服务器发送“,BYE”,的客户端,并关闭客户端,然后退出程序。,9.7,小结,本章对数据,IO,进行了介绍。介绍了,recv,(),、,send(),、,readv,(),、,writev,(),、,recvmsg,(),和,sendmsg,(),等函数的使用,并给出了多个例子。介绍了,IO,模型:阻塞,IO,模型、非阻塞,IO,模型、,IO,服用、信号驱动,IO,模型和异步,IO,模型。,select(),函数、,pselect,(),函数和,poll(),函数在查询方式程序设计中经常使用,而,fcntl,(),函数的,O_NONBLOCK,选项则是进行非阻塞编程中经常使用的设置方法。,
展开阅读全文