的回答已经很好了,我们项目里用的有点不同,每个client fd只开两个goroutine:writeRoutine、readRoutine。
作为两个后台线程:
writeRoutine平常阻塞在<-tcpConn.writeChan读数据这里(逻辑线程通过SendMsg接口往tcpConn.writeChan <- buf写数据),chan有数据即取出写入tcpConn;
readRoutine则阻塞在io.Read处,有数据到来即被唤醒,处理粘包、分发。
消息分发处理上没有再加handle goroutine,直接是readRoutine调msgDispatcher,主要是避免开过多goroutine,且HandleMsgFunc中并无耗时或阻塞操作,readRoutine能够很快处理完,不会影响tcp数据接收(readRoutine里的io.Read用的bufio缓冲,消息响应函数若有耗时/io操作会单独开goroutine)
至于关闭的问题,我们都是通过readRoutine触发的,设置tcpConn.conn.SetReadDeadline,超时/读取报错,即结束readRoutine的循环,再开启关闭流程:
(1)向writeChan中写入一个nil buf,触发writeRoutine结束;
(2)tcpConn.conn.Close()
(3)设置closeflag
(4)调用注册onNetClose函数指针,里面清理些逻辑相关的东东,比如连接管理啥的
代码参考:
Sundry/tcp_conn.go at master · 3workman/Sundry · GitHub