Golang 的 socket 编程 (四):解决粘包问题及制定通信协议

马谦马谦马谦 Golang评论4021字数 1300阅读 4 分 20 秒阅读模式

一、概述

前面已经完成了一个完美的多并发 CS 模型,但美中不足的是没有解决粘包问题。

1.1 什么是粘包问题?

在网络传输中,数据都是通过数据流来传输的,也就是以比特来传输。传输的过程中我们可能会遇到各种各样的问题导致数据传输异常,最常见的就是网络发送时延。网络时延会导致服务端此时收到的数据的时间有偏差,然后就导致数据接收数据的时间不一致。

可以看一个例子,修改上篇的服务端和客户端为以下内容:

客户端改为以下:

以上我们发送了 100 条 json 到服务端,按照预想服务端将会输出 100 行 json,但是实际上并不是:

Golang的socket编程(四):解决粘包问题及制定通信协议-图片1

这个现象产生的原因是因为服务端每次读取数据之后将会休眠 1ns,但是对于客户端来说,这 1ns 它还在一直传输数据,1ns 的时间可能 发送了 1 条,也可能是 2 条,这个数量我们不知道是多少,也无法控制。于是就导致数据堆积,服务端再读取就会出问题了。与此同时,由于缓冲区有限,一次最多读取 2048 个字节,堆积的字节超过 2048 的也无法读取,只能留到下次读取,这种现象就是粘包问题。

二、解决办法

上面抛出了粘包的问题后,现在就要开始想办法处理了,怎么处理呢?这里就需要用到协议了,协议就是双方约定好的数据包格式, 让服务端知道从哪里开始读,读到哪里结束,这样就不会出错了。实现这个协议最简单的办法就是加上一个协议头和一个数据包长度 。

假设现在要发送 [0x11, 0x22, 0x33],约定协议头为 [0xaa, 0xbb],由于发送数据的长度是三个字节,所以经过客户端封装之后的数据就变成了 [0xaa, 0xbb, 0x03, 0x11, 0x22, 0x33]

服务端收到数据后,先找 [0xaa, 0xbb]的位置,然后根据他们的位置得到数据长度为 3,于是再往后读三个字节就是真正的的数据 部分了。

三、实现

指定好了协议之后就可以开始实现了,为了方便,直接把这里写成一个对象:

包头的定义:

包头包括协议头和数据长度,共六个字节。

3.1 数据发送时的封装

writeByte()的实现

3.2 接收数据时解包

readHead()的实现:

readNByte()的实现:

3.3 完整代码

四、服务端

五、客户端

六、运行

运行服务端再运行客户端就会发现,已经不和之前的一样了,整整齐齐,perfect!

Golang的socket编程(四):解决粘包问题及制定通信协议-图片2

 

  最后更新:2017-11-18
马谦马谦马谦
  • 本文由 马谦马谦马谦 发表于 2017 年 9 月 13 日 22:19:46
  • 转载请务必保留本文链接:https://www.dyxmq.cn/program/code/golang/golang-socket-4.html
解决gvm工具无法下载安装包的问题 Golang

解决 gvm 工具无法下载安装包的问题

GVM(Go Version Manager) 是一款用于管理和切换不同 Go 语言版本的工具。它允许用户在同一台计算机上轻松安装、使用和管理多个 Go 版本,同时还能确保项目之间的依赖关系井然有序。 GVM 的主...
国内几个优质的Go Module代理仓库服务 Golang

国内几个优质的 Go Module 代理仓库服务

一、简介 go module 公共代理仓库,代理并缓存 go 模块。你可以利用该代理来避免 DNS 污染导致的模块拉取缓慢或失败的问题,加速你的构建。 简单来说就是国内访问被墙,go get 无法在线获取到仓库,...
socket多路IO复用之select模型 C/C++

socket 多路 IO 复用之 select 模型

select 模型是 socket 中的一种多路 IO 复用模型之一,通过轮询的方式来完成多路访问控制。 一个很简单的例子来描述 select 模型: 幼儿园老师要照顾所有的小朋友,每天他都会轮流去问小朋友:「小朋...
TCP协议中的三次握手和四次挥手 TCP/IP

TCP 协议中的三次握手和四次挥手

一、三次握手 TCP 协议的三次握手和四次挥手分别表示了 TCP 连接的建立和释放过程,在整个 TCP 协议是一个很重要的内容,同时也是面试时的常见考点。 趁着找工作的劲,使用 socket+tcpdump 分析了...
匿名

发表评论

匿名网友
:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:
确定

拖动滑块以完成验证