go tcp粘包-金沙1005

go tcp粘包_tcp粘包处理粘包tcp粘包:tcp是流式协议:发送包的时候一次可能没有发完可能给了下一次nagle算法导致客户端发送的粘包,本意是为了改善客户端网络nagle算法该算法要求一个tcp连接上最多只能有一个未被确认的未完成的小分组,在该分组ack到达之前不能发送其他的小分组,tcp需要收集这些少量的分组,并在ack到来时以一个分组的方式发送出去;其中小分组的定义是小于mss的任何分组;该算法的优越之处在于它是自适应的,确认到达的越快,数据也就发哦送的越快;而在希望减少微小分组数目的低速广域网上,则

  • tcp粘包:
    • tcp是流式协议:发送包的时候一次可能没有发完可能给了下一次 nagle算法导致客户端发送的粘包,本意是为了改善客户端网络
  • nagle算法

该算法要求一个tcp连接上最多只能有一个未被确认的未完成的小分组,在该分组ack到达之前不能发送其他的小分组,tcp需要收集这些少量的分组,并在ack到来时以一个分组的方式发送出去;其中小分组的定义是小于mss的任何分组; 该算法的优越之处在于它是自适应的,确认到达的越快,数据也就发哦送的越快;而在希望减少微小分组数目的低速广域网上,则会发送更少的分组;

  • nagle算法是以他的发明人john nagle的名字命名的,它用于自动连接许多的小缓冲器消息;这一过程(称为nagling)通过减少必须发送包的个数来增加网络软件系统的效率。
	if there is new data to send #有数据要发送
        # 发送窗口缓冲区和队列数据 >=mss,队列数据(available data)为原有的队列数据加上新到来的数据
        # 也就是说缓冲区数据超过mss大小,nagle算法尽可能发送足够大的数据包
        if the window size >= mss and available data is >= mss 
            send complete mss segment now # 立即发送
        else
            if there is unconfirmed data still in the pipe # 前一次发送的包没有收到ack
                # 将该包数据放入队列中,直到收到一个ack再发送缓冲区数据
                enqueue data in the buffer until an acknowledge is received 
            else
                send data immediately # 立即发送
            end if
        end if
    end if
go tcp粘包_tcp粘包处理

为防止网络爬虫,请关注公众号回复”口令”

激活idea 激活clion
datagrip dataspell
dotcover dotmemory
dottrace goland
phpstorm pycharm
resharper reshac
rider rubymine
webstorm 全家桶

https://baike.baidu.com/item/nagle算法

服务端代码

package main
import (
	"fmt"
	"net"
)
func main() { 
   
	// 本地端口启动服务
	listener, err := net.listen("tcp", "localhost:20000")
	if err != nil { 
   
		fmt.println("服务器启动失败....", err)
		return
	}
	fmt.println("监听成功...")
	// 等待连接
	for { 
   
		conn, err := listener.accept()
		if err != nil { 
   
			fmt.println("连接建立失败...", err)
			break
		}
		fmt.println("连接成功...")
		go process(conn)
	}
	// 通信
}
func process(conn net.conn) { 
   
	var temp [128]byte
	for { 
   
		n, err := conn.read(temp[:])
		if err != nil { 
   
			fmt.println("服务端读取数据失败....")
			return
		}
		fmt.println("读取数据成功:")
		fmt.println(string(temp[:n]))
	}
}

客户端代码

package main
import (
	"fmt"
	"net"
)
func main() { 
   
	conn, err := net.dial("tcp", "127.0.0.1:20000")
	if err != nil { 
   
		fmt.println("连接失败,err:", err)
		return
	}
	defer conn.close()
	msg := "hello socket"
	for i := 0; i < 20; i { 
   
		conn.write([]byte(msg))
	}
}

粘包现象

在这里插入图片描述

  • 可以看到每次收数据会收到多个“hello socket” 而不是单独的一个,也就是说客户端把多个小包合并一起发了

基础知识

大端小端

  • 对于 0x123456
    在这里插入图片描述
  • 要存入内中之中存在两种情况:
    在这里插入图片描述
    或者
    在这里插入图片描述
    高位写在右边,则在读取的时候应该从右往左读,反之亦然
    高位写在内存低地址:大端
    高位写在内存高地址:小端
    在这里插入图片描述

解决思路

  • 出现“粘包”问题的关键在于接收方不知道你发送的数据包的实际大小,也就是说我们在发送数据的时候可以指定数据包中的一个位置来存放当前数据包的实际大小。
  • 封包:实际上就是给一段数据加上包头,这样一来数据包就分为包头和包体两个部分(过滤非法包的时候会加上包尾部分)。包头的长度是固定的,并且存储了包体的长度,更具包头的长度固定以及其中包含的长度 变量就能正确的拆分出一个完整的数据包。
  • 我们可以自定义一个小协议,在数据包的前4个字节存储数据的长度

协议代码

