一、epoll模型的构建

由于网络服务高并发的需求,一般socket网络模型都采用epoll模型,有关epoll模型的原理在相关论坛中有许多讲述,在此不做重复讲解,主要讲一讲epoll模型的封装实现。

EPoller类的具体实现如下代码所示:

class EPoller
{
public: int Create(int maxfd);
void JoinSocket(Socket* sock, unsigned int flag);
void DelSocket(CEpollSocket* sock);
int LoopForEvent(int timeout);
int EpollWait(int timeout);
protected:
int epoll_fd; //epoll文件描述符
epoll_event* p_events; //用于保存epoll_wait返回的事件
int max_fd; //最大监听的文件描述符数量 }; // 创建epoll模型
int EPoller::Create(int maxfd)
{
max_fd = maxfd ;
epoll_fd = epoll_create(maxfd); p_events = new epoll_event[maxfd];
memset(p_events, , sizeof(epoll_event) * maxfd);
return ;
} //将某个fd添加至监听的队列中,并利用flag设置监听的事件类型
//其中Socket为基类
int EPoller::JoinSocket(Socket* sock, unsigned int flag)
{
int fd = sock->GetSockHandle();
epoll_event event;
event.data.ptr = sock;
event.events = flag|EPOLLHUP|EPOLLERR; if (epoll_ctl(_epoll_fd, EPOLL_CTL_MOD , fd, &ev) < )
{
if (epoll_ctl(_epoll_fd, EPOLL_CTL_ADD , fd, &ev) < )
{
return -;
}
} return ;
} //删除指定的fd
int EPoller::DelSocket(Socket* sock)
{
int fd = sock->GetSockHandle();//利用Socket对象取得相应的文件句柄
if ( fd > )
{
epoll_event ev;
ev.data.ptr = sock;
ev.events = ;
if (epoll_ctl(_epoll_fd, EPOLL_CTL_DEL, fd, &ev) < )
{
return -;
} return ;
} return -;
} //等待监控的事件触发
int EPoller::EpollWait(int timeout)
{
int fd;
int nfds;
Socket* sock;
unsigned int flag; nfds = epoll_wait(epoll_fd, p_events, max_fd, timeout);
if (nfds < )
{
return -;
}
else if ( nfds == )
{
return ;
} for (int i=; i<nfds; i++)
{
sock = (Socket*)((p_events+i)->data.ptr);
if ( sock == NULL )
{
continue;
}
flag = (p_events+i)->events;
if (flag & EPOLLIN)
{
sock->Recv();
}
else if (flag & EPOLLOUT)
{
sock->Send();
}
else if (flag & EPOLLHUP)
{
sock->Close();
}
else if (flag & EPOLLERR)
{
sock->Error();
}
} return nfds ;
}
// 循环等待事件的触发
int EPoller::LoopForEvent(int timeout)
{
while(true)
{
int result = EpollWait(timeout);
if( result <= )
{
return result;
}
}
}

其中的Socket类型作为其他网络通信服务类的基类,主要为其他的socket网络通信类提供一个统一的入口,其结构定义如下:

class Socket
{
friend class EPoller;
public:
int GetSockHandle();
virtual int SendMsg(char* buffer, int length); protected:
virtual int Recv();
virtual int Send();
virtual int Close();
virtual int Error(); EPoller* epoller;
int fd; } int Socket::SendMsg(char* buff, int len)
{
  int ret;
  int send_len = 0;
  int _len = len;
  errno = 0;
  while(_len > 0)
  {
    ret = send(fd, (void*)(buff+send_len), _len, 0);
    if (ret <= 0)
    {
      if (errno == EINTR || errno == EAGAIN)
      {
        usleep(10);
        continue;
      }
      break;
    }
    send_len += ret;
    _len -= ret;
  }
  return send_len;
}

二、UDP网络通信的实现

//Udp创建socket的流程
int CreateUdpSocket()
{
// 1.创建socket
sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); // 2.设置非阻塞
fcntl(sock_fd, F_GETFL, );
fcntl(sock_fd, F_SETFL, val | O_NONBLOCK); // 3.绑定端口与ip
struct sockaddr_in addr;
string ip = "0.0.0.0";
addr.sin_family = AF_INET;
addr.sin_port = htons();
ip2uint(ip.c_str(), &(addr.sin_addr));
bind(sock_fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)); // 4.设置socket缓存大小
int recv_buf_size = ;
int send_buf_size = ;
setsockopt(sock_fd, SOL_SOCKET, SO_RCVBUF, (char *)&recv_buf_size, sizeof(recv_buf_size));
setsockopt(sock_fd, SOL_SOCKET, SO_SNDBUF, (char *)&send_buf_size, sizeof(send_buf_size));
return ;
} // 接收数据
if ((len = recvfrom(sock, buffer, len, , (struct sockaddr *)&addr, &addrlen)) < )
{
if (errno == EAGAIN || errno == EINTR)
{
len = ;
return ;
}
else
{
return -;
}
}

