Go 学习笔记12-Go Socket编程
目录
Go 为开发人员提供了阻塞 I/O 模型,Gopher 只需在 Goroutine 中以最简单、最易用的“阻塞 I/O 模型”的方式,进行 Socket 操作就可以。
但这种方式是 Go 模拟出来,是为了让开发者使用起来更加简单易懂,对应的、真实的底层操作系统 Socket,实际上是非阻塞的。
Go 没有使用基于线程的并发模型,而是使用了开销更小的 Goroutine 作为基本执行单元,这让每个 Goroutine 处理一个 TCP 连接成为可能,并且在高并发下依旧表现出色。
虽然目前主流 socket 网络编程模型是 I/O 多路复用模型,但考虑到这个模型在使用时的体验较差,Go 语言将这种复杂性隐藏到运行时层,并结合 Goroutine 的轻量级特性,在用户层提供了基于 I/O 阻塞模型的 Go socket 网络编程模型,这一模型就大大简化了编程难度。
1,Server 端
Go Server 端编程套路模板:
func handleConn(c net.Conn) {
defer c.Close()
for {
// read from the connection
// ... ...
// write to the connection
//... ...
}
}
func main() {
l, err := net.Listen("tcp", ":8888")
if err != nil {
fmt.Println("listen error:", err)
return
}
for {
c, err := l.Accept()
if err != nil {
fmt.Println("accept error:", err)
break
}
// start a new goroutine to handle
// the new connection.
go handleConn(c)
}
}
2,Client 端
Go Client 端与 Server 端建立连接的两种方式:
conn, err := net.Dial("tcp", "localhost:8888")
// 带有超时机制的建连
conn, err := net.DialTimeout("tcp", "localhost:8888", 2 * time.Second)
net.Dial 函数的第一个参数的可选值,共九个:
- “tcp”:代表 TCP 协议,其基于的 IP 协议的版本根据参数address的值自适应。
- “tcp4”:代表基于 IP 协议第四版的 TCP 协议。
- “tcp6”:代表基于 IP 协议第六版的 TCP 协议。
- “udp”:代表 UDP 协议,其基于的 IP 协议的版本根据参数address的值自适应。
- “udp4”:代表基于 IP 协议第四版的 UDP 协议。
- “udp6”:代表基于 IP 协议第六版的 UDP 协议。
- “unix”:代表 Unix 通信域下的一种内部 socket 协议,以 SOCK_STREAM 为 socket 类型。
- “unixgram”:代表 Unix 通信域下的一种内部 socket 协议,以 SOCK_DGRAM 为 socket 类型。
- “unixpacket”:代表 Unix 通信域下的一种内部 socket 协议,以 SOCK_SEQPACKET 为 socket 类型。
3,Socket 读操作
从 Socket 读数据的三种情况:
- Socket 中无数据:如果没有设置超时时间,那么读操作会一直阻塞
- SetReadDeadline 方法可以设置超时时间,SetReadDeadline 方法接受一个绝对时间作为超时的 deadline。
- 一旦通过这个方法设置了某个 socket 的 Read deadline,当发生超时后,如果我们不重新设置 Deadline,那么后面与这个 socket 有关的所有读操作,都会返回超时失败错误。
SetReadDeadline(time.Time{})
可用于取消超时时间
- Socket 中有部分数据:Socket 中有部分数据就绪,且数据数量小于一次读操作期望读出的数据长度
- 读操作将会成功读出这部分数据,并返回,而不是等待期望长度数据全部读取后,再返回
- Socket 中有足够数据:成功读出期望的数据,并返回
- 剩下的数据会在下一次读取时获取
4,Socket 写操作
当 Write 调用的返回值 n 的值,与预期要写入的数据长度相等,且 err = nil 时,我们就执行了一次成功的 Socket 写操作,这是我们在调用 Write 时遇到的最常见的情形。
其它情况还有:
- 写阻塞
- 写入部分数据
- 写入超时
- SetWriteDeadline 方法用于设置写超时时间
5,关闭 Socket
当客户端主动关闭了 Socket,那么服务端的Read调用将会读到什么呢?这里要分“有数据关闭”和“无数据关闭”两种情况。
- 有数据关闭:指在客户端关闭连接时,Socket 中还有服务端尚未读取的数据
- 在这种情况下,服务端的 Read 会成功将剩余数据读取出来,
- 最后一次 Read 操作将得到io.EOF错误码,表示客户端已经断开了连接。
- 无数据关闭
- 服务端调用的 Read 方法将直接返回io.EOF。
(完。)
文章作者 @码农加油站
上次更改 2022-06-23