muduo实现的客户端与服务端连接

本文详细介绍了如何利用muduo库实现客户端与服务端的连接。从服务端启动、监听、连接建立、消息接收和发送,到客户端的连接操作,深入解析了每个步骤,包括回调函数的调用和事件处理流程。
摘要由CSDN通过智能技术生成

下面就是利用muduo进行客户端与服务器端的实例:

网络编程关注三个半事件:连接建立、连接断开、消息到达这算三个,还有半个是消息发送完毕(之所以是半个,因为对于低流量的服务,通常不需要关注该事件)

所以,我们利用muduo编写自己的服务器时,需要有一个XXXServer类,在该类中包含一个TcpServer对象和一个EventLoop对象。这个类需要提供3个成员函数OnConnection、OnMessage、OnWriteComplete作为回调函数,连接建立和连接断开都是回调OnConnection函数,消息到达回调OnMessage函数,消息发送完毕调用OnWriteComplete函数

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

#include <boost/bind.hpp>

#include <stdio.h>

using namespace muduo;
using namespace muduo::net;

class TestServer
{
 public:
  TestServer(EventLoop* loop,
             const InetAddress& listenAddr)
    : loop_(loop),
      server_(loop, listenAddr, "TestServer")
  {
	//连接到来和连接断开时的回调函数
    server_.setConnectionCallback(
        boost::bind(&TestServer::onConnection, this, _1));
	//消息到来时的回调函数
    server_.setMessageCallback(
        boost::bind(&TestServer::onMessage, this, _1, _2, _3));
  }

  void start()
  {
	  server_.start();
  }

 private:
  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,
                 Buffer* buf,
                 Timestamp receiveTime)
  {
	//获取应用层缓冲区的数据
    string msg(buf->retrieveAllAsString());
    printf("onMessage(): received %zd bytes from connection [%s] at %s\n",
           msg.size(),
           conn->name().c_str(),
           receiveTime.toFormattedString().c_str());
    //将数据回射回去
	conn->send(msg);
  }

  EventLoop* loop_;
  TcpServer server_;
};


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

  InetAddress listenAddr(8888);
  EventLoop loop;

  TestServer server(&loop, listenAddr);
  //服务端启动
  server.start();
  //开始事件循环
  loop.loop();
}

分析:

1.server.starrt()也就是server_.start(),此时服务端启动

//该函数可以跨线程调用
//启动线程池
void TcpServer::start()
{
  if (started_.getAndSet(1) == 0)
  {
    threadPool_->start(threadInitCallback_);

    //判断是否处于监听状态
    assert(!acceptor_->listenning());
    loop_->runInLoop(
		//进入&Acceptor::listen
        boost::bind(&Acceptor::listen, get_pointer(acceptor_)));
  }
}

2.执行Acceptor::listen(),此时服务器处于监听状态,并且注册被动连接通道,关注其可读事件

//监听
void Acceptor::listen()
{
  loop_->assertInLoopThread();
  listenning_ = true;
  //监听
  acceptSocket_.listen();
  //关注可读事件
  acceptChannel_.enableReading();
}

3.注册被动连接通道,关注其可读事件

//关注可读事件,把通道注册到EventLoop,从而注册到EventLoop所持有的poll对象中
  void enableReading() { events_ |= kReadEvent; update(); }
void Channel::update()
{
  addedToLoop_ = true;
  //调用loop的updateChannel
  loop_->updateChannel(this);
}

进而调用loop的updateChannel()

void EventLoop::updateChannel(Channel* channel)
{
  assert(channel->ownerLoop() == this);
  assertInLoopThread();
  poller_->updateChannel(channel);
}

进而调用poller(有可能是PollPoller或者EPollPoller)的updateChannel()

//注册或者更新通道
//注册某个文件描述符的一些可读可写事件,更新就是原来关注可读可写,现在只关注可读
void PollPoller::updateChannel(Channel* channel)
{
  //断言是在Loop线程中调用Poller
  Poller::assertInLoopThread();
  LOG_TRACE << "fd = " << channel->fd() << " events = " << channel->events();
  //如果index_<0,位置还不知道,说明是一个新的通道
  if (channel->i
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值