muduo网络库学习笔记(12):TcpServer和TcpConnection类

TcpServer类的主要功能是管理accept(2)获得的TcpConnection,TcpConnection则表示的是“一次TCP连接”,一旦连接断开,这个TcpConnection对象就没啥用了。

由于TcpConnection类较复杂,本篇我们先学习TcpConnection类关于连接建立的处理。

当有新连接到来时,TcpServer新建连接的相关函数的调用时序图如下:
这里写图片描述

分析:当一个连接到来,EventLoop的事件循环loop()函数返回一个活跃的通道Channel,然后调用Channel的handleEvent()函数来处理这个事件。连接到来属于可读事件,又会回调Acceptor的handleRead()函数,在handleRead()函数中又调用accept()来接受这个新的连接,然后又回调了TcpServer的newConnection()函数。newConnection()函数会创建一个TcpConnection对象,然后这个对象调用TcpConnection的成员函数connectEstablished(),在这个函数中,回调了用户设置的连接到来回调函数connCb()。

TcpServer类

TcpServer类的接口简单易懂,如下:

代码片段1:TcpServer的接口
文件名:TcpServer.h

class TcpServer : boost::noncopyable
{
 public:
  // 构造函数,InetAddress是对网际地址sockaddr_in的封装
  TcpServer(EventLoop* loop,
            const InetAddress& listenAddr,
            const string& nameArg);
  // 析构函数
  ~TcpServer();  

  const string& hostport() const { return hostport_; }
  const string& name() const { return name_; }

  void start(); // 启动TcpServer

  // 设置连接到来或者连接关闭回调函数
  void setConnectionCallback(const ConnectionCallback& cb)
  { connectionCallback_ = cb; } 

  // 设置消息到来回调函数
  void setMessageCallback(const MessageCallback& cb)
  { messageCallback_ = cb; } 

 private:
  // 此函数会创建TcpConnection对象,下面会着重分析
  void newConnection(int sockfd, const InetAddress& peerAddr);

  // 连接列表
  // key为TcpConnection的名字,value是指向TcpConnectinon对象的指针
  typedef std::map<string, TcpConnectionPtr> ConnectionMap; 

  EventLoop* loop_;  
  const string hostport_;       // 服务端口
  const string name_;           // 服务名
  boost::scoped_ptr<Acceptor> acceptor_; // Acceptor中有对accept()的封装,获得新的连接

  // 用户提供的ConnectionCallback和MessageCallback
  // 在新建TcpConnection时会原样传给后者
  ConnectionCallback connectionCallback_; 
  MessageCallback messageCallback_;

  bool started_;                // TcpServer是否启动
  int nextConnId_;              // 下一个连接ID
  ConnectionMap connections_;   // 连接列表
};
代码片段2:TcpServer::newConnection()
文件名:TcpServer.cc

void TcpServer::newConnection(int sockfd, const InetAddress& peerAddr)
{
  loop_->assertInLoopThread();
  char buf[32];
  snprintf(buf, sizeof buf, ":%s#%d", hostport_.c_str(), nextConnId_);
  ++nextConnId_;

  // 连接名称以 服务名+服务端口+连接ID 格式命名
  string connName = name_ + buf;

  LOG_INFO << "TcpServer::newConnection [" << name_
           << "] - new connection [" << connName
           << "] from " << peerAddr.toIpPort();
  InetAddress localAddr(sockets::getLocalAddr(sockfd));

  // FIXME poll with zero timeout to double confirm the new connection
  // FIXME use make_shared if necessary,此处使用make_shared()可以节约一次new
  // 创建TcpConnection对象
  TcpConnectionPtr conn(new TcpConnection(loop_,
                                          connName,
                                          sockfd,
                                          localAddr,
                                          peerAddr));
  // 把TcpConnection对象加入ConnectionMap
  connections_[connName] = conn;

  // 设置ConnectionCallback和MessageCallback
  conn->setConnectionCallback(connectionCallback_);
  conn->setMessageCallback(messageCallback_);

  // 此函数会回调用户提供的ConnectionCallback
  conn->connectEstablished();
}

TcpConnection类

本篇暂只讨论TcpConnection类关于连接建立的处理,接口也很简单:

代码片段3:TcpConnection的接口
文件名:TcpConnection.h

