文章目录

  • 一、Socket类
    • 1. Socket.h
    • 2. Socket.cc
  • 二、Accept类
    • 1. Acceptor.h
    • 2. Acceptor.cc

一、Socket类

用于封装fd、bind、listen、accept等操作

1. Socket.h

#include "noncopyable.h"struct tcp_info;class InetAddress;class Socket : noncopyable{public:explicit Socket(int sockfd): sockfd_(sockfd){}~Socket();int fd() const { return sockfd_; }void bindAddress(const InetAddress& localaddr);   // 将ip port绑定到listenfdvoid listen();int accept(InetAddress* peeraddr);                // 调用accept获取和客户通信的fdvoid shutdownWrite();                             // tcp socket是全双工通信,这是关闭监听套接字sockfd_写端void setTcpNoDelay(bool on);                      // 数据不进行tcp缓冲void setReuseAddr(bool on);void setReusePort(bool on);void setKeepAlive(bool on);private:const int sockfd_;  // 这就是服务器用于监听客户端的listenfd
};

2. Socket.cc

shutdown函数

#include "Socket.h"
#include "Logger.h"
#include "InetAddress.h"#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/tcp.h>
#include <netinet/in.h>Socket::~Socket(){::close(sockfd_);
}// 将服务器本地ip port绑定到listenfd
void Socket::bindAddress(const InetAddress& localaddr){if(0 > ::bind(sockfd_, (sockaddr*)localaddr.getSockAddr(), sizeof(sockaddr_in))){LOG_FATAL("bind sockfd:%d fail \n", sockfd_);}
}void Socket::listen(){if(0 > ::listen(sockfd_, 1024)){LOG_FATAL("listen sockfd:%d fail \n", sockfd_);}
}// 调用accept获取和客户通信的fd
// 上层调用accept,传入InetAddress对象peeraddr的地址,并且把客户的addr写入输出参数peeraddr
int Socket::accept(InetAddress* peeraddr){sockaddr_in addr;socklen_t len = sizeof(addr);::memset(&addr, 0, sizeof(addr));int connfd = ::accept4(sockfd_, (sockaddr*)&addr, &len, SOCK_NONBLOCK | SOCK_CLOEXEC);if(connfd >= 0){peeraddr->setSockAddr(addr);}return connfd;
}// tcp socket是全双工通信,这是关闭监听套接字sockfd_写端
void Socket::shutdownWrite(){// 关闭监听套接字的写端if(::shutdown(sockfd_, SHUT_WR)){LOG_ERROR("Socket::shutdownWrite error");}
}            // 数据不进行tcp缓冲
void Socket::setTcpNoDelay(bool on){int optval = on ? 1 : 0;// TCP_NODELAY禁用Nagle算法,TCP_NODELAY包含在 <netinet/tcp.h>::setsockopt(sockfd_, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval));
}void Socket::setReuseAddr(bool on){int optval = on ? 1 : 0;::setsockopt(sockfd_, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
}void Socket::setReusePort(bool on){int optval = on ? 1 : 0;::setsockopt(sockfd_, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval));
}void Socket::setKeepAlive(bool on){int optval = on ? 1 : 0;::setsockopt(sockfd_, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval));
}

写accept时,犯了两个错误:

  • len没有初始化
  • 返回的connfd没有设置为非阻塞

错误的Socket::accept如下:

int Socket::accept(InetAddress* peeraddr){// 写accept时,犯的错误有:len没初始化,返回的connfd没有设置非阻塞sockaddr_in addr;socklen_t len;::memset(&addr, 0, sizeof(addr));int connfd = ::accept(sockfd_, (sockaddr*)&addr, &len);if(connfd >= 0){peeraddr->setSockAddr(addr);}return connfd;
}

int accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags);

二、Accept类

Acceptor工作在mainReactor,用于监听新用户的连接,将与客户端通信的fd打包成Channel,muduo采用轮询算法找一个subloop,将其唤醒,把打包好的Channel给subloop

1. Acceptor.h

  • 成员变量acceptSocket_:封装了listenfd,用于监听新用户的连接
  • 成员变量acceptChannel_:封装了listenfd,以及感兴趣的事件events和发生的事件revents
  • 成员变量loop_:通过事件循环监听listenfd,也就是mainLoop
  • 成员变量newConnectionCallback_:如果一个客户端连接成功,Acceptor返回一个Channel给TcpServer,TcpServer会通过轮询唤醒一个subLoop,并把新客户端的Channel给subLoop,而这些事情都是交给一个回调函数做的,即newConnectionCallback_

