linux教程-第9章-数据的IO和复用课件_第1页
linux教程-第9章-数据的IO和复用课件_第2页
linux教程-第9章-数据的IO和复用课件_第3页
linux教程-第9章-数据的IO和复用课件_第4页
linux教程-第9章-数据的IO和复用课件_第5页
已阅读5页,还剩61页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第9章数据的IO和复用9.1IO函数9.2使用IO函数的例子9.3IO模型9.4select()函数和pselect()函数9.5poll()函数和ppoll()函数9.6非阻塞编程掌握:IO函数、select()函数(重点)了解:IO原理(难点)、IO模型9.1IO函数9.1.1使用recv()函数接收数据9.1.2使用send()函数发送数据9.1.3使用readv()函数接收数据9.1.4使用writev()函数发送数据9.1.5使用recvmsg()函数接收数据9.1.6使用sendmsg()函数发送数据9.1.7IO函数的比较3recvmsgrecvitsoreceiverecvitsoreceive读系统调用的流程4sendmsgsenditsosend9.1.1使用recv()函数接收数据recv()函数用于接收数据。recv()函数从套接字s中接收数据放到缓冲区buf中,buf的长度为len,操作的方式由flags指定。只能用于绑定了来源端地址的套接字。#include<sys/types.h>#include<sys/socket.h>ssize_trecv(ints,void*buf,size_tlen,intflags);flags值含

义MSG_DONTWAIT非阻塞操作,立刻返回,不等待MSG_ERRQUEUE错误消息从套接字错误队列接收MSG_OOB接收带外数据MSG_PEEK查看数据,不进行数据缓冲区的清空MSG_TRUNC返回所有的数据,即使指定的缓冲区过小MSG_WAITALL等待所有消息9.1.2使用send()函数发送数据send()函数用于发送数据。send()函数将缓冲区buf中大小为len的数据,通过套接字文件描述符按照flags指定的方式发送出去。只能用于绑定了目的地址的套接字。当flags=0时,send()等同于write()。#include<sys/types.h>#include<sys/socket.h>ssize_tsend(ints,constvoid*buf,size_tlen,intflags);9.1.3使用readv()函数接收数据readv()函数可用于将接收的数据分散到多个缓冲区数据。readv()函数从套接字描述符s中读取count块数据放到缓冲区向量vector中。#include<sys/uio.h>ssize_treadv(ints,conststructiovec*vector,intcount);9.1.4使用writev()函数发送数据writev()函数用于收集多个缓冲区的数据写入套接字。writev()函数向套接字描述符s中写入在向量vector中保存的count块数据。#include<sys/uio.h>ssize_twritev(intfd,conststructiovec*vector,intcount);9.1.5使用recvmsg()函数接收数据recvmsg()函数用于接收数据,与recv()函数、readv()函数相比较,这个函数的使用要复杂一些。1.函数recvmsg()原型含义2.地址结构msghdr3.函数recvmsg()用户空间与内核空间的交互1.函数recvmsg()原型含义recvmsg()从套接字s中接收数据放到缓冲区msg中,操作的方式由flags指定。#include<sys/types.h>#include<sys/socket.h>ssize_trecvmsg(ints,structmsghdr*msg,intflags);2.地址结构msghdr函数recvmsg()中用到结构msghdr的原型如下:structmsghdr{void *msg_name;

/*可选地址*/socklen_t

msg_namelen;

/*地址长度*/structiovec

*msg_iov;

/*接收数据的数组*/size_t

msg_iovlen;

/*msg_iov中的元素数量*/void

*msg_control;/*ancillarydata,seebelow*/socklen_t

msg_controllen;

/*ancillarydatabufferlen*/int

msg_flags;

