epoll的设计与实现

#include "epollserver.h"
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>

#define MAX_EPOLL_EVENT_COUNT 30000
#define EPOLL_WAIT_TIMEOUT    1000

namespace lyf
{
	EpollServer::EpollServer(void)
	{
		epfd = -1;
		listenfd = -1;
		port = 6880;
		epollEventList = new epoll_event[MAX_EPOLL_EVENT_COUNT];
		assert(epollEventList != NULL);
		thread = 0;
		stop = true;
	}

	EpollServer::~EpollServer(void)
	{
		if (epollEventList)
		{
			delete epollEventList;
			epollEventList = NULL;
		}
	}

	int EpollServer::SetNonblocking(int fd)
	{
		int opts;
		opts = fcntl(fd, F_GETFL);
		if(opts < 0)
		{
			printf("fcntl(fd, GETFL)");
			return -1;
		}
		opts = opts|O_NONBLOCK;
		if(fcntl(fd, F_SETFL, opts) < 0)
		{
			printf("fcntl(fd, SETFL, opts)");
			return -1;
		}

		return 0;
	}

	void* EpollServer::WorkProcProxy(void * param)
	{
		EpollServer * p = (EpollServer*) param;
		p->WorkProc();
	}

	int EpollServer::WorkProc(void)
	{
		listenfd = socket(AF_INET, SOCK_STREAM, 0);
		if (listenfd == -1)
		{
			printf("create socket failed\n");
			return -1;
		}

		int opt = 1;
		setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

		SetNonblocking(listenfd);

		sockaddr_in si;
		si.sin_family = AF_INET;
		si.sin_port = htons(port);
		si.sin_addr.s_addr = htonl(INADDR_ANY);

		if (0 != bind(listenfd, (sockaddr*)&si, sizeof(si)))
		{
			printf("bind socket failed, %s\n", strerror(errno));
			close(listenfd);
			return -1;
		}
		
		if (0 != listen(listenfd, 1024))
		{
			printf("listen socket failed, %s\n", strerror(errno));
			close(listenfd);
			return -1;
		}

		epfd = epoll_create(MAX_EPOLL_EVENT_COUNT);
		if (epfd == -1)
		{
			printf("epoll create failed, %s\n", strerror(errno));
			close(listenfd);
			return -1;
		}

		epoll_event e;
		e.events = EPOLLIN | EPOLLOUT | EPOLLET;
		e.data.fd = listenfd;

		if (epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &e) < 0)
		{
			printf("epoll ctrl failed, %s\n", strerror(errno));
			close(listenfd);
		}

		while (!stop)
		{
			int count = epoll_wait(epfd, epollEventList, MAX_EPOLL_EVENT_COUNT, EPOLL_WAIT_TIMEOUT);

			if (count < 0)
			{
				printf("epoll wait failed, %s\n", strerror(errno));
				exit(-1);
			}

			for (int i=0; i<count; i++)
			{
				epoll_event * p = &epollEventList[i];
				
				if (p->events & (EPOLLERR | EPOLLHUP))
				{
					printf("epoll events error, %s\n", strerror(errno));
					Connection * c = (Connection*)p->data.ptr;
					if (c)
					{
						OnClose(c);
					}
					continue;
				}

				HandleEvent(*p);
			}
		}

		if (listenfd != -1)
		{
			close(listenfd);
			listenfd = -1;
		}

		if (epfd != -1)
		{
			close(epfd);
			epfd = -1;
		}

		std::list<Connection*>::iterator it = connectionList.begin();
		for (; it!=connectionList.end(); it++)
		{
			Connection * c = *it;
			if (c && c->fd!=-1)
			{
				close(c->fd);
			}
			delete c;
		}

