转自:http://blog.163.com/huchengsz@126/blog/static/73483745201181824629285/
01
//epoll_wait范围之后应该是一个循环,遍利所有的事件:
02 for (n = 0; n < nfds; ++n)
03 {
04 if (events[n].da ta.fd
== listener)
05 { //如果是主socket的事件的话,则表示有新连接进入了,进行新连接的处理。
06 client = accept (listener, ( struct sockaddr *) &local, &addrlen);
07 if (client < 0)
08 {
09 perror ( "accept");
10 continue;
11 }
12 setnonblocking (client); // 将新连接置于非阻塞模式
13 ev.events = EPOLLIN | EPOLLET; // 并且将新连接也加入EPOLL的监听队列。
14 //注意,这里的参数EPOLLIN | EPOLLET并没有设置对写socket的监听,
15 //如果有写操作的话,这个时候epoll是不会返回事件的,
16 //如果要对写操作也监听的话,应该是EPOLLIN | EPOLLOUT | EPOLLET
17 ev.da ta.fd
= client;
18 if (epoll_ctl (kdpfd, EPOLL_CTL_ADD, client, &ev) < 0)
19 {
20 /*
21 设置好event之后,将这个新的event通过epoll_ctl加入到epoll的监听队列里面,
22 这里用EPOLL_CTL_ADD来加一个新的epoll事件,通过EPOLL_CTL_DEL来减少一个epoll事件,通
23 过EPOLL_CTL_MOD来改变一个事件的监听方式.
24 */
25 fprintf (stderr, "epoll set insertion error: fd=%d", client);
26 return - 1;
27 }
28 }
29 else // 如果不是主socket的事件的话,则代表是一个用户socket的事件,
30 do_use_fd (events[n].da ta.fd);
//则来处理这个用户socket的事情,比如说read(fd,xxx)之类的,或者一些其他的处理。
31 }
02 for (n = 0; n < nfds; ++n)
03 {
04 if (events[n].da
05 { //如果是主socket的事件的话,则表示有新连接进入了,进行新连接的处理。
06 client = accept (listener, ( struct sockaddr *) &local, &addrlen);
07 if (client < 0)
08 {
09 perror ( "accept");
10 continue;
11 }
12 setnonblocking (client); // 将新连接置于非阻塞模式
13 ev.events = EPOLLIN | EPOLLET; // 并且将新连接也加入EPOLL的监听队列。
14 //注意,这里的参数EPOLLIN | EPOLLET并没有设置对写socket的监听,
15 //如果有写操作的话,这个时候epoll是不会返回事件的,
16 //如果要对写操作也监听的话,应该是EPOLLIN | EPOLLOUT | EPOLLET
17 ev.da
18 if (epoll_ctl (kdpfd, EPOLL_CTL_ADD, client, &ev) < 0)
19 {
20 /*
21 设置好event之后,将这个新的event通过epoll_ctl加入到epoll的监听队列里面,
22 这里用EPOLL_CTL_ADD来加一个新的epoll事件,通过EPOLL_CTL_DEL来减少一个epoll事件,通
23 过EPOLL_CTL_MOD来改变一个事件的监听方式.
24 */
25 fprintf (stderr, "epoll set insertion error: fd=%d", client);
26 return - 1;
27 }
28 }
29 else // 如果不是主socket的事件的话,则代表是一个用户socket的事件,
30 do_use_fd (events[n].da
31 }
对,epoll的操作就这么简单,总共不过4个 API:epoll_create, epoll_ctl, epoll_wait和close。
如果您对epoll的效率还不太了解,请参考我 之前关于网络游戏的网络编程等相关的文章。
以前公司的服务器都是使用HTTP连接,但是这样的话,在手机目前的网络情况下不但显得速度较慢,而且不稳定。因此大家一致同意用 SOCKET来进行连接。虽然使用SOCKET之后,对于用户的费用可能会增加(由于是用了CMNET而非CMWAP),但是,秉着用户体验至上的原则, 相信大家还是能够接受的(希望那些玩家月末收到帐单不后能够保持克制...)。
这次的服务器设计中,最重要的一个突破,是使用了EPOLL模型, 虽然对之也是一知半解,但是既然在各大PC网游中已经经过了如此严酷的考验,相信他不会让我们失望,使用后的结果,确实也是表现相当不错。在这里,我还是 主要大致介绍一下这个模型的结构。
6、Linux下EPOll编程实例
EPOLL模型似乎只有一种格式,所以大家只要参考我下面的代码, 就能够对EPOLL有所了解了,代码的解释都已经在注释中:
01
while (TRUE)
02 {
03 int nfds = epoll_wait (m_epoll_fd, m_events, MAX_EVENTS, EPOLL_TIME_OUT); //等待EPOLL时间的发生,相当于监听,
04 //至于相关的端口,需要在初始化EPOLL的时候绑定。
05 if (nfds <= 0)
06 continue;
07 m_bOnTimeChecking = FALSE;
08 G_CurTime = time ( NULL);
09 for ( int i = 0; i < nfds; i ++)
10 {
11 try
12 {
13 if (m_events[i].da ta.fd
== m_listen_http_fd)
//如果新监测到一个HTTP用户连接到绑定的HTTP端口,
14 //建立新的连接。由于我们新采用了SOCKET连接,所以基本没用。
15 {
16 On AcceptHttpEpoll ();
17 }
18 else if (m_events[i].da ta.fd
== m_listen_sock_fd)
//如果新监测到一个SOCKET用户连接到了绑定的SOCKET端口,
19 //建立新的连接。
20 {
21 On AcceptSockEpoll ();
22 }
23 else if (m_events[i].events & EPOLLIN) //如果是已经连接的用户,并且收到数据,那么进行读入。
24 {
25 On ReadEpoll (i);
26 }
27
28 On WriteEpoll (i);
//查看当前的活动连接是否有需要写出的数据。
29 }
30 catch ( int)
31 {
32 PRINTF ( "CATCH捕获错误 \n ");
33 continue;
34 }
35 }
36 m_bOnTimeChecking = TRUE;
37 On Timer ();
//进行一些定时的操作,主要就是删除一些短线用户等。
38 }
02 {
03 int nfds = epoll_wait (m_epoll_fd, m_events, MAX_EVENTS, EPOLL_TIME_OUT); //等待EPOLL时间的发生,相当于监听,
04 //至于相关的端口,需要在初始化EPOLL的时候绑定。
05 if (nfds <= 0)
06 continue;
07 m_bOnTimeChecking = FALSE;
08 G_CurTime = time ( NULL);
09 for ( int i = 0; i < nfds; i ++)
10 {
11 try
12 {
13 if (m_events[i].da
14 //建立新的连接。由于我们新采用了SOCKET连接,所以基本没用。
15 {
16 On
17 }
18 else if (m_events[i].da
19 //建立新的连接。
20 {
21 On
22 }
23 else if (m_events[i].events & EPOLLIN) //如果是已经连接的用户,并且收到数据,那么进行读入。
24 {
25 On
26 }
27
28 On
29 }
30 catch ( int)
31 {
32 PRINTF ( "CATCH捕获错误 \n ");
33 continue;
34 }
35 }
36 m_bOnTimeChecking = TRUE;
37 On
38 }