阻塞/非阻塞读写总结、tcp网络编程的本质、muduo::Buffer设计简介
2013-11-07 16:42
549 查看
一、阻塞/非阻塞读写总结
1、对于read 调用,如果接收缓冲区中有 20字节,请求读 100个字节,就会返回 20;对于 write调用,如果请求写 100个字节,而发送缓冲区中只有 20个字节的空闲位置,那么 write会阻塞,直到把 100个字节全部交给发送缓冲区才返回。但如果 socket文件描述符有 O_NONBLOCK标志,则 write不阻塞,直接返回 20;此时非阻塞地read
也直接返回20。
[align=left]
[/align]
2、read 没有一点数据可读或 write 没有一点空间可以写入,如果disable O_NONBLOCK 则会阻塞,如果enable O_NONBLOCK 则会返回-1,errno = EAGAIN | EWOULDBLOCK 错误。
3、阻塞模式下可以用setsockopt设置SO_RCVTIMEO(超时时间),即如果在超时时间内接收缓冲区都没有一点数据到来,那么返回-1,errno = EAGAIN | EWOULDBLOCK 错误。同理,还有SO_SNDTIMEO
选项,在超时时间内发送缓冲区都没有足够内存存放数据,也是返回-1,errno = EAGAIN | EWOULDBLOCK 错误。
4、recv的第四个参数若为MSG_WAITALL,则在阻塞模式下不等到指定数目的数据不会返回,除非超时时间到。当然如果对方关闭了,即使超时时间未到,recv
也返回0。/usr/include/i386-linux-gnu/bits/socket.h MSG_WAITALL = 0x100
5、在多线程环境中,某个线程的阻塞不会引起进程的阻塞,除非进程中的所有线程都被阻塞。(pthread)
二、TCP网络编程的本质
TCP网络编程最本质是的处理三个半事件(来自:muduo manual.pdf)
[align=left]1. 连接的建立,包括服务端接受(accept) 新连接和客户端成功发起(connect) 连接。TCP 连接一旦建立,客户端和服务端是平等的,可以各自收发数据。[/align]
[align=left]
[/align]
[align=left]2. 连接的断开,包括主动断开(close 或shutdown) 和被动断开(read(2) 返回0)。[/align]
[align=left]
[/align]
[align=left]3. 消息到达,文件描述符可读。这是最为重要的一个事件,对它的处理方式决定了网络编程的风格(阻塞还是非阻塞,如何处理分包,应用层的缓冲如何设计等等)。[/align]
[align=left]
[/align]
[align=left]3.5 消息发送完毕,这算半个。对于低流量的服务,可以不必关心这个事件;另外,这里“发送完毕”是指将数据写入操作系统的缓冲区,将由TCP 协议栈负责数据的发送与重传,不代表对方已经收到数据。[/align]
[align=left]
[/align]
1、下图是根据muduo库中对读写事件的处理画出的草图:
2、Echoser 类图:(muduo/example/simple/Echo.h、Echo.cc)
使用基于对象风格实现,详见这里。
3、什么都不做的EventLoop
one loop per thread意思是说每个线程最多只能有一个EventLoop对象,这种线程即“reactor"(mainReactor & subReactor)。剩下一些存在于threadpool 的线程主要用于做计算(decode, compute, encode),并不是IO线程。
EventLoop对象构造的时候,会检查当前线程是否已经创建了其他EventLoop对象,如果已创建,终止程序(LOG_FATAL)
EventLoop构造函数会记住本对象所属线程(threadId_)。
创建了EventLoop对象的线程称为IO线程,其功能是运行事件循环(EventLoop::loop)
三、muduo::Buffer设计简介
所有muduo 中的IO 都是带缓冲的IO (buffered IO),你不会自己去read() 或write() 某个socket,只会操作TcpConnection 的input buffer 和output buffer。更确切的说,是在onMessage()
回调里读取input buffer;调用TcpConnection::send()来间接操作output buffer,一般不会直接操作output buffer。
TcpConnection 会有两个Buffer 成员,input buffer 与output buffer。
• input buffer,TcpConnection 会从socket 读取数据,然后写入input buffer(其实这一步是用Buffer::readFd() 完成的);客户代码从input buffer 读取数据。
• output buffer,客户代码会把数据写入output buffer (其实这一步是用TcpConnection::send() 完成的);TcpConnection 从output buffer 读取数据并写入socket。
其实,input 和output 是针对客户代码而言,客户代码从input 读,往output 写。TcpConnection 的读写正好相反。
两个indices 把vector 的内容分为三块:prependable、readable、writable,各块的大小是(公式一):
prependable = readIndex
readable = writeIndex - readIndex
writable = size() - writeIndex
[align=left]
[/align]
Muduo Buffer 里有两个常数kCheapPrepend 和kInitialSize,定义了prependable的初始大小和writable 的初始大小,readable
的初始大小为0。在初始化之后,Buffer 的数据结构如下:括号里的数字是该变量或常量的值。
[align=left]
[/align]
关于Buffer::readFd():
C++ Code
具体做法是,在栈上准备一个65536 字节的stackbuf,然后利用readv() 来读取数据,iovec 有两块,第一块指向muduo Buffer 中的writable 字节,另一块指向栈上的stackbuf。这样如果读入的数据不多,那么全部都读到Buffer 中去了;如果长度超过Buffer 的writable 字节数,就会读到栈上的stackbuf 里,然后程序再把stackbuf 里的数据append 到Buffer 中。
参考:
《UNP》
muduo manual.pdf
《linux 多线程服务器编程:使用muduo c++网络库》
1、对于read 调用,如果接收缓冲区中有 20字节,请求读 100个字节,就会返回 20;对于 write调用,如果请求写 100个字节,而发送缓冲区中只有 20个字节的空闲位置,那么 write会阻塞,直到把 100个字节全部交给发送缓冲区才返回。但如果 socket文件描述符有 O_NONBLOCK标志,则 write不阻塞,直接返回 20;此时非阻塞地read
也直接返回20。
[align=left]
[/align]
2、read 没有一点数据可读或 write 没有一点空间可以写入,如果disable O_NONBLOCK 则会阻塞,如果enable O_NONBLOCK 则会返回-1,errno = EAGAIN | EWOULDBLOCK 错误。
3、阻塞模式下可以用setsockopt设置SO_RCVTIMEO(超时时间),即如果在超时时间内接收缓冲区都没有一点数据到来,那么返回-1,errno = EAGAIN | EWOULDBLOCK 错误。同理,还有SO_SNDTIMEO
选项,在超时时间内发送缓冲区都没有足够内存存放数据,也是返回-1,errno = EAGAIN | EWOULDBLOCK 错误。
4、recv的第四个参数若为MSG_WAITALL,则在阻塞模式下不等到指定数目的数据不会返回,除非超时时间到。当然如果对方关闭了,即使超时时间未到,recv
也返回0。/usr/include/i386-linux-gnu/bits/socket.h MSG_WAITALL = 0x100
5、在多线程环境中,某个线程的阻塞不会引起进程的阻塞,除非进程中的所有线程都被阻塞。(pthread)
二、TCP网络编程的本质
TCP网络编程最本质是的处理三个半事件(来自:muduo manual.pdf)
[align=left]1. 连接的建立,包括服务端接受(accept) 新连接和客户端成功发起(connect) 连接。TCP 连接一旦建立,客户端和服务端是平等的,可以各自收发数据。[/align]
[align=left]
[/align]
[align=left]2. 连接的断开,包括主动断开(close 或shutdown) 和被动断开(read(2) 返回0)。[/align]
[align=left]
[/align]
[align=left]3. 消息到达,文件描述符可读。这是最为重要的一个事件,对它的处理方式决定了网络编程的风格(阻塞还是非阻塞,如何处理分包,应用层的缓冲如何设计等等)。[/align]
[align=left]
[/align]
[align=left]3.5 消息发送完毕,这算半个。对于低流量的服务,可以不必关心这个事件;另外,这里“发送完毕”是指将数据写入操作系统的缓冲区,将由TCP 协议栈负责数据的发送与重传,不代表对方已经收到数据。[/align]
[align=left]
[/align]
1、下图是根据muduo库中对读写事件的处理画出的草图:
2、Echoser 类图:(muduo/example/simple/Echo.h、Echo.cc)
使用基于对象风格实现,详见这里。
3、什么都不做的EventLoop
one loop per thread意思是说每个线程最多只能有一个EventLoop对象,这种线程即“reactor"(mainReactor & subReactor)。剩下一些存在于threadpool 的线程主要用于做计算(decode, compute, encode),并不是IO线程。
EventLoop对象构造的时候,会检查当前线程是否已经创建了其他EventLoop对象,如果已创建,终止程序(LOG_FATAL)
EventLoop构造函数会记住本对象所属线程(threadId_)。
创建了EventLoop对象的线程称为IO线程,其功能是运行事件循环(EventLoop::loop)
三、muduo::Buffer设计简介
所有muduo 中的IO 都是带缓冲的IO (buffered IO),你不会自己去read() 或write() 某个socket,只会操作TcpConnection 的input buffer 和output buffer。更确切的说,是在onMessage()
回调里读取input buffer;调用TcpConnection::send()来间接操作output buffer,一般不会直接操作output buffer。
TcpConnection 会有两个Buffer 成员,input buffer 与output buffer。
• input buffer,TcpConnection 会从socket 读取数据,然后写入input buffer(其实这一步是用Buffer::readFd() 完成的);客户代码从input buffer 读取数据。
• output buffer,客户代码会把数据写入output buffer (其实这一步是用TcpConnection::send() 完成的);TcpConnection 从output buffer 读取数据并写入socket。
其实,input 和output 是针对客户代码而言,客户代码从input 读,往output 写。TcpConnection 的读写正好相反。
两个indices 把vector 的内容分为三块:prependable、readable、writable,各块的大小是(公式一):
prependable = readIndex
readable = writeIndex - readIndex
writable = size() - writeIndex
[align=left]
[/align]
Muduo Buffer 里有两个常数kCheapPrepend 和kInitialSize,定义了prependable的初始大小和writable 的初始大小,readable
的初始大小为0。在初始化之后,Buffer 的数据结构如下:括号里的数字是该变量或常量的值。
[align=left]
[/align]
关于Buffer::readFd():
C++ Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | // 结合栈上的空间,避免内存使用过大,提高内存使用率 // 如果有5K个连接,每个连接就分配64K+64K的缓冲区的话,将占用640M内存, // 而大多数时候,这些缓冲区的使用率很低 ssize_t Buffer::readFd(int fd, int *savedErrno) { // saved an ioctl()/FIONREAD call to tell how much to read // 节省一次ioctl系统调用(获取有多少可读数据) char extrabuf[65536]; struct iovec vec[2]; const size_t writable = writableBytes(); // 第一块缓冲区 vec[0].iov_base = begin() + writerIndex_; vec[0].iov_len = writable; // 第二块缓冲区 vec[1].iov_base = extrabuf; vec[1].iov_len = sizeof extrabuf; const ssize_t n = sockets::readv(fd, vec, 2); if (n < 0) { *savedErrno = errno; } else if (implicit_cast<size_t>(n) <= writable) //第一块缓冲区足够容纳 { writerIndex_ += n; } else // 当前缓冲区,不够容纳,因而数据被接收到了第二块缓冲区extrabuf,将其append至buffer { writerIndex_ = buffer_.size(); append(extrabuf, n - writable); } // if (n == writable + sizeof extrabuf) // { // goto line_30; // } return n; } |
参考:
《UNP》
muduo manual.pdf
《linux 多线程服务器编程:使用muduo c++网络库》
相关文章推荐
- 阻塞/非阻塞读写总结、tcp网络编程的本质、muduo::Buffer设计简介
- 阻塞/非阻塞读写总结、tcp网络编程的本质、muduo::Buffer设计简介
- 牛人整理分享的面试知识:操作系统、计算机网络、设计模式、Linux编程,数据结构总结
- 牛人整理分享的面试知识:操作系统、计算机网络、设计模式、Linux编程,数据结构总结
- [综合面试] 牛人整理分享的面试知识:操作系统、计算机网络、设计模式、Linux编程,数据结构总结
- [综合面试] 牛人整理分享的面试知识:操作系统、计算机网络、设计模式、Linux编程,数据结构总结
- C#网络编程TCP通信实例程序简单设计
- Java网络编程(31):非阻塞I/O简介
- IO模式设置网络编程常见问题总结—IO模式设置,阻塞与非阻塞的比较,recv参数对性能的影响—O_NONBLOCK(open使用)、IPC_NOWAIT(msgrcv)、MSG_DONTWAIT(re
- 关于网络编程中MTU、TCP、UDP优化配置的一些总结
- TCP网络编程最本质是的处理三个半事件
- 【java面试系列之网络编程】TCP和UDP的区别、TCP协议的三次握手和四次挥手、TCP协议的通信状态、网络编程时的同步、异步、阻塞、非阻塞、进程间的通信方式、TCP的流量控制和拥塞控制
- IO模式设置网络编程常见问题总结—IO模式设置,阻塞与非阻塞的比较,recv参数对性能的影响—O_NONBLOCK(open使用)、IPC_NOWAIT(msgrcv)、MSG_DONTWAIT(re
- linux TCP/UDP网络编程总结
- ThreadLocal,静态变量,实例变量,局部变量的线程安全,回复:ByteBuffer 到底怎么用?网络编程中一点总结!
- 牛人整理分享的面试知识:操作系统、计算机网络、设计模式、Linux编程,数据结构总结
- Java网络编程之TCP程序设计
- Java网络编程之非阻塞I/O客户端TCP实例
- Java网络编程之非阻塞I/O服务器TCP实例
- 翻译:使用Libevent的快速可移植非阻塞网络编程:异步IO简介 (一)