您的位置:首页 > 理论基础 > 计算机网络

网络编程(10)基于epoll的服务器实现

2014-02-16 23:21 281 查看
       关于epoll的相关知识我不就写了,只把我弄的例子写下,我整的这个epoll服务器其实就只是把man epoll中举得那个例子给做完善了。

主要参考了man epoll和百度百科中关于epoll的介绍来实现的。

服务器代码:

/*************************************************
Author: xiongchuanliang
Description: 基于epoll的服务器,就是把man epoll中的例子补充完整了。

编译命令:
Linux:
g++ -o tcpepoll2 tcpepoll2.cpp -m64 -I./common

**************************************************/

// 服务端代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "initsock.h"
#include "common.h"

#include <sys/epoll.h>

CInitSock initSock;

#define MAX_EVENTS 10

int do_use_fd(int client);	//与客户端交互
int setnonblocking(int sock); // 设置套接字为不阻塞

int main(int argc, char* argv[])
{
int n = 0;

//创建套接字
SOCKET sListen = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(sListen == INVALID_SOCKET)
{
PrintError("socket() failed.\n");
exit(EXIT_FAILURE);
}

//绑定本地IP和端口到套接字
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SERVPORT); //大于1024且小于65535
server_addr.sin_addr.s_addr = INADDR_ANY;
bzero(&(server_addr.sin_zero),8);

//SO_REUSEADDR : 使bind函数能允许地址立即重用
int on = 1;
setsockopt( sListen, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on) );

if(bind(sListen,(struct sockaddr *)&server_addr,sizeof(struct sockaddr)) == SOCKET_ERROR)
{
PrintError("bind() failed.");
exit(EXIT_FAILURE);
}

//开始监听
if(listen(sListen, BACKLOG) == SOCKET_ERROR)
{
PrintError("sListen() failed.");
exit(EXIT_FAILURE);
}

struct epoll_event ev,events[MAX_EVENTS];
SOCKET  conn_sock, nfds, epollfd; //listen_sock

//指定内核需要监听的描述数
//函数原型:int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epollfd = epoll_create(10);
if (epollfd == -1) {
PrintError("epoll_create");
exit(EXIT_FAILURE);
}

/*
EPOLLLT 默认行为,只要一个文件描述符处于就绪状态,epoll 就会不停的通知你有事件发生。传统的 select/poll 都是这样处理。
EPOLLET 属新方式,只在一个文件描述符新处于就绪的时候通知一次,之后不管数据有没有读完,都不会再通知,当然,有新数据到还是会通知的。
所以,用 EPOLLET 的时候,一定要把文件描述符设置为 non-blocking,
而且最好是一直读数据,读到返回 EAGAIN 才停下
*/

//指定内核需要监听的事件,并注册一个新的fd到epoll_create()返回的epollfd中
ev.events = EPOLLIN | EPOLLET;   //对读感兴趣,边沿触发模式

ev.data.fd = sListen;
//函数原型:int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sListen, &ev) == -1) {
PrintError("epoll_ctl: listen_sock");
exit(EXIT_FAILURE);
}

socklen_t nAddrlen = sizeof(struct sockaddr_in);
for (;;) {

//函数原型: int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
//MAX_EVENTS的值不能大于 epoll_create()指定的值
//等待epoll_ctl指定的监听的事件发生
nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
if (nfds == -1) {
<span style="white-space:pre">	</span>continue; //   PrintError("epoll_pwait()");
}

//遍历发生了指定监听的事件事件的fd
for (n = 0; n < nfds; ++n) {
// 如果是主socket的事件的话,则表示
// 有新连接进入了,进行新连接的处理。
if (events
.data.fd == sListen) { //连接指定的客户端
// conn_sock = accept(sListen,(struct sockaddr *) &local, &addrlen);
conn_sock = accept(sListen,(struct sockaddr *) &server_addr, &nAddrlen);
if (conn_sock == -1) {
PrintError("accept()");
continue;
}

// 将新连接设置为非阻塞模式
setnonblocking(conn_sock);

//设置event为监控读操作,即客户端有发送数据过来。
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = conn_sock;
//将新设置的这个event加入到epoll的监听队列里
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock,
&ev) == -1) {
PrintError("epoll_ctl: conn_sock");
}
}else{
do_use_fd(events
.data.fd);
}

} //end for (n = 0; n < nfds; ++n)
} //end for (;;)

//关闭监听套接字
close(sListen);
//关闭epoll句柄
close(epollfd);

exit(EXIT_SUCCESS);
}

//与客户端交互
int  do_use_fd(int client)
{
char recvData[MAXDATASIZE]={0};
int recvbytes = read(client,recvData, MAXDATASIZE);
if( recvbytes == 0)
{
printf("read() no data!\n");

}else if( recvbytes < 0)
{
if (errno != EAGAIN)
{
PrintError("read() failed.");
close (client);
}
}else if( recvbytes > 0)
{
recvData[recvbytes]='\0';
printf("msg:%s\n",recvData);

char sendData[MAXDATASIZE]={0};
strcpy(sendData,"Hello client!\n");
int sendbytes = write(client, sendData, sizeof(sendData)) ;
if( sendbytes  < 0)
{
PrintError("write() failed.");
close (client);
}else{
printf("write() success! %d \n",sendbytes);
}
}

}

// 设置套接字为不阻塞
int setnonblocking(int sock)
{
int flags = fcntl (sock, F_GETFL, 0);
if (flags == -1)
{
PrintError("fcntl() failed.");
return -1;
}

flags |= O_NONBLOCK;
if ( (fcntl (sock, F_SETFL, flags)) == -1)
{
PrintError("fcntl() failed. O_NONBLOCK");
return -1;
}

return 0;
}


代码中用到的头文件在<<网络编程(1)跨平台的Socket同步阻塞工作模式例子>>

 

MAIL: xcl_168@aliyun.com

BLOG: http://blog.csdn.net/xcl168
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息