首页 > 其他 > 详细

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

时间:2014-02-17 15:06:34      阅读:387      评论:0      收藏:0      [点我收藏+]

         epoll听说很厉害的,走马观花的研究了下,好像是很牛。具体没空细搞了,哥最近准备动动,作为一个不务正业的DBA,数据库和UNIX是时候

得温习温习了。所以匆匆实现了个基于epoll的服务器作为最近socket研究的一个暂时的终结。

         其实最近一口气整了整socket收获还是很大的,虽然还是有很多不理解和疑问,也没关系了,这么深的水,留着以后再说。至少概念又清楚了很多,

基础打的牢,那些libevent,asio之流理解起来那还不是很容易的事?

        关于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) {
		   PrintError("epoll_pwait()");		 
	   }

	   //遍历发生了指定监听的事件事件的fd
	   for (n = 0; n < nfds; ++n) {		
   			// 如果是主socket的事件的话,则表示
			// 有新连接进入了,进行新连接的处理。
		   if (events[n].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[n].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




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

原文:http://blog.csdn.net/xcl168/article/details/19302693

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!