package proto
import (
	"bufio"
	"bytes"
	"encoding/binary"
)
func encode(message string) ([]byte, error) { 
   
	// 读取消息的长度转换成int32类型(4字节)
	var length = int32(len(message))
	var pkg = new(bytes.buffer)
	// 写入消息头
	err := binary.write(pkg, binary.littleendian, length)
	if err != nil { 
   
		return nil, err
	}
	// 写入包体
	err = binary.write(pkg, binary.littleendian, []byte(message))
	if err != nil { 
   
		return nil, err
	}
	return pkg.bytes(), nil
}
// 解码
func decode(reader *bufio.reader) (string, error) { 
   
	// 读消息长度
	lengthbyte, _ := reader.peek(4)
	lengthbuff := bytes.newbuffer(lengthbyte)
	var length int32
	err := binary.read(lengthbuff, binary.littleendian, &length)
	if err != nil { 
   
		return "", err
	}
	// buffer返回缓冲中现有的可读的字节数
	if int32(reader.buffered()) < length4 { 
   
		return "", err
	}
	// 读取真正的数据
	pack := make([]byte, int(4length))
	_, err = reader.read(pack)
	if err != nil { 
   
		return "", err
	}
	return string(pack[4:]), nil
}

服务端代码

package main
import (
	"20_tcp/03_粘包/proto"
	"bufio"
	"fmt"
	"io"
	"net"
)
func main() { 
   
	// 本地端口启动服务
	listener, err := net.listen("tcp", "localhost:20000")
	if err != nil { 
   
		fmt.println("服务器启动失败....", err)
		return
	}
	fmt.println("监听成功...")
	// 等待连接
	for { 
   
		conn, err := listener.accept()
		if err != nil { 
   
			fmt.println("连接建立失败...", err)
			break
		}
		fmt.println("连接成功...")
		go process(conn)
	}
	// 通信
}
func process(conn net.conn) { 
   
	defer conn.close()
	reader := bufio.newreader(conn)
	for { 
   
		msg, err := proto.decode(reader)
		fmt.println("收到消息:", msg)
		if err == io.eof { 
   
			return
		}
		if err != nil { 
   
			fmt.println("decode失败,err:", err)
		}
	}
}

客户端代码

package main
import (
	"20_tcp/03_粘包/proto"
	"fmt"
	"net"
)
func main() { 
   
	conn, err := net.dial("tcp", "127.0.0.1:20000")
	if err != nil { 
   
		fmt.println("连接失败,err:", err)
		return
	}
	defer conn.close()
	msg := "hello socket"
	for i := 0; i < 20; i { 
   
		// 调用协议编码协议
		b, err := proto.encode(msg)
		if err != nil { 
   
			fmt.println("encode失败,err:", err)
		}
		conn.write(b)
		fmt.println("发送成功...,msg:", b)
	}
}

结果

在这里插入图片描述

js555888金沙老品牌的版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由思创斯整理,转载请注明出处:https://ispacesoft.com/121273.html

(0)

相关推荐

  • goland永久激活成功教程_2021年激活码刚出无限使用不过期(2022.7goland激活成功教程)这是一篇idea技术相关文章,由思创斯为大家提供,主要知识点是关于2022jetbrains全家桶永久激活码的内容https://ispacesoft.com/bc-jhi…

  • 安装go语言开发环境tableofcontents1.系统硬件与操作系统2.安装golang3.配置环境变量4.测试golang的安装5.安装go的相关工具6.安装与运行gotour7.第一个包与测试7.1.包对象的编写7.2.调用包对象7.3.包的测试7.4.关于包名的讨论8.问题或要点小结9.相关博客链接1.系统硬件与操作系统本次实验安装go语言所使用的环境如下:处理器架构:x86-64(intel®.

  • vc 调用api(google com api)[通俗易懂]google公司已经将googlecomapi开放,这样我们就可以通过开放的api来对googleearth进行操作了,比如控制当前视图的高度、中心经纬度,保存当前图片等等。下面是googlecomapi的网址:http://earth.google.com/comapi/那么,vc程序员如何使用这些api来控制googleearth呢?下面我们编写一个简单的程序来说明对g

  • google maps api_谷歌乡村地图谷歌地图api的使用,包含地图的加载,标记,信息窗口,获取当前位置,自定义控件等,此外还有在echarts中使用谷歌地图。

  • sentinel-go 源码系列(二)|初始化流程和责任链设计模式上节中我们知道了 sentinel-go 大概能做什么事情,最简单的例子如何跑起来 其实我早就写好了本系列的第二篇,但迟迟没有发布,感觉光初始化流程显得有些单一,于是又补充了责任链模式,二合一,内容显

  • rubydung游戏_go后端框架”ifyouprogramandwantanylongevitytoyourwork,makeagame.allelserecycles,butpeoplerewritearchitecturestokeepgamesalive.”,_why如果想让你的代码长寿,那就做一个游戏。所有其他的代码都在回收站了,但是人们总会花时间更新代码让

  • 【第三十二期】春招 golang实习面经 七牛「终于解决」一面(问的问题太多,只记住这些) 1.红黑树和二叉搜索树的区别 2.红黑树和平衡二叉树的区别,相比于平衡二叉树。 3.解决哈希冲突的方法 4.一致性哈希算法 5.lru算法 lru和lfu的区别?lr

  • goland2022 激活码最新【最新永久激活】「终于解决」(goland2022 激活码最新)本文适用于jetbrains家族所有ide,包括intellijidea,phpstorm,webstorm,pycharm,datagrip等。https://…

发表回复

您的电子邮箱地址不会被公开。

联系金沙1005

关注“java架构师必看”公众号

回复4,添加站长微信。

附言:ispacesoft.com网而来。

关注微信
网站地图