class TcpConnection : boost::noncopyable,
                      public boost::enable_shared_from_this<TcpConnection>
{
 public:
  /// Constructs a TcpConnection with a connected sockfd
  /// User should not create this object.
  // 构造函数
  TcpConnection(EventLoop* loop,
                const string& name,
                int sockfd,
                const InetAddress& localAddr,
                const InetAddress& peerAddr);
  // 析构函数
  ~TcpConnection();

  EventLoop* getLoop() const { return loop_; }
  const string& name() const { return name_; }
  const InetAddress& localAddress() { return localAddr_; }
  const InetAddress& peerAddress() { return peerAddr_; }

  // 判断是否已连接
  bool connected() const { return state_ == kConnected; } 

  void setConnectionCallback(const ConnectionCallback& cb)
  { connectionCallback_ = cb; }

  void setMessageCallback(const MessageCallback& cb)
  { messageCallback_ = cb; }

  // called when TcpServer accepts a new connection
  void connectEstablished();   // should be called only once

 private:
  enum StateE { kConnecting, kConnected }; // 连接状态

  void handleRead(Timestamp receiveTime); 
  void setState(StateE s) { state_ = s; } // 设置连接状态

  EventLoop* loop_;         // 所属EventLoop
  string name_;             // 连接名
  StateE state_;            // FIXME: use atomic variable
  // we don't expose those classes to client.
  boost::scoped_ptr<Socket> socket_;
  boost::scoped_ptr<Channel> channel_;
  InetAddress localAddr_;    // 本地地址
  InetAddress peerAddr_;     // 对等方地址
  ConnectionCallback connectionCallback_;
  MessageCallback messageCallback_;
};
代码片段4:TcpConnection.cc(处理连接建立事件)
文件名:TcpConnection.cc

#include <muduo/net/TcpConnection.h>
#include <muduo/base/Logging.h>
#include <muduo/net/Channel.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/Socket.h>
#include <muduo/net/SocketsOps.h>
#include <boost/bind.hpp>
#include <errno.h>
#include <stdio.h>

using namespace muduo;
using namespace muduo::net;

// 构造函数
// TcpConnection没有发起连接的功能
TcpConnection::TcpConnection(EventLoop* loop,
                             const string& nameArg,
                             int sockfd,
                             const InetAddress& localAddr,
                             const InetAddress& peerAddr)
  : loop_(CHECK_NOTNULL(loop)),
    name_(nameArg),
    state_(kConnecting),
    socket_(new Socket(sockfd)),
    channel_(new Channel(loop, sockfd)),
    localAddr_(localAddr),
    peerAddr_(peerAddr)
{
  // 通道可读事件到来的时候,回调TcpConnection::handleRead,_1是事件发生时间
  channel_->setReadCallback(
      boost::bind(&TcpConnection::handleRead, this, _1));
  LOG_DEBUG << "TcpConnection::ctor[" <<  name_ << "] at " << this
            << " fd=" << sockfd;
  socket_->setKeepAlive(true); // 设置SO_KEEPALIVE选项
}

// 析构函数
TcpConnection::~TcpConnection()
{
  LOG_DEBUG << "TcpConnection::dtor[" <<  name_ << "] at " << this
            << " fd=" << channel_->fd();
}

void TcpConnection::connectEstablished()
{
  loop_->assertInLoopThread();

  // 断言连接状态为正在建立连接
  assert(state_ == kConnecting);

  // 设置连接状态为已连接
  setState(kConnected);
  channel_->tie(shared_from_this());
  channel_->enableReading(); // 将TcpConnection所对应的通道加入到Poller关注

  // 回调用户设置的函数
  connectionCallback_(shared_from_this());
}

void TcpConnection::handleRead(Timestamp receiveTime)
{
  loop_->assertInLoopThread();
  char buf[65536];
  // 调用read将消息读到buf中
  ssize_t n = ::read(channel_->fd(), buf, sizeof buf);
  messageCallback_(shared_from_this(), buf, n); 
}

示例

测试程序:

#include <muduo/net/TcpServer.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/InetAddress.h>
#include <stdio.h>

using namespace muduo;
using namespace muduo::net;

// 连接建立回调函数
void onConnection(const TcpConnectionPtr& conn)
{
  if (conn->connected())
  {
    printf("onConnection(): new connection [%s] from %s\n",
           conn->name().c_str(),
           conn->peerAddress().toIpPort().c_str());
  }
  else
  {
    printf("onConnection(): connection [%s] is down\n",
           conn->name().c_str());
  }
}

// 消息到来回调函数
void onMessage(const TcpConnectionPtr& conn,
               const char* data,
               ssize_t len)
{
  printf("onMessage(): received %zd bytes from connection [%s]\n",
         len, conn->name().c_str());
}

int main()
{
  printf("main(): pid = %d\n", getpid());

  InetAddress listenAddr(8888); // 构造地址INADDR_ANY,端口号为8888
  EventLoop loop; // 构造一个EventLoop对象

  TcpServer server(&loop, listenAddr, "TestServer"); // 构造一个TcpServer对象,传入EventLoop对象、地址和服务名

  // 设置回调函数
  server.setConnectionCallback(onConnection);
  server.setMessageCallback(onMessage);

  // 启动TcpServer
  server.start();

  // 启动事件循环
  loop.loop();
}

