TCP 的socket有创建、绑定、监听、收数据等过程,如果需要创建一个能够监听多个客户端链接的后台socket,如何支持并发访问呢?
首先需要确定在什么地方需要支持并发:显然前三步都是初始化过程,不需要并发支持;收数据的过程是需要支持并发的,那也就是需要accept过程支持并发。
如何支持并发呢?答案显然是多线程处理。那如何让多线程的处理减轻CPU 负担,同时还能及时响应客户端的请求?
可以利用libevent来实现。
Libevent 是一个用C语言编写的、轻量级的开源高性能网络库,主要有以下几个亮点:事件驱动( event-driven),高性能;轻量级,专注于网络,不如 ACE 那么臃肿庞大;源代码相当精炼、易读;跨平台,支持 Windows、 Linux、 *BSD 和 Mac Os;支持多种 I/O 多路复用技术, epoll、 poll、 dev/poll、 select 和 kqueue 等;支持 I/O,定时器和信号等事件;注册事件优先级。
在创建、绑定和监听完成后,创建一个新的线程,实现的功能是初始化libevent,
pthread_create(&tid, NULL, initLibevent,this);
在initLibevent中初始化libevent,接着利用event_assign实现accept,接收socket消息
event_assign(&(event),bt, fd, EV_READ|EV_PERSIST, onAccept, p);
在onAccept函数中调用socket的accept接口接受数据,这样接收消息便有了一个独立的线程处理,不会阻塞了;
那么客户端发过来的消息如何都在accept中进行,势必会导致消息扎堆,处理不及时,为了解决这个问题,可以再创建一个线程专门处理客户端的消息:
event_new(bt, newfd, EV_READ|EV_PERSIST, onMsgRecv, pthis);
在onMsgRecv中调用socket的接口函数recv便实现了用独立线程接收客户端消息的功能。
但是,有一个问题没有解决:socket创建时一般都会绑定一个fd作为输入输出流的文件描述符,如果这个文件是以独占方式打开的,那后续处理的多线程是没有用的,因为处理会阻塞在该文件描述符上。
为了解决这个问题,需要修改文件的属性:
int flags = fcntl(fd, F_GETFL);
flags = flags | O_NONBLOCK;
fcntl(fd, F_SETFL, flags);
这样该socket就可以处理并发的请求了。