大型分布式C++框架《二:大包处理过程》

简介: 本来这一篇是打算写包头在分布式平台中的具体变换过程的。其实文章已经写好了。但是想了这个应该是不能随便发表的。毕竟如果知道了一个包的具体每个字节的意义。能伪造包来攻击系统。其次来介绍一个包的具体变换过程意义不大。

本来这一篇是打算写包头在分布式平台中的具体变换过程的。其实文章已经写好了。但是想了这个应该是不能随便发表的。毕竟如果知道了一个包的具体每个字节的意义。能伪造包来攻击系统。其次来介绍一个包的具体变换过程意义不大。在每个分布式系统的里。包的扭转应该是个有不同。我们着重的应该是一种思想。一种共性。而不是个体的具体实现。

     这里打算就介绍下大包的处理。其实这个更多的是介绍了下TCP切包。跟分布式没啥关系。。。。  不过这也算是系统的一部分

下面介绍下一个大包的具体处理过程

一、发送请求并分析

1)首先我们在客户端发送一个超过1M 的包给客户端处理,结果是服务端只收了一次recv就拒绝了

2)为了更清晰  我们用tcpdump来抓包处理

注意由于是在本机上发送包接收包。所以走的是回路。即网卡lo  而不是eth0.抓包的时候  需要抓lo  否则看不到数据
1
[root@localhost git] # tcpdump -i lo port 53101 -w ./target.cap

拿到数据以后放到wireshark里分析


a)为什么最大协商是16396 而实际收了16384个字节
可以看到协商的mss最大传输单元为16396
但是显示netio revc只收到了16384个字节。
 
因为TCP包头中会带有12字节的选项----时间戳
所以实际是16396-12 = 16384
所以neito一次收到的是16384个字节。
 
注意这里的测试环境是本机回路。
如果是在公网上。
一次TCP能承载的最大数据包 应该是 1448(1500MTU-20 IP包头 -20TCP包头 -12时间戳)
(具体且包的原理请看下面)
能revc的数据跟TCP的接收缓冲队列有关。
 
b)为什么本机就只收了一次  mss  ?
sysctl -a | grep net.ipv4.tcp_wmem
net.ipv4.tcp_wmem = 4096 16384 81920
第二个值是send默认发送缓冲区字节的个数
所以send一次 16384个字节 然后mss的大小刚好协商的是16396。  所以就被recv一次收到了。
正常在公网下。send 的16384个字节会被TCP切成11个包发到netio。
TCP会在接受端组包。但是不一定会一下都收到这11个包。可能就收到5个 然后组包给recv来处理。然后继续循环recv。还收下面的6个包.组包给recv
所以netio会根据缓冲队列已经网路情况    recv到部分字节或者全部的16384个字节。
这里如果send的数据大于 16384 个字节。那么就是循环上面的步骤 .

c)为什么服务端拿到一次recv就之间关闭请求了。

因为我们服务端允许客户端传来的请求必须小于1M.所以拿到一次recv以后。就可以解析包头。发现客户端到底需要发送多少个字节。

超过1M 我们认为就是非法包。直接拒绝并关闭客户端连接。

 

二、接下来我们来分析一个服务端能处理的大包

1、我们发送一个262360  个字节包给服务端

2、这里注意下epoll收包的写法

当recv完一个包的时候。如果正确。就会回到最开始的while循环然后继续监听 epoll。然后会触发E_TCP_CONN事件。
看如下图。收完一个recv就会跳到while。继续epoll wait 来收下一包。反复如此直到收完一个完整的包

 

注意早期的写法是这样的。直接在while循环里收recv。 这是在阻塞机制下的写法。
我们用到epoll. 可以不用阻塞在recv 可以等待事件触发读取。
可以自己比对下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
while (1)
{
     cnt = ( int )recv(m_socket, pBuf,RECVSIZE, 0);
     if ( cnt >0 )
     {
         //正常处理数据
     }
     else
    {
          if ((cnt<0) &&( errno  == EAGAIN|| errno  == EWOULDBLOCK|| errno  == EINTR))
         {
             continue ; //继续接收数据
         }
         break ; //跳出接收循环
     }
}

 

3、结果以及分析

1
2
3
4
5
6
7
8
9
10
11
12
最后收到的结果如下 收了11次。总共262360个字节
m_iRecvBufLen:16384  sizeof (m_achRecvBuf):131072 TPT_RECV_BUF_LEN:131072
m_iRecvBufLen:16384  sizeof (m_achRecvBuf):131072 TPT_RECV_BUF_LEN:131072
m_iRecvBufLen:32768  sizeof (m_achRecvBuf):131072 TPT_RECV_BUF_LEN:131072
m_iRecvBufLen:16384  sizeof (m_achRecvBuf):131072 TPT_RECV_BUF_LEN:131072
m_iRecvBufLen:16384  sizeof (m_achRecvBuf):131072 TPT_RECV_BUF_LEN:131072
m_iRecvBufLen:65536  sizeof (m_achRecvBuf):131072 TPT_RECV_BUF_LEN:131072
m_iRecvBufLen:32768  sizeof (m_achRecvBuf):131072 TPT_RECV_BUF_LEN:131072
m_iRecvBufLen:16384  sizeof (m_achRecvBuf):131072 TPT_RECV_BUF_LEN:131072
m_iRecvBufLen:16384  sizeof (m_achRecvBuf):131072 TPT_RECV_BUF_LEN:131072
m_iRecvBufLen:16384  sizeof (m_achRecvBuf):131072 TPT_RECV_BUF_LEN:131072
m_iRecvBufLen:16602  sizeof (m_achRecvBuf):131072 TPT_RECV_BUF_LEN:131072

