我的字节`len()`长度超出 14032 以后的就没用了,下面是代码
```golang
func (rc *rpcClient) Send(data map[string]interface{}) (string, error) {
const long = 128
// 取出的字节切片
buf := make([]byte, long)
//Buffer是一个实现了读写方法的可变大小的字节缓冲
resp := new(bytes.Buffer)
//var resp string
if rc.server.Ip == "" {
fmt.Println("服务器连接参数错误")
}
// 建立连接
conn, err := net.DialTimeout("tcp", rc.server.Ip+":"+rc.server.Port, 1*time.Second)
if err != nil {
return resp.String(), errors.New("TCP 连接超时")
}
// 异常关闭连接
defer conn.Close()
// 获取请求信息
byteData, err := json.Marshal(&data)
// 发送请求
n, err := conn.Write(byteData)
if err != nil {
return resp.String(), errors.New("TCP 发送请求失败")
}
// 读取响应
n, err = conn.Read(buf)
if err != nil {
return resp.String(), errors.New("TCP 读取响应失败")
}
resp.Write(buf[0:n])
// 续读
for n == long {
n, err = conn.Read(buf)
resp.Write(buf[0:n])
}
//fmt.Println("Reply from server ", rAddr.String(), resp)
return resp.String(), nil
}
```
不清楚是什么限制了这个字节切片的长度,如果接收的信息不是很长就没有问题。
之前 `resp` 是个 `string` 类型每次从tcp读取完字节切片数据转成字符用`resp += string(buf[0:n])`拼接在后面直至读取完毕。`long`变量设置的是 10240,一直运转没有问题,直到有一个消息信息长度为25147的时候,就开始丢字符,只能接受14032个字符。然后将`long`改小,直到128才能完成接收所有字符串(150也行)。
然后我网上查高效的字节切片转字符串使用`bytes.Buffer` 类型,就把 resp 换成了`bytes.Buffer` 类型,然后相同的问题又出现了,只能读取 14032 长度的字节切片。
我有一个PHP版本的客户端读取超长字节没有问题,每次设置的是读取 10240 长度。可以排除服务端问题。
之前使用字符串拼接 `long = 128` 也可以全部接收完成
## 问题
是golang 自身限制了字节切片长度吗?
为什么字符串拼接tcp响应的时候要把接受缓冲区设置的很小才能接受很长的字符串呢?
为什么时候用字节切片拼接统一转string的时候回出现字节切片不能接收全部字符串呢?
刚刚又调试了一下,发现了新问题就是
```golang
// 续读
for n == long {
n, err = conn.Read(buf)
resp.Write(buf[0:n])
}
```
这部分代码的问题,因为有一次读取的时候 n<long 了程序认为是 TCP 接收完成了,其实还有数据,手动设置了接受次数就全部收到了。
为什么会出现TCP没有接收完成切片却没有读满呢?
第 1 条附言 ·
找到问题了,是判断TCP接收完成的代码有问题。改成如下判断就好了
```golang
// 读取响应
_, err = io.Copy(&resp, conn)
if err != nil {
return resp.String(), err
}
```
5楼 <a href="/user/BlackBerryX" title="@BlackBerryX">@BlackBerryX</a> tcp除了超时,异常等处理之外,常规数据上要处理的点。</br>
1.tcp在读取和发送时, 要注意使用的NONBLOCK还是BLOCK的。golang是NONBLOCK的, 所以在read时, 要注意指定读取的字节数。</br>
比如传入1024大小的slice, 但由于非阻塞, backlog有数据就通知可以读了, 所以可能会在小于1024时就读取了。同样写的时候也要注意实际写入的字节数。</br>
2.半包是在发送的数据比较多时,tcp会拆分成一个一个小包发送。发送到目标服务器缓存后, 非阻塞时会直接通知程序去读取,阻塞时在缓存满了也会通知程序读取,两者都会导致数据未接收全。 粘包则是小包或者发送方发送速度大于接收方时,缓存区存储了多个消息的内容。程序在未定义包的区分时,就会把不同的包作为同个包来读取。</br>
短链接时,如果每次直到读完即循环读取直到EOF, 然后再处理是可以解决,只收数据的场景没问题。当要写回响应结果时就不行,因为链接已经关闭。又因为tcp是双全共工,发送方可以只关闭读也是可以的,不过我很少见到这么处理的。</br>
更加常见的做法是自定封包解包, 最简单的一种是先写4字节长度,然后再写数据。接收时先读4位,然后读数据。
但是这种会存在问题,一旦某个环节读错位了,就可能导致长度不正确,导致该链接一直等待数据读完。一种简单的做法时再包头前加一个魔数,当头几个字节和魔数不匹配,直接关闭链接表示错误。以上只是简单的,实际根据自己的设计,自定义协议。</br>
还有种简单的是处理字符串时,用特定分隔符做包分割,如换行等。非万能场景。
#6
更多评论