3

Go 学习笔记12-Go Socket编程

 1 year ago
source link: https://codeshellme.github.io/2022/06/go12/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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()
// 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
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。

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK