Golang中Accept()的工作原理详解

Golang中Accept()的工作原理详解

func main() {
	li, err := net.Listen("tcp", ":8080")
	if err != nil {
		log.Fatalln(err)
	}
	defer li.Close()

	for {
		conn, err := li.Accept()
		if err != nil {
			log.Println(err)
			continue
		}
		go handle(conn)
	}
}

func handle(conn net.Conn) {
	scanner := bufio.NewScanner(conn)
	for scanner.Scan() {
		ln := scanner.Text()
		fmt.Println(ln)
	}
	defer conn.Close()
}

在这里,Accept() 实现了与客户端的连接。我想知道的是 Accept() 具体是如何工作的?在 net/http 包中,只有函数名和返回值。即使我没有定义它的工作方式,它又是如何运作的呢?

以下是关于 net 包中 Accept() 的说明

类型 Listener 接口 { // Accept 等待并返回监听器的下一个连接 Accept() (Conn, error)

// Close 关闭监听器
// 任何被阻塞的 Accept 操作将被解除阻塞并返回错误
Close() error

// Addr 返回监听器的网络地址
Addr() Addr

}


更多关于Golang中Accept()的工作原理详解的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

Accept 是操作系统提供的系统调用。它只是被 Go 运行时封装,以便后续可以进行读写轮询。

如果您想了解更多关于 accept 的信息,可以查阅手册页:http://man7.org/linux/man-pages/man2/accept.2.html

更多关于Golang中Accept()的工作原理详解的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go语言中,Accept()方法是net.Listener接口的核心方法,它负责在TCP服务器中接受客户端的连接请求。下面详细解释其工作原理:

Accept() 的工作机制

Accept()是一个阻塞调用,它会一直等待直到有客户端连接到服务器。当有新的连接到达时,它会返回一个net.Conn对象,该对象代表与客户端的网络连接。

底层实现

在Linux系统上,Accept()的底层实现基于操作系统的accept()系统调用:

// 简化的底层流程
func (l *TCPListener) Accept() (net.Conn, error) {
    // 1. 调用系统accept()接收新连接
    fd, err := l.fd.accept()
    if err != nil {
        return nil, err
    }
    
    // 2. 创建新的TCP连接对象
    tc := newTCPConn(fd)
    
    // 3. 设置连接属性
    if l.lc.KeepAlive >= 0 {
        setKeepAlive(fd, true)
        setKeepAlivePeriod(fd, l.lc.KeepAlive)
    }
    
    return tc, nil
}

完整的工作示例

package main

import (
    "bufio"
    "fmt"
    "log"
    "net"
    "time"
)

func main() {
    // 创建TCP监听器
    listener, err := net.Listen("tcp", ":8080")
    if err != nil {
        log.Fatalf("监听失败: %v", err)
    }
    defer listener.Close()
    
    fmt.Printf("服务器启动,监听地址: %s\n", listener.Addr())
    
    for {
        // Accept() 阻塞等待客户端连接
        fmt.Println("等待客户端连接...")
        conn, err := listener.Accept()
        if err != nil {
            log.Printf("接受连接失败: %v", err)
            continue
        }
        
        // 获取客户端地址信息
        clientAddr := conn.RemoteAddr().String()
        fmt.Printf("新客户端连接: %s\n", clientAddr)
        
        // 为每个连接启动独立的goroutine处理
        go handleConnection(conn, clientAddr)
    }
}

func handleConnection(conn net.Conn, clientAddr string) {
    defer conn.Close()
    
    // 设置读取超时
    conn.SetReadDeadline(time.Now().Add(30 * time.Second))
    
    // 创建读取器
    reader := bufio.NewReader(conn)
    
    // 向客户端发送欢迎消息
    welcomeMsg := fmt.Sprintf("欢迎连接到服务器,你的地址: %s\n", clientAddr)
    conn.Write([]byte(welcomeMsg))
    
    for {
        // 读取客户端发送的数据
        message, err := reader.ReadString('\n')
        if err != nil {
            fmt.Printf("客户端 %s 断开连接: %v\n", clientAddr, err)
            return
        }
        
        // 处理接收到的消息
        fmt.Printf("来自 %s 的消息: %s", clientAddr, message)
        
        // 回显消息给客户端
        echoMsg := fmt.Sprintf("服务器收到: %s", message)
        conn.Write([]byte(echoMsg))
    }
}

并发连接处理测试

可以使用以下客户端代码测试多个并发连接:

// 客户端测试代码
package main

import (
    "fmt"
    "net"
    "sync"
    "time"
)

func clientWorker(id int, wg *sync.WaitGroup) {
    defer wg.Done()
    
    conn, err := net.Dial("tcp", "localhost:8080")
    if err != nil {
        fmt.Printf("客户端 %d 连接失败: %v\n", id, err)
        return
    }
    defer conn.Close()
    
    // 发送消息
    msg := fmt.Sprintf("Hello from client %d\n", id)
    conn.Write([]byte(msg))
    
    // 读取响应
    buffer := make([]byte, 1024)
    n, _ := conn.Read(buffer)
    fmt.Printf("客户端 %d 收到: %s", id, string(buffer[:n]))
    
    time.Sleep(1 * time.Second)
}

func main() {
    var wg sync.WaitGroup
    
    // 启动10个并发客户端
    for i := 1; i <= 10; i++ {
        wg.Add(1)
        go clientWorker(i, &wg)
    }
    
    wg.Wait()
}

Accept() 的关键特性

  1. 阻塞操作:当没有客户端连接时,Accept()会阻塞当前goroutine
  2. 连接队列:底层维护一个连接队列,处理多个同时到达的连接请求
  3. 错误处理:网络错误或监听器关闭时会返回错误
  4. 并发安全:可以在多个goroutine中安全调用,但通常只在一个goroutine中使用

当监听器调用Close()方法时,所有被阻塞的Accept()调用会立即返回错误,这是优雅关闭服务器的关键机制。

回到顶部