客户端流和双向流里,什么时候 Recv 返回 io.EOF?CloseSend 在线上是什么?服务端先结束了客户端还能 Send 吗?两边都在发时会不会死锁?这篇逐个验证。
客户端发了 3 条、还没 CloseAndRecv:服务端 (无记录)(没有 EOF,在等更多消息)
CloseAndRecv 后:resp.N=3 err=<nil>;服务端 Recv 收到 io.EOF(共 3 条),SendAndClose
C→S HEADERS 长 25 标志 END_HEADERS
C→S DATA 长 9 标志
C→S DATA 长 9 标志
C→S DATA 长 9 标志
C→S DATA 长 0 标志 END_STREAMCloseSend 就是一个长度为 0 的 DATA 帧加 END_STREAM,表示客户端到服务端这个方向结束。服务端 Recv 得到 io.EOF 就是它。
客户端依次收到 [late-0 late-1 late-2],最后 Recv 返回 EOF;服务端收到 EOF 之后又发了 3 条
客户端 CloseSend 之后服务端还可以继续 Send,客户端 Recv 到服务端 handler 返回才得到 io.EOF。两个方向独立关闭。
early-ok 服务端读了 1 条就 return nil:后续 3 次 Send 返回 [EOF EOF EOF];Recv 返回 io.EOF(状态为 OK) early-err 服务端读了 1 条就 return PermissionDenied:后续 3 次 Send 返回 [EOF EOF EOF];Recv 返回 code=PermissionDenied message="不让你继续"
服务端提前结束后,客户端 Send 返回 io.EOF,无论服务端返回的是 nil 还是错误。真正的状态要调 Recv 才能拿到。
1 秒后客户端只 Send 成功了 5 条(共 1280 KiB),之后卡住;服务端状态:(服务端 handler 还没返回,阻塞在 Send 上,没有日志) deadline 到后:客户端 Send 退出;服务端:服务端 Send 错误:rpc error: code = Canceled desc = context canceled 对照:客户端用另一个 goroutine 同时 Recv:100 条(25 MiB)Send 完,用时 261.9 ms;服务端:服务端 Recv 收到 io.EOF(客户端已 CloseSend)
双向流里服务端同一个 goroutine 里收一条回一条,客户端只管 Send 不 Recv,两边的窗口都会满,双方互相等待。收发要放在不同的 goroutine 里。
500 ms 后服务端:(服务端还没返回) deadline 到了之后客户端 Recv 返回 code=DeadlineExceeded;服务端:服务端 Recv 错误:rpc error: code = Canceled desc = context canceled
客户端不 CloseSend,服务端的 Recv 永远等不到 EOF,只能等到 deadline 或取消。
服务端 return nil 客户端 Recv 最终:io.EOF(状态为 OK) 服务端记录:服务端读了 1 条就 return nil 服务端 return 错误 客户端 Recv 最终:code=PermissionDenied 服务端记录:服务端读了 1 条就 return PermissionDenied 客户端 CloseSend 客户端 Recv 最终:io.EOF(状态为 OK) 服务端记录:服务端 Recv 收到 io.EOF(客户端已 CloseSend) 客户端 cancel 客户端 Recv 最终:code=Canceled 服务端记录:服务端 Recv 错误:rpc error: code = Canceled desc = cont
io.EOF 只出现在状态为 OK 的时候;非 OK 的状态,Recv 返回带 code 的错误;取消得到 Canceled。