Muduo网络库的实现单线程网络服务器完善(九)

源码下载以及安装点击链接https://blog.csdn.net/YoungSusie/article/details/90021742

分类 Muduo网络库编程 学习笔记

1、Buffer 读取数据

Buffer 是非阻塞TCP网络编程必不可少的,Buffer 具有值语义。

值语义:复制后与以前的对象无关的对象;
引用语义:无法复制或复制之后与原来的对象存在关联,不可用在STL 容器中。

Buffer 的设计要点:

  • 对外表现为连续的内存,以方便客户写代码;
  • 其size() 可以自动增长,以适应不同大小的消息;
  • 内部以std::vector 来保存数据,并提供相应的访问函数,(从尾部写入数据,从头部读取数据);
  • Input buffer ,TCP connection class 从套接字读取数据,然后写入 input buffer (调用函数readFd() 来实现),
  • output buffer,客户代码将数据写入 output buffer(TcpConnection::send() 函数实现的),TcpConnection 从 outputbuffer 读取数据写入套接字。

在这里插入图片描述

2、Buffer class介绍

  • Buffer::readFd()
    在非阻塞网络编程中,如何设计并使用缓冲区?
    一方面希望减少系统调用,一次读取的数据越多越划算;另一方面希望减少内存的占用。这两方面似乎是矛盾的,假设C10K ,每个连接一建立就分配50KB 的内存的话,那么将占用1GB 内存,但是大多数的连接并不需要这么多内存。muduo 巧妙的使用了readv() 结合栈上空间巧妙的解决了这个问题。

readFd() 是最内层的函数,其在IO 线程的最大 stack 空间开销是固定的64KB , 与连接的数目无关;如果stack 的空间很紧张,也可以改为 thread local 的 extrabuf ,但是不能全局共享一个extrabuf。

ssize_t Buffer::readFd(int fd, int * savedErrno)
{
  	  char extrabuf[65536];
	  struct iovec vec[2];
	  const size_t writable = writableBytes();
	  vec[0].iov_base = begin()+writeIndex_;
	  vec[0].iov_len = writeable;
	  vec[1].iov_base = extrabuf;
	  vec[1].iov_len = sizeof extrabuf;
	  const ssize_t n = readv(fd, vec, 2);
	  if (n < 0) {
		    *savedErrno = errno;
	  } else if (implicit_cast<size_t>(n) <= writable) {
		    writeIndex_ += n;
	  } else {
		    writeIndex_ = buffer_.size();
		    append(extrabuf, n - writeable);
	  }
	  return n;
}

从代码中可以看出,具体的做法是:在栈上准备一个65535 字节的extrabuf , 然后利用readv() 来读取数据,iovec有两块,第一块是指向muduo Buffer (为每个连接准备1024字节的buf)中的writeable 字节,另一块是指向extrabuf。这样如果读入的数据不多,直接读到内置的buf;如果长度超过内置buf 的大小,就会读到栈上的extrabuf 中,然后程序再把extrabuf 里的数据append() 到 buf 中。

  • Buffer 的数据结构
    其内部是一个vector , 连续的内存,Buffer class有两个数据成员,指向该vector 中的元素,readIndex 和 writeIndex ,这两个index 的类型为 size_t ,不是 char * , 目的是为了应对迭代器失效(整数下标不是指针下标),

3、TcpConnection 发送数据

发送数据比接收数据难,因为发送发送数据是主动的,接收读取数据是内动的。目前为止我们只用到了Channel 的 ReadCallback :

  • TimerQueue 用它(channel )来读取timerfd();
  • EventLoop 用它来读取eventfd();
  • TcpServer/Acceptor 用它来读listening socket;
  • TcpConnection 用它来读普通TCP socket。

本节会关注WriteCallback ,由于muduo 采用的是 level trigger ,因此我们只在需要的时候才关注writable事件,否则会造成busy loop。

TcpConnection 的状态图如图所示,其中 shutdown() 是线程安全的,它会将工作放到shutdownInLoop() 。
在这里插入图片描述
sendInLoop() 会先尝试直接发送数据,如果一次发送完毕了就不会启用WriteCallback事件;如果只发送了部分数据,以后在handleWrite() 中发送剩余的数据。

在电平触发IO多路复用中,数据的发送比较麻烦,因为不能一直关注writeable 事件;数据的接收比较简单。理想的是对readable 采用电平触发; writable 事件采用边沿触发。

4、 完善 TcpConnection class

这里网络编程中需要考虑的问题参考连接 【】

(1)SIGPIPE

设置全局对象,忽略此信号。

class IgnoreSigpipe
{
  public:
  	IgnoreSigpipe()
  	{
  		::signal(SIGPIPE, SIG_IGN);
  	}
};

IgnoreSigpipe initObj;
(2)TCP NO DELAY 和 TCP KEEPALIVE

这两个选项都是常见的TCP选项,前者的作用是禁止Nagle 算法,避免连续发包出现延迟,这对编写低延迟网络服务很重要;后者的作用是定期探查TCP连接是否还存在,应用层心跳检查。

void socket::setTcpNoDelay(bool on)
{
	int optval = on ? 1:0;
	::setsocketopt(sockfd_, IPPROTO,TCP_NODELAY,&optval,sizeof (optval));
}

分类 Muduo网络库编程 学习笔记

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值