一、TCP发送缓冲区/TCP接收缓冲区
在传输层,每个socket对应的TCP连接都拥有自己的接收缓冲区和发送缓冲区。
接收缓冲区:用于存储网络层发往当前TCP连接的分组数据,直到应用层将数据从其中取走。
发送缓冲区:用于存储当前TCP连接即将发送给对端的分组数据,直到收到对端的接收确认。
TCP发送缓冲区和接收缓冲区用于实现TCP协议的“滑动窗口”和“流量控制”功能,保证TCP的可靠传输。
二、socket中read/write的语义
1、SocketChannel.read(ByteBuffer dest)
阻塞模式下:阻塞直到“TCP接收缓冲区”中有数据可读,并且成功将数据读取到了应用进程ByteBuffer中。
非阻塞模式下:尝试从“TCP接收缓冲区”中读取当前可读的数据,不论是否能读取到数据均立即返回。
2、SocketChannel.write(ByteBuffer src)
阻塞模式下:阻塞直到ByteBuffer src中的数据全部成功写入“TCP发送缓冲区”中。
非阻塞模式下:尝试将ByteBuffer src中的数据写入“TCP发送缓冲区”中,不保证数据全部写入(如果“TCP发送缓冲区”中空间不足,也可能写入部分数据),然后立即返回。
三、SelectionKey的operation语义
OP_READ:“TCP接收缓冲区”中有了新的可读数据。
OP_WRITE:“TCP发送缓冲区”中可以写入新的数据了。
OP_CONNECT:“TCP三次握手”完成。
OP_ACCEPT:新的客户端socket连接建立完成,放入了服务端“已完成连接队列”。
四、Direct ByteBuffer和Non-Direct ByteBuffer
1、概述
Non-Direct ByteBuffer的内存是分配在堆上的,可以把它想象成一个字节数组的包装类,直接由JVM负责垃圾收集。
Direct ByteBuffer是通过JNI在JVM外的内存空间分配的,该内存块并不直接由JVM负责垃圾回收,而是在Direct ByteBuffer包装类被回收时,通过Java reference机制来释放该内存块。
2、网络读写
操作系统无法直接操作用户空间的ByteBuffer,socket在调用read/write进行网络读写操作时,会先把用户空间的Non-direct ByteBuffer转换为操作系统内核空间的临时Direct ByteBuffer(这个过程需要进行内存拷贝),然后再进行相关的操作。
如果直接将用户进程的数据存储在Direct ByteBuffer中,则可以节省两次内存拷贝的时间。然后构造和析构临时Direct ByteBuffer的时间和代价比较长,因此一般对Direct ByteBuffer进行池化处理。