我们看到每次发送的length都是16384 但是由于滑动窗口win。时大时小。发送的速度。不一样。导致recv一次能收到的数据也是不一样的

收到这些包。把他们组成一个完整的包。即发送端发过来的262360的包 
开始跳到int CNetioApp::OnRecv(int iTcpHandle,char* pBuffer,uint32_t nBufLen)处理
后面就是常规逻辑了。丢到container的请求消息队列。
然后container处理完丢到netio的回包队列。
最后netio拿到包返回给前端
 

这里最后总结:

1、这里对分布式平台来说。会有一个专门收包的进程。只负责收包和转发。本身没有业务处理。它收到包以后会打上时间戳或者其他一些基本信息。然后把包丢给其他业务进程处理。然后等包回来了在把处理完后的包返回给前端。如果需要请求的服务不再本机。会有另一个转发器。把请求包发送大分布式中的其他服务处理。这里先说下大概的功能。后面会单独开篇介绍我们的netio。
2、收包并不是一次全收完才可以处理。一般一次recv以后。拿到固定包头信息就可以来判断包的一些基本状态。可以分析是不是系统想要的包、允许的包
3、理解下TCP的一些基本原理。
     a)假如我们在TCP层用更大的数据量来打包会有什么结果呢?
          答案是降低了传输效率。
          分片最大的坏处就是降低了传输性能,本来一次可以搞定的事情,分成多次搞定,所以在网络层更高一层(就是传输层)的实现中往往会对此加以注意!
          这个就是在以太网上,TCP不发大包,反而发送1448小包的原因。只要这个值TCP才能对链路进行效能最高的利用。
          所以TCP在发包的时候已经切好大小刚好的包。不需要IP层再去切包
       b)简单来说  滑动窗口是用来控制发送速度的
目录
相关文章
|
5月前
|
算法 Linux C++
C++框架设计中实现可扩展性的方法
在软件开发中,可扩展性至关重要,尤其对于C++这样的静态类型语言。本文探讨了在C++框架设计中实现可扩展性的方法:1) 模块化设计降低耦合;2) 使用继承和接口实现功能扩展;3) 通过插件机制动态添加功能;4) 利用模板和泛型提升代码复用;5) 遵循设计原则和最佳实践;6) 应用配置和策略模式以改变运行时行为;7) 使用工厂和抽象工厂模式创建可扩展的对象;8) 实现依赖注入增强灵活性。这些策略有助于构建适应变化、易于维护的C++框架。
360 2
|
3月前
|
数据安全/隐私保护 C++ 开发者
C++框架设计秘籍:解锁可扩展性的神奇密码,让你的代码无所不能!
【8月更文挑战第5天】在C++框架设计中,实现可扩展性至关重要以适应需求变化和新功能的加入。主要策略包括:使用接口与抽象类提供统一访问并允许多种实现;采用依赖注入分离对象创建与依赖管理;运用模板和泛型编程实现代码通用性;设计插件机制允许第三方扩展;以及利用配置文件和动态加载支持灵活的功能启用与模块加载。遵循这些实践能构建出更灵活、可维护及可扩展的框架。
42 1
|
5月前
|
jenkins 测试技术 持续交付
利用C++增强框架的可测试性(Testability)
**C++框架可测试性提升策略**:通过模块化设计、依赖注入、使用Mock对象和Stub、编写清晰接口及文档、断言与异常处理、分离测试代码与生产代码、自动化测试,可以有效增强C++框架的可测试性。这些方法有助于确保代码正确性、健壮性,提高可维护性和可扩展性。示例包括使用类和接口实现模块化,通过构造函数进行依赖注入,以及利用Google Test和Google Mock进行断言和模拟测试。
75 1
|
5月前
|
存储 算法 安全
用C++打造极致高效的框架:技术探索与实践
本文探讨了如何使用C++构建高性能框架。C++凭借其高性能、灵活性和跨平台性成为框架开发的理想选择。关键技术和实践包括:内存管理优化(如智能指针和自定义内存池)、并发编程(利用C++的并发工具)、模板与泛型编程以提高代码复用性,以及性能分析和优化。在实践中,应注意代码简洁性、遵循最佳实践、错误处理和充分测试。随着技术发展,不断提升对框架性能的要求,持续学习是提升C++框架开发能力的关键。
103 1
|
6月前
|
分布式计算 网络协议 大数据
基于C++的分布式计算框架设计与实现
基于C++的分布式计算框架设计与实现
356 2
|
6月前
|
JSON Linux C语言
全网最权威唯一值得推荐的《C/C++框架和库》
关于C++框架、库和资源的一些汇总列表,内容包括:标准库、Web应用框架、人工智能、数据库、图片处理、机器学习、日志、代码分析等。
75 1
|
6月前
|
机器学习/深度学习 JSON 编译器
C++ 资源大全:标准库、Web框架、人工智能等 | 最全整理
C++ 资源列表,内容包括: 标准库、Web应用框架、人工智能、数据库、图片处理、机器学习、日志、代码分析等
119 1
|
6月前
|
开发框架 Linux C++
Qt:强大的跨平台C++应用程序开发框架
Qt:强大的跨平台C++应用程序开发框架
199 3
|
6月前
|
开发框架 编译器 C++
Qt:一个强大的跨平台C++应用程序开发框架
Qt:一个强大的跨平台C++应用程序开发框架
171 1
|
6月前
|
设计模式 编解码 程序员
探索 C++ 20 (co_await、co_yield 和 co_return)协程基本框架的使用
探索 C++ 20 (co_await、co_yield 和 co_return)协程基本框架的使用
528 1
探索 C++ 20 (co_await、co_yield 和 co_return)协程基本框架的使用