		return 0;
	}

	int EpollServer::HandleEvent(epoll_event & e)
	{
		if (e.data.fd == listenfd)
		{
			sockaddr_in si;
			socklen_t sl = sizeof(si);
			int fd = accept(listenfd, (struct sockaddr*)&si, &sl);
			if (fd == -1)
			{
				printf("accept socket failed, %s\n", strerror(errno));
				return -1;
			}
			//设置fd为非阻塞
			SetNonblocking(fd);

			Connection * c = new Connection;
			c->fd = fd;
			c->closed = 0;
			c->remoteAddress = si;

			epoll_event _e;
			_e.data.fd = fd;
			_e.data.ptr = c;
			_e.events = EPOLLIN|EPOLLOUT|EPOLLET;

			if (0 != epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &_e))
			{
				printf("epoll ctl add failed, %s\n", strerror(errno));
				close(fd);
				c->fd = -1;
				c->closed = 1;
				delete c;

				return -1;
			}
			else
			{
				OnAccept(c);
			}
		}
		else
		{
			Connection * c = (Connection*)e.data.ptr;
			
			if (e.events & EPOLLIN && !c->closed)
			{
				OnRead(c);
			}

			if (e.events & EPOLLOUT && !c->closed)
			{
				OnWrite(c);
			}
		}
	}

	int EpollServer::OnAccept(Connection * c)
	{
		connectionList.push_back(c);
		printf("a client connect to server: %d.\n", c->fd);
		return 0;
	}

	int EpollServer::OnClose(Connection * c)
	{
		printf("a client disconnect to server: %d.\n", c->fd);

		epoll_event e;
		e.data.fd = c->fd;
		e.events = EPOLLIN|EPOLLOUT|EPOLLET;

		if (0 != epoll_ctl(epfd, EPOLL_CTL_DEL, c->fd, &e))
		{
			printf("epoll event del failed, %s\n", strerror(errno));
		}

		close(c->fd);
		c->fd = -1;
		c->closed = 1;
		c->recvBuffer.clear();
		c->sendBuffer.clear();

		connectionList.remove(c);

		//delete c;

		return 0;
	}

	int EpollServer::OnError(Connection * c)
	{

		return 0;
	}

	int EpollServer::OnRead(Connection * c)
	{
		char buf[4096] = {0};
		int toread = 0;

		while (1)
		{
			memset(buf, 0, sizeof(buf));
			int bytes = recv(c->fd, buf, 4095, 0);
			if (bytes < 0)
			{
				if (errno == EINTR)
				{
					continue;
				}
				else if (errno == EAGAIN)
				{
					break;
				}
				else
				{
					return -1;
				}
			}
			else if (bytes == 0)
			{	
				OnClose(c);
				break;
			}
			else
			{
				c->recvBuffer.append(buf, bytes);
			}
		}// end while

		if (!c->recvBuffer.empty())
		{
			OnResponse(c);

			c->recvBuffer.clear();
		}

		return 0;
	}

	int EpollServer::OnWrite(Connection * c)
	{
		if (c->sendBuffer.empty())
		{
			c->writeReady = 1;
			printf("write ready, %d\n", c->fd);
		}
		else
		{
			int pos = 0;
			int towrite = c->sendBuffer.length();
			const char* buf = c->sendBuffer.c_str();

			do
			{
				int bytes = send(c->fd, buf+pos, towrite, 0);

				if (bytes < 0)
				{
					if (errno == EINTR)
					{
						continue;
					}
					else if (errno == EAGAIN)
					{
						break;
					}
					else
					{
						return -1;
					}
				}
				else if (bytes == 0)
				{
					OnClose(c);
				}
				else
				{
					pos += bytes;
					towrite -= bytes;
				}

			}while (towrite > 0);

			if (pos > 0)
			{
				printf("send bytes: %d\n", pos);
				pthread_mutex_lock(&c->mutex);
				c->sendBuffer.erase(0, pos);
				pthread_mutex_unlock(&c->mutex);
			}
		}

		return 0;
	}

	int EpollServer::PostSend(Connection *c)
	{
		if (c->writeReady)
		{
			int pos = 0;
			int towrite = c->sendBuffer.length();
			const char* buf = c->sendBuffer.c_str();

			do
			{
				int bytes = send(c->fd, buf+pos, towrite, 0);

				if (bytes < 0)
				{
					if (errno == EINTR)
					{
						continue;
					}
					else if (errno == EAGAIN)
					{
						break;
					}
					else
					{
						return -1;
					}
				}
				else if (bytes == 0)
				{
					OnClose(c);
				}
				else
				{
					pos += bytes;
					towrite -= bytes;
				}

			}while (towrite > 0);

			if (pos > 0)
			{
				printf("send bytes: %d\n", pos);
				pthread_mutex_lock(&c->mutex);
				c->sendBuffer.erase(0, pos);
				pthread_mutex_unlock(&c->mutex);
			}
		}
		else
		{
			c->writeReady = 0;
			printf("post send not ready\n");
		}

		return 0;
	}

	bool EpollServer::Start(void)
	{
		stop = false;
		if (0 != pthread_create(&thread, 0, WorkProcProxy, this))
		{
			printf("create thread failed, %s\n", strerror(errno));
			return false;
		}

		return true;
	}

	void EpollServer::Stop(void)
	{
		stop = true;
		pthread_join(thread, 0);
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值