Go/Socket 编程

Socket 是一种操作系统提供的进程间通信机制. 在操作系统中, 通常会为应用程序提供一组应用程序接口, 称为套接字接口(Socket API). 注意的是, Socket API 本身不负责通信, 它仅提供基础函数供应用层调用, 底层通信一般由 TCP, Unix 或 UDP 实现.

TCP

以下是一个简单的 TCP 服务与其配套客户端实现.

// server.go
package main

import (
    "log"
    "net"
)

func main() {
    ln, err := net.Listen("tcp", ":3000")
    if err != nil {
        log.Fatalln(err)
    }
    defer ln.Close()
    for {
        conn, err := ln.Accept()
        if err != nil {
            log.Println(err)
            continue
        }

        go func(conn net.Conn) {
            defer conn.Close()
            var (
                n   int
                b   = make([]byte, 1024)
                err error
            )
            n, err = conn.Read(b)
            if err != nil {
                log.Println(err)
                return
            }
            log.Println(string(b[:n]))
            n, err = conn.Write([]byte("pong"))
            if err != nil {
                log.Println(err)
                return
            }
        }(conn)
    }
}
// client.go
package main

import (
    "log"
    "net"
)

func main() {
    var (
        conn net.Conn
        b    = make([]byte, 1024)
        n    int
        err  error
    )

    conn, err = net.Dial("tcp", "127.0.0.1:3000")
    if err != nil {
        log.Fatalln(err)
    }
    defer conn.Close()

    _, err = conn.Write([]byte("ping"))
    if err != nil {
        log.Fatalln(err)
    }
    n, err = conn.Read(b)
    if err != nil {
        log.Fatalln(err)
    }
    log.Println(string(b[:n]))
}

UNIX

Unix Socket 是 POSIX 操作系统里的一种组件. 它通过文件系统来实现 Socket 通信. 常见的 Unix Socket 文件有 mysql.sock, supervisor.sock 等, 它们均位于 /var/run/ 目录下.

Go 中使用 Unix Socket 与 TCP Socket 的方法完全相同, 唯一区别是在 Listen 与 Dial 时, 参数 network 为 "unix", address 为文件路径, 如 "/var/run/accu.sock"

UDP

// server
package main

import (
    "log"
    "net"
)

func main() {
    var (
        conn *net.UDPConn
        addr *net.UDPAddr
        b    = make([]byte, 1024)
        n    int
        err  error
    )

    conn, err = net.ListenUDP("udp", &net.UDPAddr{IP: net.ParseIP("0.0.0.0"), Port: 3000})
    if err != nil {
        log.Fatalln(err)
    }
    defer conn.Close()

    for {
        n, addr, err = conn.ReadFromUDP(b)
        if err != nil {
            log.Println(err)
            continue
        }
        log.Println(addr, string(b[:n]))

        _, err = conn.WriteToUDP([]byte("pong"), addr)
        if err != nil {
            log.Println(err)
            continue
        }
    }
}
//client
package main

import (
    "log"
    "net"
)

func main() {
    var (
        conn net.Conn
        b    = make([]byte, 1024)
        n    int
        err  error
    )

    conn, err = net.Dial("udp", "127.0.0.1:3000")
    if err != nil {
        log.Fatalln(err)
    }
    defer conn.Close()

    _, err = conn.Write([]byte("ping"))
    if err != nil {
        log.Fatalln(err)
    }
    n, err = conn.Read(b)
    if err != nil {
        log.Fatalln(err)
    }
    log.Println(string(b[:n]))
}