• Aeins
    2022-06-07
    几点疑问 1. 协议处理程序保证使用相同的字节序的情况下,有必要一定用大端序吗,改成小端序,也能成功。 2. TCP 保证顺序交付的,不指定字节序,顺序处理数据流可以吗。这时会有字节序问题吗,如果协议栈都使用同一种字节序呢。(我认为字节序和程序使用的字节序有关,如果每个程序都使用同一种字节序,那应该就不存在字节序问题了,比如本程序,收发都用相同的字节序处理,不知道这个结论对不对) 3. 协议头和协议体,分两次写入的,会不会有并发安全问题,为什么?这里应该没做到上节课说的,一次写入一个“业务包”吧。 4. 多次运行 client,错误偶发。有时 io.ReadFull 读不满数据,有时读取的数据长度不对,会是哪些原因导致的呢?

    作者回复: 问题很棒! 这里逐一回答一下: 1、2:网络字节序是TCP/IP中规定好的一种数据表示格式,它与具体的CPU类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释。 3. 按照每个连接一个 Goroutine 的模型,不是并发写,不存在你说的问题。 4. go doc io.ReadFull一下,一般情况下,ReadFull都会读出你想要的长度的数据。你遇到错误时,ReadFull返回什么error呢。 [upd]: 发现问题了。是client的SetReadDeadline设置为1s,太短了。已改,请pull最新demo代码。

    
    6
  • 左耳朵东
    2022-02-12
    client 代码中的 done chan 好像没必要吧,去掉它也能正常退出

    作者回复: 这里的确没必要。但是如果handle ack的goroutine在退出前需要执行一些清理工作,那么done就有必要了。否则可能会出现handle ack的goroutine没有执行完清理工作,send goroutine就退出的进而导致main goroutine退出前某handle ack的goroutine都没有执行完清理工作。

    共 2 条评论
    4
  • 枫
    2022-07-28 来自陕西
    // select { case <-quit: done <- struct{}{} return default: } 老师,client中读取服务端返回响应的这个goroutine中,这段select的作用不是很理解,如果没有从quit中收到值就会一直轮询,但是从quit中收到值又会return,那下面的代码不是一直都没有机会执行了吗

    作者回复: 如果没有从quit中收到值,是会轮询啊。不过每次轮询的间隔是5s,程序会先在socket上做阻塞读,直到超时。超时后就回到for开始处,这也给了goroutine一个优雅退出的机会。

    共 2 条评论
    3
  • 晚枫
    2022-05-09
    为什么totalLen指定了字节序,payload不需要指定吗?

    作者回复: 字节序是针对size>=2个字节的整型数而言的。payload对于该协议来说只是一个“字节序列”。协议的任务就是解析出payload,然后交给上层处理。

    共 2 条评论
    2
  • 罗杰
    2022-01-26
    还是老师实现的代码优雅,我们项目的这块代码是刚开始学 Go 时实现的,只能说可以用。但对比老师的实现,我觉得我们的代码可以好好优化一下了。

    作者回复: 👍

    
    2
  • 张尘
    2022-12-27 来自北京
    白老师好, 本节课受益颇多, 有点疑问, 还望有时间能够帮忙解答下: frameCodec.Decode返回值是自定义数据结构FramePayload packet.Decode的入参是[]byte client/server 代码中直接将FramePayload当做[]byte使用 frameCodec.Decode为什么要返回自定义数据结构FramePayload而不是[]byte呢? 是因为FramePayload的结构可能改变吗? FramePayload可能不是[]byte吗? FramePayload可能包含Packet之外的其它数据吗? 可是如果FramePayload的结构改变, 那client/server 的代码中直接将FramePayload当做[]byte的用法不是就有问题了吗?

    作者回复: 可以直接使用[]byte类型,这里定义FramePayload更多为了强调其是frame的payload,仅此而已。

    
    1
  • Sunrise
    2022-11-24 来自辽宁
    有个小疑问: func (p *myFrameCodec) Encode(w io.Writer, framePayload FramePayload) error { var f = framePayload ... } var f = framePayload 这个地方有必要重新定义一个 f 吗,直接使用 framePayload 会有什么问题?

    作者回复: “直接使用 framePayload ” 也没有问题。

    
    1
  • 农民园丁
    2022-11-02 来自北京
    请问老师,framePayload, err := frameCodec.Decode(c) 以上代码中"c"是net.Conn 类型, 而frameCodec.Decode(io.Reader)的输入参数是io.Reader, 这两个为什么可以不一样?

    作者回复: net.Conn可以理解为io.Reader这个接口类型的方法集合的超集,也就是说所有实现了net.Conn的类型,也都实现了io.Reader接口类型。

    
    1
  • Geek_25f93f
    2022-06-19
    老师,单元测试的代码是不是有点问题,就判断条件是if err == nil

    作者回复: 你指的是TestEncodeWithWriteFail这个unit test? 这个测试就是为了测试Encode失败的情况。只有err == nil的情况下,才不符合我们的预期。

    
    1
  • qiutian
    2022-06-10
    // tcp-server-demo1/packet/packet.go func Encode(p Packet) ([]byte, error) { var commandID uint8 var pktBody []byte var err error switch t := p.(type) { case *Submit: commandID = CommandSubmit pktBody, err = p.Encode() if err != nil { return nil, err } case *SubmitAck: commandID = CommandSubmitAck pktBody, err = p.Encode() if err != nil { return nil, err } default: return nil, fmt.Errorf("unknown type [%s]", t) } return bytes.Join([][]byte{[]byte{commandID}, pktBody}, nil), nil } 老师,这段代码的最后的 return bytes.Join(), nil这个在什么情况下回运行到呢?不是很理解

    作者回复: return语句最后的nil是代表err=nil,就是一切ok,没有报错。Encode函数的原型,最后一个返回值是一个error类型。

    共 2 条评论
    1