Go_TCP服务端实现读写分离
实现简单的TCP通信,客户端定时推送消息,服务端收到客户端消息后进行响应
客户端代码,连接服务端,定时发送消息
func main() {
conn, err := net.Dial("tcp", "127.0.0.1:8000")
if err != nil {
fmt.Println(err)
return
}
defer conn.Close()
for {
conn.Write([]byte("client message"))
buf := make([]byte, 512)
n, err := conn.Read(buf)
if err != nil {
fmt.Println("Read from client failed, err:", err)
break
}
data := string(buf[:n])
fmt.Println("Receive server: ", data)
time.Sleep(time.Second)
}
}
服务端代码,接收客户端连接,响应消息
func main() {
listen, err := net.Listen("tcp", ":8000")
if err != nil {
fmt.Println("Listen failed, err:", err)
return
}
for {
conn, err := listen.Accept()
if err != nil {
fmt.Println("Accept failed, err:", err)
continue
}
go Start(conn)
}
}
func Start(conn net.Conn) {
defer conn.Close()
for {
// read
buf := make([]byte, 512)
n, err := conn.Read(buf)
if err != nil {
fmt.Println("Read from client failed, err:", err)
break
}
data := string(buf[:n])
fmt.Println("Receive client: ", data)
// write
conn.Write([]byte("ok"))
}
}
服务端这里处理客户端连接时,读和写是在一个协程中。为了便于在读和写逻辑中添加更多的控制,实现读写分离
- 单独启动Reader和Writer协程
- 保证回应收到的信息,增加channel维护读和写次序
- 出错时读和写可以退出循环,增加连接是否关闭属性
- 退出时可以清理资源,增加channel阻塞等待断开连接
type Connection struct {
conn net.Conn
msgChan chan []byte
exitChan chan bool
isClose bool
}
func Start(conn net.Conn) {
defer fmt.Println("Client exit")
defer conn.Close()
c := &Connection{conn, make(chan []byte), make(chan bool, 1), false}
go c.StartReader()
go c.StartWriter()
select {
case <-c.exitChan:
}
}
func (c *Connection) StartReader() {
fmt.Println("Reader start")
defer fmt.Println("Reader exit")
for !c.isClose {
buf := make([]byte, 512)
n, err := c.conn.Read(buf)
if err != nil {
c.exitChan <- true
c.isClose = true
fmt.Println("Read Data error: ", err)
break
}
c.msgChan <- buf[:n]
data := string(buf[:n])
fmt.Println("Receive client: ", data)
}
}
func (c *Connection) StartWriter() {
fmt.Println("Writer start")
defer fmt.Println("Writer exit")
for !c.isClose {
select {
case <-c.msgChan:
if _, err := c.conn.Write([]byte("ok")); err != nil {
c.exitChan <- true
c.isClose = true
fmt.Println("Send Data error: ", err)
return
}
case <-time.After(3 * time.Second):
}
}
}
这里需要注意的点有:
- 定义Connection结构体
- isClose属性控制是否退出循环
- Writer增加超时等待
- exitChan的长度设置为1,出错时写入数据不会阻塞