Linux 下使用epoll实现转发服务器的小DEMO
2014-05-30 11:26
381 查看
一个小DEMO. LINUX 下使用socket做的一个消息转发的示例.
bterr.h
bterr.cpp
btsocket.cpp
服务器代码测试
客户端测试:
实现结果:
客户端A:
客户端B:
大概是这样, 没怎么测试, 基本能实现转发效果.
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:
大概是这样, 没怎么测试, 基本能实现转发效果.
相关文章推荐
- Linux非阻塞IO(八)使用epoll重新实现非阻塞的回射服务器
- Linux网络编程 使用epoll实现一个高性能TCP Echo服务器
- Linux非阻塞IO(八)使用epoll重新实现非阻塞的回射服务器
- 使用 ipmitool 实现 Linux 系统下对服务器的 ipmi 管理
- 服务器间通过ssh使用密钥对实现无密码登录solaris,redhat,linux
- 使用RHEL5.5配置DNS服务,实现主辅DNS服务器同步以及DNS转发服务器的配置
- linux下使用select I/O复用和多线程编程实现简单的TCP服务器
- 使用Linux的rsync命令实现:多服务器镜像同步
- 使用Linux的rsync命令实现 多服务器镜像同步
- Linux下使用Daemon实现服务器永久存活
- 使用epoll 在 linux 上开发高性能应用服务器【epoll】
- 使用Linux的rsync命令实现:多服务器镜像同步
- 使用 ipmitool 实现 Linux 系统下对服务器的 ipmi 管理
- 使用 ipmitool 实现 Linux 系统下对服务器的 ipmi 管理
- 使用epoll 在 linux 上开发高性能应用服务器
- 使用 ipmi实现Linux系统下对服务器的管理
- 使用Linux的rsync命令实现:多服务器镜像同步
- 【转】linux中使用命名管道实现客户端/服务器模型的进程间通信
- Linux中使用rsync实现多服务器之间文件同步问题
- 使用Linux的SSH端口转发实现代理上网