1.5.C++项目:仿muduo库实现并发服务器之socket模块的设计

项目完整版在:

一、socket模块:套接字模块

二、提供的功能

Socket模块是对套接字操作封装的一个模块,主要实现的socket的各项操作。

socket 模块:套接字的功能
创建套接字
绑定地址信息
开始监听
向服务器发起连接
获取新连接
接受数据
发送数据
关闭套接字
创建一个监听链接
创建一个客户端连接
设置套接字选项------开启地址端口重用!
设置套接字阻塞属性------设置为非阻塞!

三、实现思想

(一)功能

对socket套接字的操作进行封装。

(二)意义

对socket套接字的操作进行封装。

(三)功能设计

  1. 创建套接字
  2. 绑定地址信息
  3. 开始监听
  4. 向服务器发起连接
  5. 获取新连接
  6. 接受数据
  7. 发送数据
  8. 关闭套接字
  9. 创建一个监听链接
  10. 创建一个客户端连接

四、代码

cpp 复制代码
#define MAX_LISTEN 1024
class Socket {
        private:
                int _sockfd;
        public:
                Socket() :_sockfd(-1) {}
                Socket(int fd) : _sockfd(fd) {} 
                ~Socket() {Close(); }
                int fd() {return _sockfd;}
                // 1.创建套接字
                bool Create() {
                
                        //int socket (int domain,int type,int protocol);
                        _sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
                        if (_sockfd < 0) {
                                ERR_LOG("CREATE SOCKET FAILED !!");
                                return false;
                        }
                        return true;
                } 
                // 2.绑定地址信息
                bool Bind(const std::string &ip,uint16_t port) {
	       
                        struct sockaddr_in addr;
                        addr.sin_family = AF_INET;
                        addr.sin_port = htons(port);
                        addr.sin_addr.s_addr = inet_addr(ip.c_str());
                        socklen_t len = sizeof(struct sockaddr_in);
                        // int bind(int sockfd,struct sockaddr * addr,socklen_t len);
                        int ret = bind(_sockfd,(struct sockaddr*)&addr,len);
                        if (ret < 0) {
                                ERR_LOG("BIND ADDRESS FAILED!!!!");
                                return false;
                        }
                        return true;
                }
                 // 3.开始监听
                bool Listen(int backlog = MAX_LISTEN) {
                        // int listen(int backlog)
                        int ret = listen(_sockfd,backlog);
                        if (ret < 0) {
                                ERR_LOG("SOCKET LISTEN FAILED!!");
                                return false;
                        }
                        return true;
                }
                 // 4. 向服务器发起连接
                bool Connect(const std:: string& ip,uint16_t port) {
                
                        struct sockaddr_in addr;
                        addr.sin_family = AF_INET;
                        addr.sin_port = htons(port);
                        addr.sin_addr.s_addr = inet_addr(ip.c_str());
                        socklen_t len = sizeof(struct sockaddr_in);
                        // int bind(int sockfd,struct sockaddr * addr,socklen_t len);
                        int ret = connect(_sockfd,(struct sockaddr*)&addr,len);
                        if (ret < 0) {
                                ERR_LOG("CONNECT ADDRESS FAILED!!!!");
                                return false;
                        }
                        return true;
                } 
                // 5. 获取新连接
                int Accept() {
                
                        // int accept(int sockfd,struct sockaddr* addr,socklen_t *len) /
                        int newfd = accept(_sockfd,NULL,NULL);
                        if (newfd < 0) {
                                ERR_LOG("SOCKET ACCEPT FAILED!!!!");
                                return false;
                        }
                        return newfd;
                }
                ssize_t Recv(void *buf,size_t len,int flag = 0){
                        // 6.接收数据
                        //有符号长整型 
                        //ssize_t Recv(int sockfd,void* buf,size_t len,int flag);
                        ssize_t ret = recv(_sockfd,buf,len,flag);
                        if (ret <= 0) {
                                // EAGAIN 当前socket的接收缓冲区没有数据来,在非阻塞二点情况下才会有这个错误
                                // ENTER 当前socket的阻塞等待,被信号打断了
                                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); // MSG_DONTWAIT 表示当前接受为非阻塞
                }
	        // 7.发送数据
                ssize_t Send(const void* buf,size_t len,int flag = 0) {
                        // ssize_t send(int sockfd,void *data,size_t len,int flag) 
                        ssize_t ret = send(_sockfd,buf,len,flag);
                        if (ret < 0) {
                                ERR_LOG("SOCKET SEND FAILED!!");
                                return -1; // 出错
                        }
                        return ret; // 实际发送数据长度!!
                }
                ssize_t nonBlockSend(void* buf,size_t len) {
                        return Send(buf,len,MSG_DONTWAIT); // MSG_DONTWAIT 表示当前接受为非阻塞
                }
	        // 8.关闭套接字
                void Close() {
                        if (_sockfd != -1) {
                                close(_sockfd);
                                _sockfd = -1;
                        }
                
                }
	        // 9.创建一个服务端链接
                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 (Bind(ip,port) == false) return false;
                        if (Listen() == false) return false;
                       if (block_flag) NonBlock();
                        ReuseAddress();
                        return true;
                }
                // 10.创建一个客户端链接 
                bool createClient(uint16_t port, const std::string &ip) {
                        if (Create() == false) return false;
                        if (Connect(ip,port) == false) return false;
                        return true;
                }

                // 11. 设置套接字选项------开启地址端口重用!
                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;
                        setsockopt(_sockfd, SOL_SOCKET, SO_REUSEPORT, (void*)&val, sizeof(int));
                }
                // 12. 设置套接字阻塞属性------设置为非阻塞! 
                void NonBlock() {
                        int flag = fcntl(_sockfd, F_GETFL, 0);
                        fcntl(_sockfd, F_SETFL, flag | O_NONBLOCK);
                }

};

