数据的IO和复用,高级的噢


  • IO函数

    • 使用recv()函数接收数据

      原型:ssize_t recv(int s,void *buf,size_t len, int flags);

      recv从套接字s中接受数据,缓冲区buf,buf长度len,操作方式由flags指定。s是由socket函数返回的。

      flags值及含义

      MSG_DONTWAIT

      非阻塞操作,立刻返回,不等待

      MSG_ERRQUEUE

      错误消息从套接字错误队列接收

      MSG_OOB

      接受外数据数据

      MSG_PEEK

      查看数据,不进行数据缓冲区的清空

      MSG_TRUNC

      返回所有数据,即使缓冲区不够大,超过缓冲区的将被截断丢弃

      MSG_WAITALL

      等待所有消息,没读到足够字节时不返回,任性

      如果发生一下情况之一,MSG_WAIT返回的字节仍然会比请求的少。

      1、捕获到一个信号 2、连接终止 3、套接字上发生错误

      返回值为-1时错误发生,成功返回读到的字节数,当另一方使用正常的方式关闭连接时返回0,例如调用close()

      error的值及其含义

      EAGAIN

      套接字定义为非阻塞的,而操作采用了阻塞方式,或者定义的超时时间已经达到却没有接收到数据

      EBADF

      参数s不是合法描述符

      ECONNREFUSED

      远程主机不允许此操作

      EFAULT

      接收缓冲区指针在此进程之外

      EINTR

      接收到中断信号

      EINVAL

      传递了不合法参数

      ENOTCONN

      套接字s传递了流式套接字,此套接字没有连接

      ENOTSOCK

      参数不是套接字描述符

      如果内核缓冲区接收到的数据比用户指定的要少时,会在下一次调用再复制缓冲区的内容



    • send函数发送数据

      原型:ssize_t send(int s,const void *buf,size_t len,int flags);

      参数意义和recv类似

      errno值及其含义

      EAGAIN/EWOULDBLOCK

      套接字定义为非阻塞的,而操作采用了阻塞方式,或者定义的超时时间已经达到却没有接收到数据

      EBADF

      参数s不是合法描述符

      ECONNREFUSED

      远程主机不允许此操作

      EFAULT

      接收缓冲区指针在此进程之外

      EINTR

      在发送数据之前接收到中断信号

      EINVAL

      传递了不合法参数

      ENOTCONN

      套接字s传递了流式套接字,此套接字没有连接

      ENOTSOCK

      参数不是套接字描述符

      ECONNRESET

      连接断开

      EDESTADDRRED

      套接字没有处于连接状态

      ENOBUFS

      发送缓冲区已满

      ENOMEN

      没有足够内存

      EOPNOTSUPP

      设定发送方式flag没有实现

      EPIPE

      套接字已经关闭

      EACCES

      套接字不可写

      当flag为0时send和write完全一样,send只能用于套接字处于连接状态的描述符,之前必须要用connect函数或者其他函数进行连接。



    • readv接收数据

原型:ssize_t readv(int s,const struct iovec*vector,int count);

readv从套接字描述符s中读取count块数据放到缓冲区向量vector中

count为vector的块数


struct iovec{

void *iov_base;

size_t iov_len;

}


    • writev发送函数

      原型:ssize_t writev(int fd,const struct iovec*vector,int count);




    • recvmsg

      原型:ssize_t recvmsg(int s,struct msghdr *msg,int flags);

把接收到的数据放在msg中,操作方式由msg内的msg_flags指定

flags和errno值和以上的类似

struct msghdr{

void *msg_name; //可选地址

socklen_t msg_namelen; //地址长度

struct iovec *msg_iov; //接收数据的数组,和readv中的含义一样

size_t msg_iovlen; //msg_iovlen中元素数量

void *msg_control; //指向缓冲区,根据msg_flags的值,会放入不同的值

socklen_t msg_controllen; //msg_control的大小

int msg_flags; //接收消息的标志

};


msg_name表示源地址,指向一个struct sockaddr的指针,当套接字还没有连接的时候有效


    • sendmsg

      原型:ssize_t sendmsg(int s,const struct msghdr *msg,int flags);

      msghdr.mas_name指向目的地址,可直接发送UDP


IO函数比较

名称

任何描述符

只对套接字描述符

单个缓冲区

多个缓冲区

可选标志

可选对方地址

可选控制信息

read()/write()

()


()





readv()/writev()

()



()




recv()/send()


()

()


()



recvfrom()/writeto()


()

