当前位置:网站首页 > C++编程 > 正文

tcp坐标系(tcp坐标系原点的概念)



1. 什么是粘包

在基于TCP协议的端到端通讯中,如果一端连续发送两个或者两个以上的数据包,对端在一次接收时,收到的数据包数量可能大于1个,也可能是几个完整数据包加上一个完整包的一部分数据,这些统称为粘包。

2.TCP粘包示例

本文将设计一个示例演示数据粘包的表现,具体的思路是这样的:

1)使用鸿蒙应用发起TCP客户端到服务端连接。

2)服务端为回声服务器,会把收到的信息原样发回给客户端

3)TCP客户端连续发送从0到100的数字字符信息到服务端,每次发送一个数字,发送后休眠1毫秒。

4)客户端对于接受到的服务端信息在日志输出,每次一行(也就是在接受信息后面加上回车换行)

5)如果没有所谓的“粘包”问题,客户端会收到100次回复

下面详细介绍创建该应用的步骤。

步骤1:创建Empty Ability项目。

步骤2:在module.json5配置文件加上对权限的声明:

"requestPermissions": [

  {

    "name": "ohos.permission.INTERNET"

  },

  {

    "name": "ohos.permission.GET_WIFI_INFO"

  }

 ]

这里分别添加了访问互联网和访问WIFI信息的权限。

步骤3:在Index.ets文件里添加如下的代码:

import socket from '@ohos.net.socket';

import wifiManager from '@ohos.wifiManager';

import systemDateTime from '@ohos.systemDateTime';

import util from '@ohos.util';

//说明:本地的IP地址不是必须知道的,绑定时绑定到IP:0.0.0.0即可,显示本地IP地址的目的是方便对方发送信息过来

//本地IP的数值形式

let ipNum = wifiManager.getIpInfo().ipAddress

//本地IP的字符串形式

let localIp = (ipNum >>> 24) + '.' + (ipNum >> 16 & 0xFF) + '.' + (ipNum >> 8 & 0xFF) + '.' + (ipNum & 0xFF);

@Entry

@Component

struct Index {

 //连接、通讯历史记录

 @State msgHistory: string = ''

 //服务端IP地址

 @State serverIp: string = "0.0.0.0"

 //服务端端口

 @State serverPort: number = 9990

 scroller: Scroller = new Scroller()

 build() {

  Row() {

   Column() {

    Text("TCP通讯粘包示例")

    .fontSize(14)

    .fontWeight(FontWeight.Bold)

    .width('100%')

    .textAlign(TextAlign.Center)

    .padding(10)

    Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {

     Text("本地IP:")

     .width(100)

     .fontSize(14)

     .flexGrow(0)

     Text(localIp)

     .width(110)

     .fontSize(12)

     .flexGrow(1)

     Button("测试")

     .onClick(() => {

       this.sendTestMsg2Server()

     })

     .width(80)

     .fontSize(14)

     .flexGrow(0)

   }.width('100%')

   .padding(10)

    Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {

     Text("服务端地址:")

     .fontSize(14)

     .width(90)

     .flexGrow(0)

     TextInput({ text: this.serverIp })

     .onChange((value) => {

       this.serverIp = value

     })

     .width(110)

     .fontSize(12)

     .flexGrow(4)

     Text(":")

     .width(5)

     .flexGrow(0)

     TextInput({ text: this.serverPort.toString() })

     .type(InputType.Number)

     .onChange((value) => {

       this.serverPort = parseInt(value)

     })

     .fontSize(12)

     .flexGrow(0)

     .width(70)

   }

   .width('100%')

   .padding(10)

    Scroll(this.scroller) {

     Text(this.msgHistory)

     .textAlign(TextAlign.Start)

     .padding(10)

     .width('100%')

     .backgroundColor(0xeeeeee)

   }

   .align(Alignment.Top)

   .backgroundColor(0xeeeeee)

   .height(300)

   .flexGrow(1)

   .scrollable(ScrollDirection.Vertical)

   .scrollBar(BarState.On)

   .scrollBarWidth(20)

  }

  .width('100%')

  .justifyContent(FlexAlign.Start)

  .height('100%')

 }

 .height('100%')

}

 //发送测试消息到服务端

 async sendTestMsg2Server() {

  //执行TCP通讯的对象

  let tcpSocket = socket.constructTCPSocketInstance();

  await this.bind2Port(tcpSocket)

  await this.connect2Server(tcpSocket)

  for (let i = 0;i < 100; i++) {

   await tcpSocket.send({ data: i.toString() })

   await sleep(1)

 }

}

 //绑定本地端口

 async bind2Port(tcpSocket: socket.TCPSocket) {

  //本地地址

  let localAddress = { address: "0.0.0.0", family: 1 }

  await tcpSocket.bind(localAddress)

  .then(() => {

    this.msgHistory = 'bind success' + " ";

  })

  .catch((e) => {

    this.msgHistory = 'bind fail ' + e.message + " ";

  })

  //收到消息时的处理

  tcpSocket.on("message", async (value) => {

   let msg = buf2String(value.message)

   this.msgHistory += "S:" + msg + " "

   this.scroller.scrollEdge(Edge.Bottom)

 })

}

