本文是解读ASIO之前的一碟小菜。。。
ascent 2010的网络模块代码采用了IOCP,代码量很小。
带着以下问题溜了一遍:
1.IOCP的乱序问题
如果同时对一个socket投递多个recv或者send请求,那么应用层会将TCP辛辛苦苦保证的有序
给打乱。
解决办法很简单,串行投递。
这对于服务器程序来说是可行的,毕竟它管理的是成百上千个sockets,每个2请求,那IOCP要处理
的也很多了。
ascent也是这么做的(从socket类只含有两个overlap对象一眼看出)。
但不是强制:发生了重复投递,它只是写了一条错误日志。
2. send操作能否避开锁?(recv无须锁是很好做的,不提)
事实上我觉得是可以的,至少epoll是绝对可以的,边写边想IOCP是否也可以。
从线程的角度看,一条连接的发送缓冲区的消费者是网络线程,生产者是逻辑线程。
即发送数据一定是逻辑线程发起的(前提是心跳包也要一并移给逻辑线程处理);
那么缓冲区的读游标是由网络线程修改,写游标是由逻辑线程修改。
要做到无锁,一定要保证共享变量只会被一个线程修改,且为volatile类型;
逻辑线程投递写请求,将数据放入发送缓冲,修改写游标;
对于IOCP来说,网络工作者线程可能有多个(一般来说是CPU数目的两倍),
多个网络线程阻塞在GetQueuedCompletionStatus函数,尽管执行send请求的成功回调
不是由某个固定网络线程执行,但在同一时刻send请求的回调一定是由某一个线程单独处理,
并调整读游标。
所以感觉iocp方式下,socket的发送缓冲区也无须锁。
(也许考虑还不是很全面,待修正)
asent中使用了锁来保护发送缓冲区,但我认为是它的缓冲区结构设计问题导致的(很复杂,做成了两段缓冲),如果做成简单的不可扩充的
足够大的环形缓冲区,是无须锁的。(带来一个问题:由于发送的数据可能不是在连续缓冲,发送操作请调用类似writev的函数,
当然WINDOWS支持的很好,WSASend与WSABUF共同实现gather-write)
在逻辑线程发送数据时,如果检测到并没有send操作正在挂起中,那么就直接WSASend
3.是否单独启动LISTEN线程
ascent确实单独启动了一个监听线程。不过有点恶的是,如果启动了两个监听socket,那就启动两个监听线程,
太浪费了。
监听线程阻塞ACCEPT,并将新连接与IOCP关联,同时投递第一个RECV请求。
另记:
为什么非阻塞socket,send也会阻塞?
A端发数据,B端不调用recv;
于是一会儿A端的发送缓冲区满了,竟然阻塞在send调用;而不是返回-1和EAGAIN?
必须send时候指定MSG_DONTWAIT。。尽管是非阻塞socket
但是man手册说了,设置了O_NONBLOCK,就不用指定这个。
艹,误导人