Linux 下使用epoll实现转发服务器的小DEMO

一个小DEMO.  LINUX 下使用socket做的一个消息转发的示例. 

bterr.h

#ifndef _BTERR_H_
#define _BTERR_H_
#include <stdexcept>
#include <string>
using namespace std;
class btsock_err : public exception {
	private:
		string m_errstr;
	public:
		btsock_err (const string& errstr);
		~btsock_err (void) throw();
		const char* what (void) const throw ();
};
#endif //_BTERR_H_

bterr.cpp

#include "bterr.h"
btsock_err::btsock_err (const string& errstr) :
	m_errstr (errstr) {}
btsock_err::~btsock_err (void) throw() {}

const char* btsock_err::what (void) const throw()
{
	return m_errstr.c_str();
}

#ifndef _BTSOCKET_H_
#define _BTSOCKET_H_
#include <string>
#include <cstdlib>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/fcntl.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
using namespace std;
enum enum_btstatus {e_start = 1, e_init, e_bind, e_listen, e_accept, e_connect, e_end};
class Btsocket {
	private:
		string m_host;
		unsigned short m_port;
		int m_family;
		int m_socktype;
		int m_protocol;
		int m_fd;
		enum_btstatus m_status;

		static const int MAX_CLIENT;

		void initSocket (void);

	public:
		Btsocket (const string& host, unsigned short port, int max = 50,
				int family = AF_INET, int socktype = SOCK_STREAM, 
				int protocol = 0); 
		Btsocket (const char* host, unsigned short port, int max = 50,
				int family = AF_INET, int socktype = SOCK_STREAM, 
				int protocol = 0); 
		Btsocket (int fd);
		Btsocket (void);
		~Btsocket (void); 

		Btsocket& makeSocketNonBlock(void);

		const int getFd(void) const;

		Btsocket& bind (void);

		void listen (void);

		Btsocket accept(void);

		void connect (void);

		ssize_t send (const void* data, size_t size, int reset = 5) const;

		ssize_t recv (void* data, size_t size) const;
		
		void close (void) const;
};
#endif // _BTSOCKET_H_

btsocket.cpp

#include "btsocket.h"
#include "bterr.h"
#include <iostream>
void Btsocket::initSocket (void) 
{
	m_fd = socket (m_family, m_socktype, m_protocol);
	if (0 >  m_fd)
		throw btsock_err ("创建socket失败");
	m_status = e_init;
}

Btsocket& Btsocket::makeSocketNonBlock(void)
{
	int flags;
	flags = fcntl (m_fd, F_GETFL, 0);
	if (-1 == flags)
		throw ("set non blocking: 获取文件标志失败");
	flags |= O_NONBLOCK;
	if (-1 == fcntl(m_fd, F_SETFL, flags))
		throw ("set non blocking: 设置文件标志失败");

	return *this;
}

Btsocket::Btsocket(const string& host, unsigned short port, 
		int max, int family, int socktype, int protocol) :
	m_host (host), m_port (port), m_family (family),
	m_socktype (socktype), m_protocol (protocol), m_status (e_start) 
{
		initSocket();	
}

Btsocket::Btsocket (const char* host, unsigned short port,
		int max, int family, int socktype, int protocol) :
	m_host (string(host)), m_port (port),
	m_family (family), m_socktype (socktype), 
	m_protocol (protocol), m_status (e_start)
{
	initSocket();
}

Btsocket::Btsocket (int fd) : m_fd (fd), m_status (e_end)
	 {}

Btsocket::Btsocket (void) : m_fd (0), m_status (e_start)
	 {}
Btsocket::~Btsocket (void)
{
}

const int Btsocket::getFd(void) const
{
	return m_fd;
}

Btsocket& Btsocket::bind (void)
{
	if (m_status != e_init)
		throw btsock_err ("当前状态不可绑定");
	sockaddr_in serverAddr;
	serverAddr.sin_family = m_family;
	serverAddr.sin_addr.s_addr = inet_addr(m_host.c_str());
	serverAddr.sin_port = htons (m_port);

	if (0 > ::bind (m_fd, reinterpret_cast<struct sockaddr*>(&serverAddr), sizeof(serverAddr)))
		throw btsock_err ("绑定失败");
	m_status = e_bind;
	return *this;
}

void Btsocket::listen(void)
{
	if (m_status != e_bind)
		throw btsock_err ("当前状态不可监听");
	if (0 > ::listen (m_fd, MAX_CLIENT))
		throw btsock_err ("监听失败");
	m_status = e_listen;
}

Btsocket Btsocket::accept(void)
{
	if (m_status != e_listen)
		throw btsock_err ("当前状态不可接受连接");
	sockaddr in_addr;
	socklen_t  in_len;
	int cfd;
	in_len = sizeof (in_addr);
	cfd = ::accept (m_fd, &in_addr, &in_len);
	if (0 > cfd)
		throw btsock_err ("accept失败");
	//m_status = e_accept;
	Btsocket client(cfd);
	client.makeSocketNonBlock();
	return client; // 构造客户端socket
	
}