/*接收消息的标志*/};3.函数recvmsg()用户空间与内核空间的交互接收来自192.168.1.150的发送到192.168.1.151的200个UDP数据,接收后msghdr结构的情况。9.1.6使用sendmsg()函数发送数据函数sendmsg()可用于向多个缓冲区发送数据。函数sendmsg()向套接字描述符s中按照结构msg的设定写入数据,其中操作方式由flags指定。#include<sys/uio.h>ssize_tsendmsg(ints,conststructmsghdr*msg,intflags);向192.168.1.200端口为9999的主机发送300B数据,将msg参数中的3个向量缓冲区大小都为100,msg状态如图。如何使用sendmsg()函数structsockaddr_inreceiver_addr;//1.定义地址charline[15]="HelloWorld!";//2.缓冲区structmsghdrmsg;//3.定义msghdrstructioveciov;//4.定义iovec//5.各种值的设置msg.msg_name=&receiver_addr;msg.msg_namelen=sizeof(receiver_addr);msg.msg_iov=&iov;msg.msg_iovlen=1;msg.msg_iov->iov_base=line;msg.msg_iov->iov_len=15;msg.msg_control=0;msg.msg_controllen=0;msg.msg_flags=0;sendmsg(sock_fd,&msg,0);//6.调用该函数9.1.7IO函数的比较表9.8为上述函数使用时的特点,○标记的为具有此种属性。有如下规律:9.1.7IO函数的比较表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.2使用IO函数的例子9.2.1客户端处理框架的例子9.2.2服务器端程序框架9.2.3使用recv()和send()函数9.2.4使用readv()和writev()函数9.2.5使用recvmsg()和sendmsg()函数9.2.1客户端处理框架的例子客户端处理程序是一个程序框架,为后面使用3种类型的收发函数建立基本的架构。1.客户端程序框架2.客户端程序框架代码1.客户端程序框架图9.6客户端处理流程2.客户端程序框架代码P.246-24729

signal(SIGINT,sig_proccess);

/*挂接SIGINT信号,处理函数为sig_process()*/30

signal(SIGPIPE,sig_pipe);

/*挂接SIGPIPE信号,处理函数为sig_pipe()*/9.2.2服务器端程序框架P.247-248服务器端处理程序是一个程序框架,为后面使用3种类型的收发函数建立基本的架构。函数process_conn_server()是进行服务器端处理的函数,不同收发函数的实现方式不同。20

signal(SIGINT,sig_proccess);/*挂接SIGINT信号,处理函数为sig_process()*/21

signal(SIGPIPE,sig_proccess);/*挂接SIGPIPE信号,处理函数为sig_pipe()*/9.2.3使用recv()和send()函数下面使用recv()和send()函数进行网络数据收发。1.服务器端的实现代码2.客户端的处理代码3.信号SIGINT的处理函数4.信号SIGPIPE的处理函数1.服务器端的实现代码P.249服务器端的处理过程先使用recv()函数从套接字文件描述符s中读取数据到缓冲区buffer中,如果不能接收到数据则退出操作。服务器成功接收数据后,利用接收到的数据构建发送给客户端的响应字符串,调用send()函数将响应字符串发送给客户端。07

