C++仿muduo库高并发服务器项目:Socket模块
前言
本篇文章所讲的是本人的个人项目仿muduo库高并发服务器中的Socket模块实现部分。
Socket模块介绍:
Socket模块是对套接字操作封装的⼀个模块,主要实现的socket的各项操作。
-
功能:对socket套接字的操作进行封装
-
意义:在接下来的开发中,使我们对于套接字的各项操作更加简便,避免代码冗余
-
功能设计:
-
创建套接字
-
绑定地址信息
-
开始监听
-
向服务器发起连接
-
获取新连接
-
接收数据
-
发送数据
-
关闭套接字
-
创建一个监听连接
-
创建一个客户端连接
关于对套接字操作的函数socket,bind,listen,accept等想更深入了解的,可以看我这篇博客:
Linux下简单的TCP服务器
-
代码实现:
const int MAX_LISTEN = 1024;
class Socket
{
private:int _sockfd;
public:Socket():_sockfd(-1){}Socket(int fd):_sockfd(fd){}~Socket(){Close();}int Fd(){return _sockfd;}//创建套接字bool Create(){//int socket(int domain,int type,int protocol)//ipv4 字节流 TCP_sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);if(_sockfd < 0){ERR_LOG("CREATE SOCKET FAILED!!");return false;}return true;}//绑定地址信息bool Bind(const std::string& ip,uint16_t port){//ipv4专用地址struct sockaddr_in addr;addr.sin_family = AF_INET;//将点分十进制ip地址转换为网络字节序(大端) //ipv4addr.sin_addr.s_addr = inet_addr(ip.c_str());//记得主机转网络序列addr.sin_port = htons(port);socklen_t len = sizeof(struct sockaddr_in);//bind时转换为通用协议地址int ret = bind(_sockfd,(struct sockaddr*)&addr,len);if(ret < 0){ERR_LOG("BIND ADDRESS FAILED!!");return false;}return true;}//开始监听 bool Listen(int backlog = MAX_LISTEN){int ret = listen(_sockfd,backlog);if(ret < 0){ERR_LOG("SOCKET LISTEN FAILED!!");return false;}return true;}//向服务器发起连接bool Connect(const std::string& ip,uint16_t port){struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_addr.s_addr = inet_addr(ip.c_str());addr.sin_port = htons(port);socklen_t len = sizeof(struct sockaddr_in);int ret = connect(_sockfd,(struct sockaddr*)&addr,len);if(ret < 0){ERR_LOG("CONNECT SERVER FAILED!!");return false;}return true;}//获取新连接int Accept(){ //不关心客户端,后两个参数设为空int newfd = accept(_sockfd,nullptr,nullptr);if(newfd < 0){ERR_LOG("SOCKET ACCEPT FAILED!!");return -1;}return newfd;}//接收数据ssize_t Recv(void* buf,size_t len,int flag = 0){ssize_t ret = recv(_sockfd,buf,len,flag);//ret == 0 连接断开if(ret <= 0){//EAGAIN,没有数据,非阻塞模式才会出现这个错误//EINTR,阻塞等待时被打断if(errno == EAGAIN || errno == EINTR){return 0;//这次没有收到数据}ERR_LOG("SOCKET RECV FAILED!!");return -1;}return ret;//返回实际接收长度}ssize_t NonBlockRecv(void* buf,size_t len){return Recv(buf,len,MSG_DONTWAIT);//非阻塞模式}//发送数据ssize_t Send(const void* buf,size_t len,int flag = 0){ssize_t ret = send(_sockfd,buf,len,flag);if(ret < 0){if(errno == EAGAIN || errno == EINTR){return 0;}ERR_LOG("SOCKET SEND FAILED!!");return -1;}return ret;//实际发送长度}ssize_t NonBlockSend(const void* buf,size_t len){return Send(buf,len,MSG_DONTWAIT);}//关闭套接字void Close(){if(_sockfd != -1){close(_sockfd);_sockfd = -1;}}//创建一个服务器连接bool CreateServer(uint16_t port,const std::string& ip = "0.0.0.0",bool block_flag = false){//1.创建套接字 2.设置非阻塞 3.设置地址重用 4.绑定地址 5.开始监听if(Create() == false) return false;if(block_flag) Nonblock();//绑定前开启地址重用ReuseAddress();if(Bind(ip,port) == false) return false;if(Listen() == false) return false;return true;}//创建一个客户端连接bool CreateClient(uint16_t port,const std::string& ip){//1.创建套接字 2.连接服务器if(Create() == false) return false;if(Connect(ip,port) == false) return false;return true;}//设置套接字选项,开启地址端口重用void ReuseAddress(){// int setsockopt(int fd, int leve, int optname, void *val, int vallen)int val = 1;//开启地址端口重用setsockopt(_sockfd,SOL_SOCKET,SO_REUSEADDR,(void*)&val,sizeof(int));val = 1;//允许多个进程绑定相同ip+端口setsockopt(_sockfd,SOL_SOCKET,SO_REUSEPORT,(void*)&val,sizeof(int));}//设置套接字属性,设置为非阻塞void Nonblock(){//int fcntl(int fd, int cmd, ... /* arg */ );int flag = fcntl(_sockfd,F_GETFL,0);fcntl(_sockfd,F_SETFL,flag | O_NONBLOCK);}
};
