数据的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/,转载请务必注明出处
本文出自 “重剑无锋” 博客,请务必保留此出处http://qianyang.blog.51cto.com/7130735/1614914
原文地址:http://qianyang.blog.51cto.com/7130735/1614914