for(;;){

/*循环处理过程*/08

size=recv(s,buffer,1024,0);

09

/*从套接字中读取数据放到缓冲区buffer中*/10

if(size==0){

/*没有数据*/11

return;

12

}...2.客户端的处理代码P.24907

for(;;){

/*循环处理过程*/08

size=read(0,buffer,1024);

09

/*从标准输入中读取数据放到缓冲区buffer中*/10

if(size>0){

/*读到数据*/11

send(s,buffer,size,0);/*发送给服务器*/12

size=recv(s,buffer,1024,0);

/*从服务器读取数据*/13

write(1,buffer,size);

/*写到标准输出*/14

}3.信号SIGINT的处理函数01

/*信号SIGINT的处理函数*/02

voidsig_proccess(intsigno)03

{04

printf("Catchaexitsignal\n");05

_exit(0);

06

}4.信号SIGPIPE的处理函数01

/*信号SIGPIPE的处理函数*/02

voidsig_pipe(intsign)03

{04

printf("CatchaSIGPIPEsignal\n");05

06

/*释放资源*/

07

}9.2.4使用readv()和writev()函数使用如下的代码代替9.2.1节中的函数process_conn_client()和9.2.2节中的process_conn_server(),使用readv()和writev()进行读写。1.服务器端的实现代码2.客户端的处理代码3.信号SIGINT的处理函数4.信号SIGPIPE的处理函数1.服务器端的实现代码P.250-251下面是使用readv()和writev()进行数据IO的服务器处理的代码,利用向量来接收和发送网络数据。12

structiovec*v=(structiovec*)malloc(3*sizeof(structiovec));2.客户端的处理代码P.251-252与服务器端的代码类似,客户端也使用3个10字节大小的向量来完成数据的发送和接收操作。3.信号SIGINT的处理函数01

/*信号SIGINT的处理函数*/02

voidsig_proccess(intsigno)03

{04

printf("Catchaexitsignal\n");05

/*释放资源*/

06

free(vc);07

free(vs);08

_exit(0);

09

}4.信号SIGPIPE的处理函数01

/*信号SIGPIPE的处理函数*/02

void

sig_pipe(intsign)03

{04

printf("CatchaSIGPIPEsignal\n");05

06

/*释放资源*/

07

free(vc);08

free(vs);09

_exit(0);10

}9.2.5使用recvmsg()和sendmsg()函数使用如下的代码代替9.2.1节中的函数process_conn_client()和9.2.2节中的process_conn_server()函数,使用recvmsg()和sendmsg()进行读写。1.服务器端的实现代码2.客户端的处理代码3.信号SIGINT的处理函数4.信号SIGPIPE的处理函数1.服务器端的实现代码P.253-254在服务器端调用函数recvmsg()从套接字s中接收数据到消息msg中,将接收到的消息进行处理后,调用sendmsg()函数将响应数据通过套接字s发出。2.客户端的处理代码P.254-255与服务器端对应,客户端的实现也将3个向量挂接在一个消息上进行数据的收发操作。注意:缓冲区接收数据前要清0,否则可能出现乱字符。发送前发送缓冲区中的数据字节数要重新设置。3.信号SIGINT的处理函数01

/*信号SIGINT的处理函数*/02

voidsig_proccess(intsigno)03

{04

printf("Catchaexitsignal\n");05

/*释放资源*/

06

free(vc);07

free(vs);08

_exit(0);

09

}4.信号SIGPIPE的处理函数01

/*信号SIGPIPE的处理函数*/02

voidsig_pipe(intsign)03

{04

printf("CatchaSIGPIPEsignal\n");05

/*释放资源*/

06

free(vc);07

free(vs);08

_exit(0);09

}9.3IO模型9.3.1阻塞IO模型9.3.2非阻塞IO模型9.3.3IO复用9.3.4信号驱动IO模型9.3.5异步IO模型9.3.1阻塞IO模型阻塞IO是最通用的IO类型,使用这种模型进行数据接收的时候,在数据没有到之前程序会一直等待。例如对于函数recvfrom(),内核会一直阻塞该请求直到有数据到来才返回。以数据报为例

阻塞IO模型(同前模型)阻塞IO是最通用的IO类型,使用这种模型进行数据接收的时候,在数据没有到之前程序会一直等待。例如对于函数recvfrom(),内核会一直阻塞该请求直到有数据到来才返回。9.3.2非阻塞IO模型当把套接字设置成非阻塞的IO,则对每次请求,内核都不会阻塞,会立即返回;当没有数据的时候,会返回一个错误。例如对recvfrom()函数,前几次都没有数据返回,直到最后内核才向用户层的空间复制数据。轮询会消耗大量CPU时间。非阻塞IO模型同前模型)9.3.3IO复用如果TCP客户端同时处理两个输入:标准输入和TCP套接字。当客户端阻塞于(标准输入上时)(gets调用期间,服务器进程被杀死。服务器TCP虽然正确地给客户TCP发送了一个FIN。但是既然客户进程正阻塞于从标准输入读入的过程,它将看不到这个EOF,直到从套接字读时为止(可能已过了很长时间)。这样的进程需要一种预先告知内核的能力,使得内核一旦发现进程指定的一个或多个I/O条件就绪(也就是说输入已准备好被读取,或者描述符己能承接更多的输出),它就通知进程。这个能力称为I/O复用(multiplexing),是由select和po11这两个函数支持的。IO复用模型使用IO复用模型可以在等待的时候加入超时的时间,当超时时间没有到达与阻塞的情况一致(但阻塞在select或poll上),而当超时时间到达仍然没有数据接收到,系统会返回,不再等待。select()函数按照一定的超时时间轮询,直到需要等待的套接字有数据到来,利用recvfrom()函数,将数据复制到应用层。IO复用模型(同前一模型)对I/O复用的评述1.比较阻塞I/O与I/O复用,后者的优势在于select可以等待多个描述符就绪。2.与I/O复用密切相关的另一种I/O模型是在多线程中使用阻塞式I/O。两者极为相似,后者使用多个线程(每个文件描述符开一个线程),这样每个线程都可以自由地调用诸如recvfrom之类的阻塞式I/O系统调用了。9.3.4信号驱动IO模型信号驱动的IO在进程开始的时候注册一个信号处理的回调函数,进程继续执行,当信号发生时,即有了IO的时间,这里即有数据到来,利用注册的回调函数将到来的数据用recvfrom()接收到。9.3.5异步IO模型异步IO与前面的信号驱动IO相似,其区别在于信号驱动IO当数据到来的时候,使用信号通知注册的信号处理函数何时启动I/O操作,而异步IO则在数据复制完成的时候才发送信号通知注册的信号处理函数I/O操作何时完成。异步IO模型(同前一模型)9.4select()函数和pselect()函数9.4.1select()函数9.4.2pselect()函数参考《UNIX环境高级编程2版》14.5节9.4.1select()函数函数select()与之前的recv()和send()直接操作文件描述符不同。使用select()函数可以先对需要操作的文件描述符进行查询,查看目标文件描述符是否可以进行读、写或者错误操作,然后当文件描述符满足操作的条件的时候才进行真正的IO操作。1.函数select()简介2.select()函数的例子1.函数select()简介函数select()的原型如下:#include<sys/select.h>#include<sys/time.h>#include<sys/types.h>#include<unistd.h>intselect(intnfds,fd_set*readfds,fd_set*writefds,

fd_set*exceptfds,structtimeval*timeout);thelastargument,Thisspecifieshowlongwewanttowait:structtimeval{longtv_sec;/*seconds*/longtv_usec;/*andmicroseconds*/};Therearethreeconditions.tvptr==NULLWaitforever.Thisinfinitewaitcanbeinterruptedifwecatchasignal.Returnismadewhenoneofthespecifieddescriptorsisreadyorwhenasignaliscaught.Ifasignaliscaught,selectreturns–1witherrnosettoEINTR.tvptr->tv_sec==0&&tvptr->tv_usec==0Don'twaitatall.Allthespecifieddescriptorsaretested,andreturnismadeimmediately.Thisisawaytopollthesystemtofindoutthestatusofmultipledescriptors,withoutblockingintheselectfunction.tvptr->tv_sec!=0||tvptr->tv_usec!=0Waitthespecifiednumberofsecondsandmicroseconds.Returnismadewhenoneofthespecifieddescriptorsisreadyorwhenthetimeoutvalueexpires.Ifthetimeoutexpiresbeforeanyofthedescriptorsisready,thereturnvalueis0.(Ifthesystemdoesn'tprovidemicrosecondresolution,thetvptr–>tv_usecvalueisroundeduptothenearestsupportedvalue.)Aswiththefirstcondition,thiswaitcanalsobeinterruptedbyacaughtsig。打开描述符1、4、5对应的位2.select()函数的例子P.259-260函数select()监视标准输入是否有数据输入,所设置的超时时间为5s。如果select()函数出错,则打印出错信息;如果标准输入有数据输入,则打印输入信息;如果等待超时,则打印超时信息。15

