Golang 網絡編程:像 C 語言一樣操作 Socket

在 C 語言中,進行網絡編程通常使 socket()、bind()、listen()、accept() 等系統調用,而在 Golang 中,我們既可以使用 net 庫提供的高級封裝,也可以直接使用 syscall 庫進行底層操作。本篇文章將簡單的介紹 Golang 如何像 C 語言一樣進行網絡編程。

使用 net 庫進行 TCP 編程(推薦方式)

Golang 的 net 包對 socket 進行了封裝,使得網絡編程更簡單、更易用。

TCP 服務器例子 net 版

package main
import (
	"fmt"
	"net"
)
func main() {
	listener, err := net.Listen("tcp", ":8080") // 監聽 8080 端口
	if err != nil {
		panic(err)
	}
	defer listener.Close()
	fmt.Println("Server listening on port 8080...")
	for {
		conn, err := listener.Accept() // 接受客戶端連接
		if err != nil {
			fmt.Println("Accept error:", err)
			continue
		}
		go handleConnection(conn) // 處理連接
	}
}
func handleConnection(conn net.Conn) {
	defer conn.Close()
	buffer := make([]byte, 1024)
	n, err := conn.Read(buffer) // 讀取數據
	if err != nil {
		fmt.Println("Read error:", err)
		return
	}
	fmt.Println("Received:", string(buffer[:n]))
	conn.Write([]byte("Hello from server\n")) // 發送響應
}

TCP 客戶端 net 版

package main
import (
	"fmt"
	"net"
)
func main() {
	conn, err := net.Dial("tcp", "127.0.0.1:8080") // 連接服務器
	if err != nil {
		panic(err)
	}
	defer conn.Close()
	conn.Write([]byte("Hello, Server!\n")) // 發送數據
	buffer := make([]byte, 1024)
	n, _ := conn.Read(buffer) // 讀取服務器響應
	fmt.Println("Server response:", string(buffer[:n]))
}

上述給出的就是使用 golang 標準庫中 net 包編寫的,簡單的 tcp server 代碼例子,這比傳統的 socket 代碼量少了很多,卻能達到預期的效果。但是在實際的工作中,往往有些需求我們不能直接使用 net 包進行操作的,例如一些特殊情況下我們需要自定義的數據包包頭,加密等等操作。

使用 syscall 實現 TCP 服務器

TCP 服務器例子 syscall 版

package main
import (
    "fmt"
    "syscall"
)
func main() {
    // 創建 socket
    fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, 0)
    if err != nil {
        panic(err)
    }
    defer syscall.Close(fd)
    // 設置 SO_REUSEADDR,避免端口占用問題
    err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
    if err != nil {
        panic(err)
    }
    // 綁定地址和端口
    addr := syscall.SockaddrInet4{Port: 9527, Addr: [4]byte{0, 0, 0, 0}}
    if err := syscall.Bind(fd, &addr); err != nil {
        panic(err)
    }
    // 監聽連接
    if err := syscall.Listen(fd, syscall.SOMAXCONN); err != nil {
        panic(err)
    }
    fmt.Println("Server listening on port 9527...")
    for {
        // 接受客戶端連接
        clientFD, _, err := syscall.Accept(fd)
        if err != nil {
            fmt.Println("Accept error:", err)
            continue
        }
        go handleClient(clientFD)
    }
}
// 處理客戶端連接
func handleClient(fd int) {
    defer syscall.Close(fd)
    buffer := make([]byte, 1024)
    n, err := syscall.Read(fd, buffer) // 讀取數據
    if err != nil {
        fmt.Println("Read error:", err)
        return
    }
    fmt.Println("Received:", string(buffer[:n]))
    // 發送數據
    syscall.Write(fd, []byte("Hello from server\n"))
}

代碼解析

    • syscall.Socket(AF_INET, SOCK_STREAM, 0): 創建 TCP 套接字(IPv4)。

    • syscall.Bind(fd, &addr): 綁定端口 9527。

    • syscall.Listen(fd, syscall.SOMAXCONN): 監聽連接。

    • syscall.Accept(fd): 接受連接並返回新的 clientFD。

    • syscall.Read(fd, buffer): 讀取客戶端數據。

    • syscall.Write(fd, response): 發送數據。

TCP 客戶端 syscall 版

package main
import (
    "fmt"
    "syscall"
)
func main() {
    // 創建 socket
    fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, 0)
    if err != nil {
        panic(err)
    }
    defer syscall.Close(fd)
    // 連接服務器
    serverAddr := syscall.SockaddrInet4{Port: 9527, Addr: [4]byte{127, 0, 0, 1}}
    if err := syscall.Connect(fd, &serverAddr); err != nil {
        panic(err)
    }
    // 發送數據
    message := "Hello, Server!"
    syscall.Write(fd, []byte(message))
    // 讀取響應
    buffer := make([]byte, 1024)
    n, err := syscall.Read(fd, buffer)
    if err != nil {
        fmt.Println("Read error:", err)
        return
    }
    fmt.Println("Server response:", string(buffer[:n]))
}

代碼解析

    • syscall.Socket(AF_INET, SOCK_STREAM, 0): 創建 TCP 套接字。

    • syscall.Connect(fd, &serverAddr): 連接到 127.0.0.1:9527 服務器。

    • syscall.Write(fd, message): 發送數據。

    • syscall.Read(fd, buffer): 讀取服務器響應。

syscall 方式 vs net 庫

seA1dq

如果只是進行一般的 TCP 網絡編程,推薦使用 net 庫。如果你想更深入理解底層 socket 機制,或者進行 epoll、raw socket 等底層優化,則可以使用 syscall。

總結

本文介紹了 Golang 進行網絡編程的兩種方式:

✅ net 庫提供了簡單易用的 API,適合大多數應用場景。

✅ syscall 允許 Golang 直接調用 Linux/Unix 系統 API 進行底層網絡編程。

✅ syscall.Socket()、syscall.Bind()、syscall.Listen() 等函數的用法與 C 語言幾乎一致。

✅ syscall 適用於高性能和自定義網絡編程,但比 net 庫更復雜。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/ig8R-inbTB3M0Cm_m-ypCQ?poc_token=HNJVzmejNmlx9wXkPdX1WrPkq73iPBh4B8JoFa2J