非阻塞的TCP服务设计

云风在12年写了段代码,讲的是socket server开启一个tcp服务,自己维护一个内存ringbuffer,然后利用epoll/kevent来poll request,自己维护循环收集请求.他的原始代码在此处.

最近抽空看了下代码,发现颇有可观之处,然后就改写下收集到我的工具库里面去.代码可以参考这里

代码的核心思想是检查系统,将BSD的kqueue和Linux的epoll包装成统一的一组api,以供调用.

在具体实现的时候,我们可以看到它有很多好玩的地方,ringbuffer里面的map实现就不细说,我copy来的里面有个隐藏的链表.在初始化的时候,开辟一个连接数组,每个element的fd都是指向下一个的offset,最后一个指向的是-1.再初始化一个头,指向的是0.

每次取连接的时候,都是从头部获得offset,然后将头更新为offset的fd的值.每次还的时候,则是将将要还的的fd改成头的值,再把头更新为将要还的的offset.若头指向-1,则表示没有空的位置.

不过,kquque和epoll只能包括linux和bsd,windows服务下是无法用的,若哪天有兴趣,我打算把select也加上,这样就可以在任何地方都能用了.此外,ringbuffer其实也是个很好的概念,等我有空把rb搞清楚了,也把它补上.

附上调用示例:

#include
#include

#include "argcv/nio/tcp_listen.hh"

using namespace argcv::nio;

int main(int argc, char * argv[]) {
tcp_listen pool(9527, 200000);
if (pool._error_no() != 0) {
printf("pool establish failed .. %d \n", pool._error_no());
} else {
printf("pool established .. %d \n", pool._error_no());
for (;;) {
int id = pool.poll(0);
if (id != -1) {
printf("#### id: %d\n", id);
co_lacus::conn &c = pool[id];
bool st = pool.pull(id, 1);
if (st) {
// printf("data:[%s] %lu \n",c.to_str().c_str(), c.to_str().length());
std::string s = c.to_str();
for (size_t i = 0; i < c.to_str().length(); i++) {
printf("%lu %d %c\n", i, c.to_str()[i], c.to_str()[i]);
}
// sleep(3);
c.write(c.to_str(), c.to_str().length());
} else {
if (c.closed()) {
printf("is closed .. \n");
} else {
printf("unknown error ? \n status : %hhu\n", c._status());
}
}
c.flush();
}
}
}
}