err=select(1,&rd,NULL,NULL,&tv);9.4.2pselect()函数函数select()是用一种超时轮循的方式来查看文件的读写错误可操作性。在Linux下,还有一个相似的函数pselect()。1.pselect()函数简介2.pselect()函数的例子

Eachprocesshasasignalmaskthatdefinesthesetofsignalscurrentlyblockedfromdeliverytothatprocess.Wecanthinkofthismaskashavingonebitforeachpossiblesignal.Ifthebitisonforagivensignal,thatsignaliscurrentlyblocked.Aprocesscanexamineandchangeitscurrentsignalmaskbycallingsigprocmask,whichwedescribeinSection10.12.《unix环境高级编程英文版第2版》POSIX.1definesadatatype,calledsigset_t,thatholdsasignalset.Thesignalmask,forexample,isstoredinoneofthesesignalsets.1.pselect()函数简介pselect()函数的原型如下:#include<sys/select.h>#include<sys/time.h>#include<sys/types.h>#include<unistd.h>intpselect(intnfds,fd_set*readfds,fd_set*writefds,

fd_set*exceptfds,conststructtimespec*timeout,

constsigset_t*sigmask);2.pselect()函数的例子在例子中先清空信号,然后将SIGCHLD信号加入到要处理的信号集合中。设置pselect()监视的信号时,在挂载用户信号的同时将系统原来

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论