五、测试

(一)tcp_cli.cc

cpp 复制代码
#include "../source/server.hpp"

int main() {
    Socket cli_sock;
    cli_sock.createClient(8500,"127.0.0.1");
    std::string str = "nihao";
    cli_sock.Send(str.c_str(),str.size());
    char buf[1024] = {0};
    cli_sock.Recv(buf,1023);
    DBG_LOG("%s",buf);
    return 0;
}

(二)tcp_srv.cc

cpp 复制代码
#include "../source/server.hpp"

int main() {
    Socket lst_sock;
    bool ret = lst_sock.createServer(8500);
    while (1) {
        int newfd = lst_sock.Accept();
        if (newfd < 0) {
            continue;
        }
        Socket cli_sock(newfd);
        char buf[1024] = {0};
        int ret = cli_sock.Recv(buf,1023);
            if(ret < 0) {
            cli_sock.Close();
        }
        cli_sock.Send(buf,ret);
        cli_sock.Close();
    }
    lst_sock.Close();
    return 0;
}

(三)makefile

cpp 复制代码
all:client server
client:tcp_cli.cc
	g++ -std=c++11 $^ -o $@
server:tcp_srv.cc
	g++ -std=c++11 $^ -o $@
相关推荐
StrokeAce1 小时前
linux桌面软件(wps)内嵌到主窗口后的关闭问题
linux·c++·qt·wps·窗口内嵌
一颗花生米。2 小时前
深入理解JavaScript 的原型继承
java·开发语言·javascript·原型模式
问道飞鱼2 小时前
Java基础-单例模式的实现
java·开发语言·单例模式
学习使我快乐012 小时前
JS进阶 3——深入面向对象、原型
开发语言·前端·javascript
通信仿真实验室3 小时前
(10)MATLAB莱斯(Rician)衰落信道仿真1
开发语言·matlab
勿语&3 小时前
Element-UI Plus 暗黑主题切换及自定义主题色
开发语言·javascript·ui
家有狸花4 小时前
VSCODE驯服日记(三):配置C++环境
c++·ide·vscode
dengqingrui1235 小时前
【树形DP】AT_dp_p Independent Set 题解
c++·学习·算法·深度优先·图论·dp
C++忠实粉丝5 小时前
前缀和(8)_矩阵区域和
数据结构·c++·线性代数·算法·矩阵
热爱嵌入式的小许5 小时前
Linux基础项目开发1:量产工具——显示系统
linux·运维·服务器·韦东山量产工具