 //连接服务端

 async connect2Server(tcpSocket: socket.TCPSocket) {

  //本地地址

  let serverAddress = { address: this.serverIp, port: this.serverPort, family: 1 }

  await tcpSocket.connect({ address: serverAddress })

  .then(() => {

    this.msgHistory = 'connect success ' + " ";

  })

  .catch((e) => {

    this.msgHistory = 'connect fail ' + e.message + " ";

  })

}

}

//ArrayBuffer转utf8字符串

function buf2String(buf: ArrayBuffer) {

 let msgArray = new Uint8Array(buf);

 let textDecoder = util.TextDecoder.create("utf-8");

 return textDecoder.decodeWithStream(msgArray)

}

//休眠指定的毫秒数

function sleep(time) {

 return new Promise((resolve) => setTimeout(resolve, time));

}

步骤4:编译运行,可以使用模拟器或者真机。

步骤5:运行回声服务器(需要读者自行开发或者等我拿到鸿蒙Next权限后写一个鸿蒙版本的回声服务器),然后配置客户端的服务端地址,然后单击“测试”按钮,执行后的截图如下所示:

截图清晰表明,出现了严重的粘包情况,客户端虽然是一个个发送的,代码如下所示:

 //发送测试消息到服务端

 async sendTestMsg2Server() {

  //执行TCP通讯的对象

  let tcpSocket = socket.constructTCPSocketInstance();

  await this.bind2Port(tcpSocket)

  await this.connect2Server(tcpSocket)

  for (let i = 0;i < 100; i++) {

   await tcpSocket.send({ data: i.toString() })

   await sleep(1)

 }

}

但是接收的时候和发送的不一样,是一堆堆接收的,原因是什么呢?

3.TCP粘包原因分析

TCP是一种面向流的数据传输协议,传输的对象是连续的字节流,内容之间并没有明确的分界标志,严格来说,并不存在粘包的问题,而通常所说的粘包,更多的是一种逻辑上的概念,也就是人为的把TCP传输的字节流划分成了一个个的数据包,发送端确定了数据包之间的边界,但是接收端并不能保证按照数据包的边界来接收。对于本示例中发送端和接收端不匹配的情况,还可能和下面的原因有关:

1)发送端启用了Nagle算法

发送端对于小包,可能会累计起来,到了一定的数据量或者其他条件满足才发送给接收端,这是导致粘包的一个重要原因。

2)TCP的滑动窗口机制

根据滑动窗口的机制,发送端一次发送数据量的多少并不完全是由自己决定的,还要受接收端的缓存大小限制,这也会导致发送端原本计划一次发送的数据包被分为多次发送。

3)MSS和MTU分片

如果一次需要发送的数据大于MSS或者MTU时,数据会被拆分成多个包进行传输,这也会导致粘包的产生。

4)接收端不及时接收

如果接收端不能及时接收缓冲区的数据包,那么在其后的某次接收中,就会出现接收多个数据包的情况。

4.TCP粘包问题解决方案

知道了数据粘包的原因,解决起来就有方向了,大体可以分为两种方案,一种是指定数据包结束标志,另外一种是通过固定包头指定包的长度,不过这两种方案都需要服务端配合才行,因为API 9版本的鸿蒙尚未支持TCP服务端,虽然更高的10、11支持了,但是我还没有这些版本的使用权限,暂时无法编写服务端程序,等以后开放了权限再补充编写粘包问题的解决方案示例吧。

(本文作者原创,除非明确授权禁止转载)

本文源码地址:

https://gitee.com/zl3624/harmonyos_network_samples/tree/master/code/tcp/StickyDemo

本系列源码地址:

https://gitee.com/zl3624/harmonyos_network_samples

到此这篇tcp坐标系(tcp坐标系原点的概念)的文章就介绍到这了,更多相关内容请继续浏览下面的相关推荐文章,希望大家都能在编程的领域有一番成就!

版权声明


相关文章:

  • 甲骨文ocm(甲骨文ocm证书含金量)2025-10-19 13:54:10
  • git如何用socks代理(git ssh代理)2025-10-19 13:54:10
  • condy怎么读(condy读音)2025-10-19 13:54:10
  • tomcat乱码怎么改(tomcat解决乱码问题)2025-10-19 13:54:10
  • 预适应训练仪功效与作用视频CCTV10(预适应训练仪功效与作用视频)2025-10-19 13:54:10
  • cnns认证有什么用(cns认证标准的内容有哪些)2025-10-19 13:54:10
  • pointcnn和pointnet很像(pointnetvlad)2025-10-19 13:54:10
  • cond是什么意思(ocufilcond是什么意思)2025-10-19 13:54:10
  • pcie50固态硬盘什么时候出(pci e固态硬盘)2025-10-19 13:54:10
  • vconn什么意思(vcont)2025-10-19 13:54:10
  • 全屏图片