golag udp和tcp服务端在使用上的一些区别

1.tcp和udp的golang服务端示例代码

udp https://github.com/fwhezfwhez/TestX/tree/master/test_udp
tcp https://github.com/fwhezfwhez/TestX/tree/master/test_tcp/basic

2. 一些需要注意到的区别

udp与tcp的一些概念定义和使用场景不是本文的讨论范畴,自行百度。

2.1 tcp需要自主拆包组包

tcp是面向连接的,服务端与客户端在不关闭连接前,共用一个net.Conn,这意味着,如果某一端出现:
(消息连续发送)

func NewByte(byts ...byte) []byte {
	var rs = make([]byte, 0 , 512)
	rs = append(rs, byts...)
	return rs
}
func main(){
	...
	// var con net.Conn
	conn.Write([]byte(NewByte(1,2,3,4,5,6)))
	conn.Write([]byte(NewByte(1,2,3,4,5,6)))
	conn.Write([]byte(NewByte(1,2,3,4,5,6)))
	conn.Write([]byte(NewByte(1,2,3,4,5,6)))
}

另一端收到的数据包是:

[1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4 5 6]

所以,对协议的定义和拆组,都需要有相应的手段,常见的是:

  1. 消息定长
  2. 消息头记录消息长度

这两种方法,第一种因为消息定长,截取时按照长度截取即可,协议简单,缺点是消息长度取消息可能达到的最大值,未达到长度的消息将会补0,即很浪费空间。第二种需要设定好协议的内容,比如

[4]byte 消息总长度(不包括消息总长度本身的4)
[]byte 消息实体

读取时,先读[4]byte,再读 [4]byte 经过大端/ 小端转换成的int长度,对应的消息实体。

相比较之下,udp不需要拆组包
udp的接口不包含常用的Read()和Write(),而是ReadFrom()和WriteTo(),每次读取,都会把单次的消息读完。
假设连续发送和tcp相同的情形.

func NewByte(byts ...byte) []byte {
	var rs = make([]byte, 0 , 512)
	rs = append(rs, byts...)
	return rs
}
func main(){
	...
	// var conn net.PackConn
	conn.WriteTo([]byte(NewByte(1,2,3,4,5,6)))
	conn.WriteTo([]byte(NewByte(1,2,3,4,5,6)))
	conn.WriteTo([]byte(NewByte(1,2,3,4,5,6)))
	conn.WriteTo([]byte(NewByte(1,2,3,4,5,6)))
}

另一端会收到:

[1,2,3,4,5,6],[1,2,3,4,5,6],[1,2,3,4,5,6],[1,2,3,4,5,6]

看起来很美,但是有两个致命问题,将在后面描述。
i. 预设定的buffer一旦小了,直接抛错,不可循环ReadFrom:
ii. 收到的消息虽然分块好了,但是顺序不保证,且不保证收到

2.2 udp的乱序描述

常说udp不保证顺序可靠,这句话讲的还不够细致。
当一端发送:

[1,2,3,4,5,6], [7,8,9]

另一端可能得到:

[7,8,9], [1,2,3,4,5,6]

乱序指的就是这种,最小单元为消息块的乱序,而不是像这样:

[1, 2, 5,6,4,3], [7,8,9]

又或是:

[1,2,7,8] [3,4,5,9,6]

消息块内部的混乱,不管是udp还是tcp都不存在的,tcp里遇到这种情况,会重发,udp里这种情况会丢弃,也就是常说的丢包

2.3 udp与tcp的连接,后者支持分段读取,前者不支持

udp和tcp在客户端的的连接使用的是接口类型net.Conn,都具备Read([]byte) error方法.
在客户端读取时,tcp和udp的读取存在区别(因为其实现接口的实例细节不同造成的)。
tcp的读取方式

// tcp 可以这样分段读,先读头部信息[4]byte,再读载荷
var info = make([]byte,4, 216)
conn.Read(info)
length := binary.BigEndian.Uint32(info)
var content = make([]byte, lengh, length)
conn.Read(content)

udp的读取方式
以tcp的读取方式来读取udp消息,会抛出 buffer size too small error,正确的读法是:

var buffer = make([]byte, 4096, 4096)
n,_ := conn.Read(buffer)
buffer = buffer[0:n]

和前面说的一致,udp的读取,一直需要预设一个buffer大小,不可过小

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值