三、TCP网络通信的实现

//Tcp创建
bool CreateTcpServer()
{
//建立TCP套接字
sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); struct sockaddr_in svr_addr= {};
svr_addr.sin_port = htons(port);
svr_addr.sin_family = AF_INET;
inet_pton(AF_INET, ip.c_str(), &(svr_addr.sin_addr)); // 绑定ip地址
if(bind(sock_fd, (struct sockaddr*)&svr_addr, sizeof(struct sockaddr_in)) < ) // 设置socket属性
int recv_buf_size = * ;
int send_buf_size = * ;
if (setsockopt(_sock_fd, SOL_SOCKET, SO_RCVBUF, (char*)&recv_buf_size, sizeof(recv_buf_size)) < )
if(setsockopt(_sock_fd, SOL_SOCKET, SO_SNDBUF, (char*)&send_buf_size, sizeof(send_buf_size)) < )
int reuse=;
if(setsockopt(_sock_fd, SOL_SOCKET, SO_REUSEADDR, (char*)&reuse, sizeof(reuse)) < ) // 设置非阻塞
int val = fcntl(_sock_fd, F_GETFL, );
if(fcntl(_sock_fd, F_SETFL, val | O_NONBLOCK) == -) // 监听socket
if(listen(sock_fd,) < )
{
close(sock_fd);
sock_fd = -;
return false;
}
return true ;
}
// 一种hash值的生成算法
int string2hash(const char * str, const int& len, const int& hash_size)
{
int hashvalue = ;
for (int i = len-; i < len; ++i)
{
hashvalue = ((unsigned char)(*(str + i)) + hashvalue) % hash_size;
}
return hashvalue;
} //接收数据流程
int TCP_recv()
{
char recv_buff[];
int total_recv_len = ; int clientfd = accept(_sock_fd,(struct sockaddr *)NULL,(socklen_t*)NULL);
if (clientfd <= )
{
return -;
} //设置发送缓冲区和接收缓冲区大小
int send_buff_size = ;
int recv_buff_size = ;
int set_result = ;
set_result = setsockopt(clientfd, SOL_SOCKET, SO_RCVBUF, (char*)&recv_buff_size, sizeof(recv_buff_size));
set_result = setsockopt(clientfd, SOL_SOCKET, SO_SNDBUF, (char*)&send_buff_size, sizeof(send_buff_size)); int free_len = sizeof(recv_buff) - total_recv_len ;
if(free_len <= || total_recv_len >= )
{
//包太长了
return ;
} //接收数据
int recv_len = ;
do
{
recv_len = recv(sock_fd, &recv_buff[total_recv_len], free_len, );
}
while((recv_len < ) && (errno == EINTR)); if(recv_len <= )
{
// 对端连接关闭
if( recv_len == )
{
return ;
}
else if( errno == EAGAIN)
{
//暂时阻塞
return ;
}
else
{
return ;
}
} total_recv_len += recv_len;
// 解析命令
int offset = ParseCmd(recv_buff, total_recv_len);
if( == offset)
{
//未接收完
return ;
}
// 完成解析后,将已处理后的数据清空,使留下的未处理数据移到缓存区的起始位置。
else if( < offset)
{
//解析成功,单条命令
total_recv_len = total_recv_len - offset;
memmove(recv_buff, recv_buff + offset, total_recv_len);
} return ; } // 与服务端建立socket长连接的方法
int fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
int recv_buf_size = ;
int send_buf_size = ; setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *)&recv_buf_size, sizeof(recv_buf_size));
setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *)&send_buf_size, sizeof(send_buf_size)); struct sockaddr_in addr;
memset(&addr, , sizeof(struct sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(ip.c_str()); int argc = fcntl(fd, F_GETFL, );
if (fcntl(fd, F_SETFL, argc | O_NONBLOCK) == -)
{
close(fd);
return false;
} int ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
if ( > ret)
{
bool bConnected = false; struct pollfd conncet_client[];
int _nfd = ;
memset(&_conncet_client[], , sizeof(pollfd));
conncet_client[].fd = fd;
conncet_client[].events = POLLOUT; int can_write = ::poll(_conncet_client, _nfd, (int)());
if (can_write > && (_conncet_client[].revents & POLLOUT) > )
{
bConnected = true;
}
}

socket网络服务实战的更多相关文章

  1. 【linux高级程序设计】(第十三章)Linux Socket网络编程基础 4

    网络调试工具 tcpdump 功能:打印指定网络接口中与布尔表达式匹配的报头信息 关键字: ①类型:host(默认).net.port host 210.27.48.2 //指明是一台主机 net 2 ...

  2. Socket网络编程(2)--服务端实现

    中秋了,首先祝大家中秋快乐,闲着无事在家整一个socket的聊天程序,有点仿QQ界面,就是瞎折腾,不知道最后是不是能将所有功能实现. 如果你对socket不了解,请看这篇文章:http://www.c ...

  3. keepalived安装配置实战心得(实现高可用保证网络服务不间断)

    keepalived安装配置实战心得(实现高可用保证网络服务不间断) 一.准备2台虚拟机     安装的系统是:centos-release-7-1.1503.el7.centos.2.8.x86_6 ...

  4. socket 网络编程高速入门(一)教你编写基于UDP/TCP的服务(client)通信

    由于UNIX和Win的socket大同小异,为了方便和大众化,这里先介绍Winsock编程. socket 网络编程的难点在入门的时候就是对基本函数的了解和使用,由于这些函数的结构往往比較复杂,參数大 ...

  5. Socket网络编程-SocketServer

    Socket网络编程-SocketServer 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.SocketServer概述 socket编程过于底层,编程虽然有套路,但是想要写 ...

  6. Socket网络编程-TCP编程

    Socket网络编程-TCP编程 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.socket介绍 1>.TCP/IP协议 2>.跨网络的主机间通讯 在建立通信连接的 ...

  7. SSH远程管理服务实战

    目录 SSH远程管理服务实战 1.SSH基本概述 2.SSH相关命令 3.Xshell连接不上虚拟机 4.scp命令 5.sftp命令 6.SSH验证方式 7.SSH场景实践 8.SH安全优化 9.交 ...

  8. Linux Socket 网络编程

    Linux下的网络编程指的是socket套接字编程,入门比较简单.在学校里学过一些皮毛,平时就是自学玩,没有见识过真正的socket编程大程序,比较遗憾.总感觉每次看的时候都有收获,但是每次看完了之后 ...

  9. Python Socket 网络编程

    Socket 是进程间通信的一种方式,它与其他进程间通信的一个主要不同是:它能实现不同主机间的进程间通信,我们网络上各种各样的服务大多都是基于 Socket 来完成通信的,例如我们每天浏览网页.QQ ...

随机推荐

  1. Sentry入门

    指令类型: 创建.删除角色: CREATE ROLE sentry_all; droop role admin_role;   角色授权/撤销权限: 数据库级别角色授权 GRANT ALL ON DA ...

  2. BZOJ4009:[HNOI2015]接水果(整体二分版)

    浅谈离线分治算法:https://www.cnblogs.com/AKMer/p/10415556.html 题目传送门:https://lydsy.com/JudgeOnline/problem.p ...

  3. [转] Linux 查找文件内容

    Linux查找文件内容的常用命令方法. 从文件内容查找匹配指定字符串的行: $ grep "被查找的字符串" 文件名例子:在当前目录里第一级文件夹中寻找包含指定字符串的.in文件g ...

  4. HTTP-Runoob:HTPP消息结构

    ylbtech-HTTP-Runoob:HTPP消息结构 1.返回顶部 1. HTTP 消息结构 HTTP是基于客户端/服务端(C/S)的架构模型,通过一个可靠的链接来交换信息,是一个无状态的请求/响 ...

  5. phonegap中使用自带浏览器打开链接

    <center><a id="ssl2" href="#" onclick="window.open('http://127.0.0 ...

  6. The lesser known pitfalls of allowing file uploads on your website

    These days a lot of websites allow users to upload files, but many don’t know about the unknown pitf ...

  7. Java 的编译和运行机制

    创建一个 名为 test.java 的 Java 源文件 源代码: class Hello{ public static void main(String[] args) { System.out.p ...

  8. 利用Synergy在局域网内让Ubuntu和Windows 7两台机器共用一套键鼠。

    一个主机可以连接多个显示器, 方便自己使用, 但是这只是一个系统分屏显示, 如果想用两台不同系统的电脑, 并且还不想老是在两套键鼠之间来回转换, 那么建议你可以用Synergy软件来实现多台电脑之间的 ...

  9. javaScript之NodeList

    NodeList对象 是DOM操作取出的集合(实际上是基于DOM结构动态查询的结果),用来保存一组有序的节点,可以通过位置来访问这些节点,它并不是array的实例. Nodelist最大的特点就是它的 ...

  10. Android studio导入svn工程

    Quick Start——> Check outproject from Version——> Subversion——> ‘+’加号 ——> 输入网址 ——> 注意选择 ...