服务端而言,对于每一个新的连接我们都需要去保存其基本信息,如ip地址,套接字fd,也需要赋予其唯一标识如用户名。 这里,我们来谈谈对用户连接的封装。
关于Buffer细节可看Epoll-ET模式下非阻塞读写之Buffer的封装
本身 send和recv都是线程安全的。 因为读操作,我们可以确定只会有一个线程来对读操作进行处理(通过设置EPOLLONESHOT),也就是说对读缓冲区的写入(接受数据)和取出(处理数据)都是在一个线程内,所以读缓冲区本质上是线程安全的。 但是写操作,写缓冲区可能会有多个线程对其进行写入和更改,所以我们用一个原子变量,来标志写缓冲区是否被操作,来保证数据读写不会冲突。
本代码数据处理只是简单的进行转发到原本socket里
// // Connection.h // QuoridorServer // // Created by shiyi on 2016/12/2. // Copyright © 2016年 shiyi. All rights reserved. // #ifndef Connection_H #define Connection_H #include <stdio.h> #include <iostream> #include <atomic> #include <fcntl.h> #include <sys/socket.h> #include <sys/types.h> #include <signal.h> #include <string.h> #include <sys/epoll.h> #include <unistd.h> #include <assert.h> #include <stdlib.h> #include <arpa/inet.h> #include <errno.h> #include "Buffer.h" #include "Util.h" #include "BaseFunc.h" const size_t BUFFER_SIZE = 65535; class Connection { public: Connection() : m_writeing(true), m_epollfd(-1), m_sockfd(-1) {} ~Connection(){} //初始化连接 void init(int epollfd, int sockfd, const sockaddr_in& client_addr) { m_epollfd = epollfd; m_sockfd = sockfd; m_writeing = true; m_address = client_addr; //初始化读写缓冲区 m_inBuff.init(); m_outBuff.init(); } void HandleRead() { while(true) { char buf[BUFFER_SIZE]; int ret = recv(m_sockfd, buf, BUFFER_SIZE, 0); if(ret < 0) { //缓冲区内容已读完 if((errno == EAGAIN) || (errno == EWOULDBLOCK)) { break; } //其他错误直接断开连接 Util::outError("HandleRead"); shutdown(); } //断开连接 else if(ret == 0) { shutdown(); } else { //将读取的内容加入缓冲区 m_inBuff.PutData(buf, ret); } //printf("[%s]\n", buf); } worker(); } void HandleWrite() { //更改临界值 if(!m_writeing) return; m_writeing = false; //取出数据 char buf[BUFFER_SIZE]; int len = m_outBuff.GetData(buf, BUFFER_SIZE); int n = len; while (n > 0) { int ret = send(m_sockfd, buf+len-n, n, 0); if (ret < n) { if (ret == -1 && errno != EAGAIN) { Util::outError("write error"); } break; } n -= ret; } //n=0表示数据全部写完,删除写事件 if(n == 0) modFd(m_epollfd, m_sockfd, EPOLLIN | EPOLLET | EPOLLONESHOT); //恢复临界值 m_writeing = true; } void worker() { //解析 //取出数据 char buf[BUFFER_SIZE]; int len = m_inBuff.GetData(buf, BUFFER_SIZE); while(!m_writeing) usleep(1000); m_writeing = false; m_outBuff.PutData(buf, len); modFd(m_epollfd, m_sockfd, EPOLLIN | EPOLLOUT | EPOLLET | EPOLLONESHOT); m_writeing = true; } void shutdown() { //等待写事件完成后关闭 while(!m_writeing) usleep(1000); m_writeing = false; removeFd(m_epollfd, m_sockfd); } private: int m_epollfd; //epoll描述符 int m_sockfd; //套接字描述符 sockaddr_in m_address; //套接字地址 Buffer m_inBuff; //读缓冲 Buffer m_outBuff; //写缓冲 std::atomic_bool m_writeing; //是否正在写 }; #endif /* Connection_H */