void Btsocket::connect (void) 
{
	if (m_status != e_init)
		throw btsock_err ("当前状态不可连接服务器");
	sockaddr_in serverAddr;
	serverAddr.sin_family = m_family;
	serverAddr.sin_addr.s_addr = inet_addr(m_host.c_str());
	serverAddr.sin_port = htons (m_port);
	if (0 > ::connect (m_fd, (struct sockaddr*)&serverAddr, sizeof (sockaddr)))
		throw btsock_err ("连接失败");
	m_status = e_connect;
}

ssize_t Btsocket::send(const void* data, size_t size, int reset) const
{
	if (m_status != e_connect && m_status != e_end)
		throw btsock_err("当前状态不可发送数据");
	ssize_t sendSize = 0, send = 0;
	char* _data = static_cast<char*>(const_cast<void*>(data));
	do {
		sendSize = ::send (m_fd, _data, size, 0);
		_data = _data + sendSize;
		size = size - sendSize;
		send += sendSize;
		--reset; //重试次数
	} while (sendSize < size || reset > 0);
	return send;
}

ssize_t Btsocket::recv(void* data, size_t size) const
{
	//std::cout << "接收时状态: " << m_status << std::endl;
	if (m_status != e_connect && m_status != e_end)
		throw btsock_err("当前状态不可接收数据");
	return ::recv(m_fd, data, size, MSG_DONTWAIT);
}

void Btsocket::close(void) const
{
	::close(m_fd);	
}
const int Btsocket::MAX_CLIENT = 50;

服务器代码测试

#include <iostream>
#include "btsocket.h"
#include "bterr.h"
#include <sys/epoll.h>
#include <map>
#define MAXEVENTS 64
using namespace std;

int main(int argc, char* argv[])
{
	try {
		epoll_event event, *events;
		int efd, s, clientNum = 0;
		//Btsocket client[1024];
		map<int, Btsocket> clientMap;
		Btsocket server("127.0.0.1", 7777);
		server.makeSocketNonBlock().bind().listen();
		efd = epoll_create1(0);
		if (-1 == efd)
			throw btsock_err("创建epoll失败");
		event.data.fd = server.getFd();
		event.events = EPOLLIN | EPOLLET;
		s = epoll_ctl (efd, EPOLL_CTL_ADD, server.getFd(), &event);
		if (-1 == s)
			throw btsock_err ("添加服务到epoll失败");
		events = new epoll_event;
		for (;;)
		{
			int n, i, j;
			n = epoll_wait (efd, events, MAXEVENTS, -1);
			for (i = 0; i < n; ++i)
			{
				if ((events[i].events & EPOLLERR) || 
						(events[i].events & EPOLLHUP) ||
						(!(events[i].events & EPOLLIN)))
				{
					if (clientMap.find(events[i].data.fd) != clientMap.end())
						clientMap.erase(events[i].data.fd);
					close (events[i].data.fd);
					epoll_ctl(efd, EPOLL_CTL_DEL, events[i].data.fd, &(events[i]));
					continue;
				}
				else if (server.getFd() == events[i].data.fd)
				{
					Btsocket c = server.accept();
					clientMap.insert(pair<int, Btsocket>(c.getFd(), c)); 
					event.data.fd = c.getFd(); //需要把client 保存起来, 以执行析构
					event.events = EPOLLIN | EPOLLET;
					
					s = epoll_ctl(efd, EPOLL_CTL_ADD, c.getFd(), &event);
					if (-1 == s)
					{
						cout << "添加客户端到epoll失败" << endl;
						continue;
					}
				}
				else
				{
					ssize_t count = 0;
					char buf[1024] = {0};
					count = clientMap[events[i].data.fd].recv((void*) buf, 1024);
					for (map<int, Btsocket>::iterator it = clientMap.begin(); it != clientMap.end(); ++it)
						it->second.send((void*)buf, count);
				}
			}
		}
		server.close();
		for (map<int, Btsocket>::iterator  it = clientMap.begin(); it != clientMap.end(); ++it)
			it->second.close();
	} catch (exception& ex) {
		cout << ex.what() << endl;
		return -1;
	}
	return 0;
}

客户端测试:

#include <iostream>
#include "btsocket.h"
#include "bterr.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
using namespace std;

int main(int argc, char* argv[])
{
	try {
		Btsocket server("127.0.0.1", 7777);
		server.connect();
		fd_set fset;
		struct timeval tval = {1, 0};
		FILE* fd = stdin;
		char buf[1024] = "";
		FD_ZERO(&fset);
		for (;;)
		{
			FD_SET (server.getFd(), &fset);	
			FD_SET (fileno(fd), &fset);
			select (50, &fset, NULL, NULL, &tval);
			if (FD_ISSET(server.getFd(), &fset))
			{
				server.recv((void*)buf, 1024);
				cout << "接收到:" << buf << endl;
			}

			if (FD_ISSET(fileno(fd), &fset))
			{
				fgets(buf, 1024, stdin);
				ssize_t count = server.send((void*)buf, strlen(buf));
				cout << "已发送:" << count << " Bytes" << endl;
			}
		}
		server.close();
	} catch (exception& ex) {
		cout << ex.what() << endl;
		return -1;
	}

	return 0;
}

实现结果: 

客户端A:


客户端B:


大概是这样, 没怎么测试, 基本能实现转发效果. 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值