此处TcpServer流程待修改

#pragma once#include "noncopyable.h"
#include "Socket.h"
#include "Channel.h"
#include "EventLoop.h"#include <functional>class EventLoop;
class InetAddress;class Acceptor : noncopyable{public:using NewConnetionCallback = std::function<void(int sockfd, const InetAddress&)>;Acceptor(EventLoop* loop, const InetAddress& listenAddr, bool reuseport);~Acceptor();void setNewConnectionCallback(const NewConnetionCallback& cb){newConnetionCallback_ = std::move(cb);}bool listening(){ return listening_; }void listen();private:void handleRead();EventLoop* loop_;Socket acceptSocket_;Channel acceptChannel_;NewConnetionCallback newConnetionCallback_;bool listening_;
};
  • 成员函数handleRead:服务器的listenfd发生读事件调用,即新用户连接

2. Acceptor.cc

socket函数第二个参数type

SOCK_NONBLOCK:非阻塞形式,没有事件发生立刻返回
SOCK_CLOEXEC:fork之后子进程会继承父进程所有的文件描述符,此时子进程会关闭这些继承来的文件描述符

#include "Acceptor.h"
#include "Logger.h"
#include "InetAddress.h"#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <errno.h>static int createNonblocking(){// 创建listenfdint sockfd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP);if(sockfd <= 0){LOG_FATAL("%s:%s:%d listen socket crete err:%d \n", __FILE__, __FUNCTION__, __LINE__, errno);}return sockfd;
}Acceptor::Acceptor(EventLoop* loop, const InetAddress& listenAddr, bool reuseport): loop_(loop), acceptSocket_(createNonblocking())             // Socket的构造函数需要一个int参数,createNonblocking()返回值就是int, acceptChannel_(loop, acceptSocket_.fd())       // 第一个参数就是Channel所属的EventLoop, listening_(false)
{acceptSocket_.setReuseAddr(true);acceptSocket_.setReusePort(true);acceptSocket_.bindAddress(listenAddr);        // bind// 当我们TcpServer调用start方法时,就会启动Acceptor的listen方法// 有新用户连接时,要执行一个回调,这个方法会将和用户连接的fd打包成Channel,然后交给subloop// 下面就是注册包装了listenfd的Channel发生读事件后,需要执行的回调函数,,Acceptor只管理封装了listenfd的Channel,只需要注册读事件的回调acceptChannel_.setReadCallBack(std::bind(&Acceptor::handleRead, this));
}Acceptor::~Acceptor(){acceptChannel_.disableAll();acceptChannel_.remove();
}void Acceptor::listen(){listening_ = true;              // 开始监听acceptSocket_.listen();         // 调用底层的::listenacceptChannel_.enableReading(); // 将acceptChannel_注册到Poller,或者在Poller更新自己感兴趣的事件
}// 服务器的listenfd发生读事件调用,即新用户连接
void Acceptor::handleRead(){InetAddress peerAddr;  // 客户端地址int connfd = acceptSocket_.accept(&peerAddr);if(connfd >= 0){if(newConnetionCallback_){newConnetionCallback_(connfd, peerAddr);  // 轮询找到subloop,唤醒并分发封装connfd的Channel}else{// 如果没有设置新用户连接的回调操作,就关闭连接::close(connfd);}}else{// accept出错LOG_ERROR("%s:%s:%d accept err:%d \n", __FILE__, __FUNCTION__, __LINE__, errno);if(errno == EMFILE){// 打开的fd达到上限LOG_ERROR("%s:%s:%d the open fd exceeding the resource limit\n", __FILE__, __FUNCTION__, __LINE__);}}
}

