muduo网络库之net库源码分析(1)

<TCP网络编程本质>

TCP网络编程最本质是的处理三个半事件:
连接建立:服务器accept(被动)接受连接,客户端connect(主动)发起连接
连接断开:主动断开(close、shutdown),被动断开(read返回0)
消息到达:文件描述符可读
消息发送完毕:这算半个。对于低流量的服务,可不必关心这个事件;这里的发送完毕是指数据写入操作系统缓冲区(如果数据全部填到了内核缓冲区那么调用回掉函数OnWriteComplete函数),将由TCP协议栈负责数据的发送与重传,不代表对方已经接收到数据。对于高流量的服务就要关心这个事件了,因为内核缓冲区可能不足以容纳数据,此时我们要将数据追加到应用层的发送缓冲区,如果内核缓冲区空闲了,那么就把应用层缓冲区中的数据发送出去,发送完了之后,也会调用OnWriteComplete函数。


下图演示了消息到达后和消息发送的数据处理流程:




下面我们看一下TcpSerer如何设置上面三个半事件的回调函数的:



EchoServer类是用户自己实现的类,由于muduo使用的是基于对象编程思想,所以从上面可以看出EchoServer包含TcpServer实例,而EcpServer这个类中又包含了上面set*开头的几个函数,它门分别用于注册链接建立和断开、消息到达、数据发送完毕这三个回调函数,其中setConnectionCallback同时是链接建立和断开的回调函数。所以我们如何使用EchoServer呢?很明显我们可以先定义EchoServer类型,这个类型包含我们自己实现的事件回调函数,并且包含TcpServer实例,然后调用TcpServer实例中的函数注册回调函数。

TcpServer使用实例如下:
    EchoServer.h源码如下:
class EchoServer
{
 public:
  EchoServer(muduo::net::EventLoop* loop,
             const muduo::net::InetAddress& listenAddr);

  void start();  // calls server_.start();

 private:
  void onConnection(const muduo::net::TcpConnectionPtr& conn);//链接建立或断开回调函数

  void onMessage(const muduo::net::TcpConnectionPtr& conn,   //收到消息后回调函数
                 muduo::net::Buffer* buf,
                 muduo::Timestamp time);

  muduo::net::EventLoop* loop_;  
  muduo::net::TcpServer server_; //包含注册三个半事件回调函数的方法
};

EchoServer.cpp源码如下:
#include "echo.h"

#include <muduo/base/Logging.h>

#include <boost/bind.hpp>

// using namespace muduo;
// using namespace muduo::net;

EchoServer::EchoServer(muduo::net::EventLoop* loop,
                       const muduo::net::InetAddress& listenAddr)
  : loop_(loop),
    server_(loop, listenAddr, "EchoServer")
{//注册回调函数
  server_.setConnectionCallback(
      boost::bind(&EchoServer::onConnection, this, _1));
  server_.setMessageCallback(
      boost::bind(&EchoServer::onMessage, this, _1, _2, _3));
}

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

void EchoServer::onConnection(const muduo::net::TcpConnectionPtr& conn)
{
  LOG_INFO << "EchoServer - " << conn->peerAddress().toIpPort() << " -> "
           << conn->localAddress().toIpPort() << " is "
           << (conn->connected() ? "UP" : "DOWN"); //<span style="font-family: Arial, Helvetica, sans-serif;">conn->connected()判断链接断开还是建立链接 </span>
}

void EchoServer::onMessage(const muduo::net::TcpConnectionPtr& conn, //只是简单回射
                           muduo::net::Buffer* buf, 
                           muduo::Timestamp time)
{
  muduo::string msg(buf->retrieveAllAsString());
  LOG_INFO << conn->name() << " echo " << msg.size() << " bytes, "
           << "data received at " << time.toString();
  conn->send(msg);
}

main.cpp源码如下:
#include "echo.h"

#include <muduo/base/Logging.h>
#include <muduo/net/EventLoop.h>

// using namespace muduo;
// using namespace muduo::net;

int main()
{
  LOG_INFO << "pid = " << getpid();
  muduo::net::EventLoop loop;
  muduo::net::InetAddress listenAddr(2007);
  EchoServer server(&loop, listenAddr);
  server.start();
  loop.loop(); //监听事件
}


<这里首先分析什么也不做的EventLoop类,后面再深入分析>

