ascent的网络模块简析

来源:互联网 发布:西工大网络继续教育 编辑:程序博客网 时间:2024/06/14 14:15

本文是解读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,就不用指定这个。
艹,误导人