muduo中封装fd的Socket类和分发Channel的Acceptor类相关推荐

  1. 第2章 包装外观(Wrapper Facade):用于在类中封装函数的结构型模式

    2.1 介绍 本论文描述包装外观模式.该模式的意图是通过面向对象( OO)类接口来封装低级函数和数据结构.常见的包装外观模式的例子是像 MFC. ACE和 AWT这样的类库,它们封装本地的 OS C ...

  2. python异常类封装_在Python中封装异常

    如何使应用程序与使用过的库依赖关系所引发的异常脱钩? [app] --uses--> [lib] --dependson--> [dependency] / / x- \ / `----- ...

  3. 在C++的类中封装多线程

    在C++的类中,普通成员函数不能作为pthread_create的线程函数,如果要作为pthread_create中的线程函数,必须是static 在C++的类中,普通成员函数不能作为pthread_ ...

  4. 实现电话簿管理程序, Mytel类描述单个电话号码,TelManager类负责管理电话号码。把增、删、改、查功能封装到该类中。要求电话号码能从磁盘读写。

    1.设计原理: 1)类的设计,运用Mytel类和TelManager类的组合关系管理电话号码:在类外定义电话号码的增删查改等功能函数: 2)文件流的操作,用ifstream以输入的方式打开文件加载数据 ...

  5. muduo源代码分析--Reactor模式在muduo中的使用

    一. Reactor模式简单介绍 Reactor释义"反应堆",是一种事件驱动机制.和普通函数调用的不同之处在于:应用程序不是主动的调用某个API完毕处理.而是恰恰相反.React ...

  6. muduo学习笔记-Acceptor类

    Acceptor类一般由TCPServer创建,负责处理客户端发送的connect,它拥有一个acceptSocket_和acceptChannel_成员. 1.创建Acceptor : TcpSer ...

  7. Python 中最全面的 Socket 编程指南

    (点击上方公众号,可快速关注一起学Python) 翻译:  keelii   链接: https://keelii.com/2018/09/24/socket-programming-in-pytho ...

  8. excel字段自动java类,Java 接口自动化系列--工具类之Excel测试数据解析封装

    在进行数据解析时,先来看看excel测试数据格式,这里采用接口和测试数据分离的方式,即分为两个sheet页签分别存放接口信息,用例信息 excel封装成对象步骤 1.导入easypoi的坐标 2.加载 ...

  9. c++ 私有内部类_C++类成员的访问权限以及类的封装

    "程序员大咖,一个分享编程知识的公众号.跟着站长一起学习,每天都有进步. 文章不涉及代码,不烧脑细胞,人人都可以学习. 当你决定关注「程序员大咖CodePush」,你已然超越了90%的程序员 ...

最新文章

  1. Python使用sklearn构建广义线性模型:gamma回归(Gamma regression)实战
  2. 看完书要及时消化(1)《暗时间》
  3. 2014年度工作总结
  4. jzoj3085-图的计数【组合数,数论】
  5. linux普通用户命令权限,Linux普通用户没有权限使用命令怎么办
  6. GIS二次开发之上一个视图/下一个视图
  7. LeetCode(824)——山羊拉丁文(JavaScript)
  8. torch.Tensor.scatter_(dim, index, src, reduce=None)
  9. 2T比特每秒!瞻博推出业界最快防火墙
  10. 开启MyBatis(一)
  11. h5页面自定义字体_H5页面字体设置
  12. 【路径规划】基于粒子群算法实现机器人栅格地图路径规划matlab源码
  13. Linux安装R相关包出现icudt error
  14. 本地访问阿里云服务器不需要密码,怎么操作
  15. Android5.1浏览器证书问题
  16. 【加密与解密】C#如何读取pem的KEY文件
  17. 一个比较全的vim命令
  18. 光敏电阻、测试夹、DC2.1电源接口及万用表的使用
  19. python爬取王者皮肤_Python爬取王者荣耀所有英雄以及高清大图
  20. 通过XtraBackup进行数据库表备份和表空间传输实例

热门文章

  1. php的soapclient,如何使用SoapClient类进行PHP SOAP调用
  2. ”找你妹“是如何利用积分墙盈利的。
  3. 上班不开心,想裸辞又不敢提离职!
  4. 输出信噪比公式_最大输出信噪比准则接收机.ppt
  5. 服务器内部转发和客户端重定向
  6. 开绕组电机 开绕组永磁同步电机SVPWM仿真 开绕组异步电机
  7. pid算法_pid控制原理
  8. 普歌-uniapp安卓打包apk发布软件商城
  9. audio插入背景音乐_在HTML中添加背景音乐
  10. 职场生存秘籍:如何在竞争激烈的环境中活下去