one loop per thread意思是说每个线程最多只能有一个EventLoop对象。
EventLoop对象构造的时候,会检查当前线程是否已经创建了其他EventLoop对象,如果已创建,终止程序(LOG_FATAL)
EventLoop构造函数会记住本对象所属线程(threadId_)。
创建了EventLoop对象的线程称为IO线程,其功能是运行事件循环(EventLoop::loop)

//EventLoop.cpp的代码本身很复杂,这里是把它精简后的源码
#include <muduo/net/EventLoop.h>
#include <muduo/base/Logging.h>
#include <poll.h>

using namespace muduo;
using namespace muduo::net;

namespace
{
// 当前线程EventLoop对象指针
// __thread线程局部存储,也就是每个线程都有一个这个变量
__thread EventLoop* t_loopInThisThread = 0;
}

EventLoop* EventLoop::getEventLoopOfCurrentThread()
{
  return t_loopInThisThread;
}

EventLoop::EventLoop()
  : looping_(false),  //是否处于循环状态
    threadId_(CurrentThread::tid()) //把线程id设置为EventLoop对象所属的线程
{
  LOG_TRACE << "EventLoop created " << this << " in thread " << threadId_;
  // 如果当前线程已经创建了EventLoop对象,终止(LOG_FATAL)
  if (t_loopInThisThread)
  {
    LOG_FATAL << "Another EventLoop " << t_loopInThisThread
              << " exists in this thread " << threadId_;
  }
  else
  {
    t_loopInThisThread = this;  //设置当前线程EventLoop对象指针
  }
}

EventLoop::~EventLoop()
{
  t_loopInThisThread = NULL;
}

// 事件循环,该函数不能跨线程调用
// 只能在创建该对象的线程中调用
void EventLoop::loop()
{
  assert(!looping_);
  // 断言当前处于创建该对象的线程中
  assertInLoopThread();
  looping_ = true;
  LOG_TRACE << "EventLoop " << this << " start looping";

  ::poll(NULL, 0, 5*1000);  //什么都没做只是简单地休息5秒钟

  LOG_TRACE << "EventLoop " << this << " stop looping";
  looping_ = false;
}

void EventLoop::abortNotInLoopThread()
{
  LOG_FATAL << "EventLoop::abortNotInLoopThread - EventLoop " << this
            << " was created in threadId_ = " << threadId_
            << ", current thread id = " <<  CurrentThread::tid();
}

//EventLoop.h精简后的源码如下
#ifndef MUDUO_NET_EVENTLOOP_H
#define MUDUO_NET_EVENTLOOP_H

#include <boost/noncopyable.hpp>

#include <muduo/base/CurrentThread.h>
#include <muduo/base/Thread.h>

namespace muduo
{
namespace net
{

///
/// Reactor, at most one per thread.
///
/// This is an interface class, so don't expose too much details.
class EventLoop : boost::noncopyable
{
 public:
  EventLoop();
  ~EventLoop();  // force out-line dtor, for scoped_ptr members.

  ///
  /// Loops forever.
  ///
  /// Must be called in the same thread as creation of the object.
  ///
  void loop();
  
  //断言EventLoop是否处于创建它的线程当中
  void assertInLoopThread()
  {
    if (!isInLoopThread())
    {
      abortNotInLoopThread();
    }
  }
  
  //如果当前线程id和创建EventLoop的线程id一致那么返回true
  bool isInLoopThread() const { return threadId_ == CurrentThread::tid(); }  

  static EventLoop* getEventLoopOfCurrentThread();

 private:
  void abortNotInLoopThread();
  
  bool looping_; /* atomic */    //标志是否处于EventLoop状态
  const pid_t threadId_;		// 当前对象所属线程ID
};

}
}
#endif  // MUDUO_NET_EVENTLOOP_H


正确使用的实例:

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

using namespace muduo;
using namespace muduo::net;

void threadFunc()
{
	printf("threadFunc(): pid = %d, tid = %d\n",
		getpid(), CurrentThread::tid());

	EventLoop loop;
	loop.loop();
}

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

	EventLoop loop;

	Thread t(threadFunc);
	t.start();

	loop.loop();//只是等待5秒
	t.join();
	return 0;
}

错误的使用实例:

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

using namespace muduo;
using namespace muduo::net;

EventLoop* g_loop;

void threadFunc()
{
	g_loop->loop();  //错误!!!在不是创建该EventLoop实例的线程中调用了该实例的loop函数,loop函数不能跨线程调用
}

int main(void)
{
	EventLoop loop;
	g_loop = &loop;
	Thread t(threadFunc);
	t.start();
	t.join();
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值