()


()

()


recvmsg()/sendmsg()


()


()

()

()

()







IO模型:

阻塞模型

非阻塞模型


IO复用

在等待的时候加入超时时间,当超时时间没达到时和阻塞的情况一致,到达时仍然没有数据时,系统会返回,不再等待。

信号驱动IO

在信号处理函数里调用接收函数。SIGIO


异步IO模型

aio_read 在数据完成复制时发出信号





select函数和pselect函数

用于IO复用

select函数

原型:int select (int nfds,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,struct timeval *timeout);

nfds很奇怪,它要比所有文件描述符集合中的文件描述符的最大值大1。

readfds这个文件描述符集合监视文件描述符集中的任何文件是否有数据可读,当select函数返回时,readfds将清除其中不可读的文件描述符,只留下可读的文件描述符,即可以被recv和read等进行操作。

writefds和readfds类似,监视的是是否可写,send和write等

exceptfds监视是否发生错误,也可以用于其它用途,例如监视带外数据OOB,带外数据MSG_OOB标志发送到套接字上。当select函数返回时,readfds将清除其他文件中的其他文件描述符。只留下可读OOB

timeout设置select所监视的文件集合中的事件没有发生时,最长等待时间。当超时时间为NULL时,表示阻塞操作,会一直等待,直到某个监视的文件集中的某个文件描述符符合返回条件。当timeout为0时,会立即返回。linux中select返回时会修改timeout的时间为剩余时间

struct timeval{

time_t tv_sec; //秒

long tv_usec; //微秒

}


返回值:超时返回0,错误返回-1,其他返回正值

当所有集合为NULL时,表示等待一段时间。

4个操作文件描述符的集合

FD_SERO()

清理文件描述符

FD_SET()

向某个文件描述符集合中加入文件描述符

FD_CLR()

从某个文件描述符的集合中取出某个文件描述符

FD_ISSET()

测试某个文件描述符是否某个集合中的一员



pselect函数

原型:int pselect(int nfds,fd_set *readfds,fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout, const sigset_t sigmask);

跟select类似,但timeout是timespec

struct timespec{

long tv_sec; //超时的秒数

long tv_nsec; //超时的纳秒数 唬人的,linux内核的调度精度为10毫秒级

};

当sigmask为NULL时,与select的方式一样。

pselect返回时不会修改timeout的值

ready=pselect(nfds,&readfds,&writefds,&exceptfds,timeout,&sigmask);

相当于

sigset_t origmask;

sigprocmask(SIG_SETMASK,&sigmask,&origmask);

ready=select(nfds,&readfds,&writefds,&exceptfds,timeout);

sigprocmask(SIG_SETMASK,origmask,NULL);




poll函数和ppoll函数

poll函数:

原型:int poll(struct pollfd *fds,nfds_t nfds,int timeout);

poll函数监视在fds数组指明的一组文件描述符上发生的动作,当满足条件或者超时时退出。

fds是指向结构体pollfd数组的指针,监视的文件描述符和条件放在里面

nfds是比监视的最大描述符大1的值

timeout是超时时间,单位毫秒,负值表示永远等待。

返回值,大于0表示成功,0表示超时,-1错误。

错误码

EBADF

参数s不是合法描述符

EINTR

接收到中断信号

EINVAL

传递了不合法参数

ENOMEM

没有足够内存


struct pollfd{

int fd; //文件描述符

short events; //请求的事件,是请求监视的事件,输入参数

short revents; //返回的事件,是造成返回的具体事件,是输出参数

};

events和revents的值及含义

POLLIN

有数据到来,文件描述符可读

POLLPRI

有紧急数据可读,例如带外数据

POLLOUT

文件可写

POLLRDHUP

流式套接字半关闭

POLLERR

错误发生

POLLHUP

关闭

POLLNVAL

非法请求

POLLRDNORM

与POLLIN相同

POLLRDBAND

优先数据可读

POLLWRNORM

与POLLOUT相同

POLLWRBAND

优先数据可写



ppoll函数

原型:int ppoll(struct pollfd *fds,nfds_t nfds,const struct timespec *timeout,const sigset_t *sigmask);

与select和pselect函数情况类似。




非阻塞编程


fcntl

原型:int fcntl(int fd,int cmd,...);

例子:

fcntl(s,F_SETFL,O_NONBLOCK);

设置由socket函数产生的描述符即可,accept函数产生的会带上该标志。

本文出自http://qianyang.blog.51cto.com/,转载请务必注明出处