测试结果:
1.启动我们的测试服务
这里写图片描述

2.客户端使用telnet发起连接
这里写图片描述

3.TcpServer接受新连接,处理连接到来事件
这里写图片描述

4.断开客户端连接,由于还未实现对连接断开事件的处理,服务端会陷入busy loop状态
这里写图片描述

这里写图片描述

### muduo::net::TcpConnection 的用法与 SSL 连接实现分析 #### 1. **muduo::net::TcpConnection 概述** `muduo::net::TcpConnection` 是一个封装了 TCP 连接的对象,主要用于处理客户端服务端之间的通信。该提供了多种方法来管理连接状态、发送接收数据等功能[^3]。然而,原生的 `muduo` 并未内置对 SSL/TLS 支持的功能,因此如果需要使用 SSL 加密传输,则需自行扩展或集成第三方库(如 OpenSSL)。 #### 2. **TcpConnection 的核心成员变量与方法** 以下是 `TcpConnection` 的几个重要组成部分及其功能描述: - **`std::string name_`**: 表示连接的名字,通常由用户自定义或者系统生成唯一标识符。 - **`Socket socket_`**: 封装底层文件描述符及相关操作接口。 - **`Channel channel_`**: 绑定到指定事件循环中监控 I/O 活动变化的通知机制。 - **`Buffer inputBuffer_, outputBuffer_`**: 缓存来自远端的数据包以及待发送的消息队列内容存储区域。 对于常规非加密模式下工作而言,可以通过如下方式创建并启动一条新的 TCP 链路实例[^3]: ```cpp // 创建一个新的 TcpConnection 对象 TcpConnectionPtr conn(new TcpConnection(loop, sock, localAddr, peerAddr)); conn->setConnectionCallback(onConnection); // 设置连接回调函数 conn->setMessageCallback(onMessage); // 设置消息到达时触发的动作处理器 conn->setWriteCompleteCallback(onWriteComplete); // 数据写入完成后执行的任务指针 conn->connectEstablished(); // 明确告知已成功握手完毕可以正常收发信息了 ``` #### 3. **基于 OpenSSL 扩展支持 SSL 功能** 为了使得现有的基础框架能够满足更高层次的安全防护需求,我们可以引入开源工具集 OpenSSL 来达成目标效果。具体做法如下所示: ##### (1)**初始化阶段准备** 在应用程序启动初期加载必要组件,并准备好相应的配置项供后续环节调用: ```cpp SSL_CTX* InitCTX(void){ const SSL_METHOD *method; SSL_CTX *ctx; OpenSSL_add_all_algorithms(); /* Load cryptos, et.al. */ SSL_load_error_strings(); /* Bring in and register error messages */ method = TLS_server_method(); /* Create server instance */ ctx = SSL_CTX_new(method); if (ctx == NULL){ ERR_print_errors_fp(stderr); abort(); } return ctx; } ``` ##### (2)**改造原有代码路径加入新特性** 针对每一次新建成功的普通套接字对象,在正式投入使用前先将其转换成具备加解密能力的新形态实体后再交付给高层模块继续运作下去: ```cpp class SslTcpConnection : public muduo::net::TcpConnection{ public: explicit SslTcpConnection(EventLoop* loop, const std::string& nameArg, int sockfd, const InetAddress& localAddr, const InetAddress& peerAddr) : muduo::net::TcpConnection(loop,nameArg,sockfd,localAddr,peerAddr), ssl_(nullptr),sslCtx_(InitCTX()){ setupSsl(); } private: void setupSsl(){ ssl_=SSL_new(sslCtx_); BIO *bio=BIO_new_socket(fd(),BIO_NOCLOSE); SSL_set_bio(ssl_, bio,bio); if(SSL_accept(ssl_)<=0){ handleError("Error during SSL accept"); } } void send(const void* message,size_t len) override { char buf[BUFLEN]; size_t bytes=SSL_write(ssl_,message,len); if(bytes<0){ handleError("Failed writing data over SSL connection."); } } ssize_t read(void* buffer,size_t maxBytes)override{ return SSL_read(ssl_,buffer,maxBytes); } ~SslTcpConnection(){ SSL_free(ssl_); SSL_CTX_free(sslCtx_); } protected: SSL* ssl_; SSL_CTX* sslCtx_; }; ``` 以上即完成了从标准版升级至增强型版本的主要改动部分介绍[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值