• golang socket编程


    TCP client

    Go语言中通过net包中的DialTCP函数来建立一个TCP连接,并返回一个TCPConn类型的对象,当连接建立时服务器端也创建一个同类型的对象,此时客户端和服务器端通过各自拥有的TCPConn对象来进行数据交换。一般而言,客户端通过TCPConn对象将请求信息发送到服务器端,读取服务器端响应的信息。服务器端读取并解析来自客户端的请求,并返回应答信息,这个连接只有当任一端关闭了连接之后才失效,不然这连接可以一直在使用。建立连接的函数定义如下:

    func DialTCP(network string, laddr, raddr *TCPAddr) (*TCPConn, error)
    • network参数是"tcp4"、"tcp6"、"tcp"中的任意一个,分别表示TCP(IPv4-only)、TCP(IPv6-only)或者TCP(IPv4,IPv6的任意一个)
    • laddr表示本机地址,一般设置为nil
    • raddr表示远程的服务地址

    客户端代码:

    package main
    
    import (
        "fmt"
        "io/ioutil"
        "net"
        "os"
    )
    
    func main() {
        if len(os.Args) != 2 {
            fmt.Fprintf(os.Stderr, "Usage: %s host:port ", os.Args[0])
            os.Exit(1)
        }
        service := os.Args[1]
        tcpAddr, err := net.ResolveTCPAddr("tcp4", service)
        checkError(err)
        conn, err := net.DialTCP("tcp", nil, tcpAddr)
        checkError(err)
        _, err = conn.Write([]byte("HEAD / HTTP/1.0
    
    "))
        checkError(err)
        // result, err := ioutil.ReadAll(conn)
        result := make([]byte, 256)
        _, err = conn.Read(result)
        checkError(err)
        fmt.Println(string(result))
        os.Exit(0)
    }
    func checkError(err error) {
        if err != nil {
            fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
            os.Exit(1)
        }
    }

    通过上面的代码我们可以看出:首先程序将用户的输入作为参数service传入net.ResolveTCPAddr获取一个tcpAddr,然后把tcpAddr传入DialTCP后创建了一个TCP连接conn,通过conn来发送请求信息,最后通过ioutil.ReadAllconn中读取全部的文本,也就是服务端响应反馈的信息。

    TCP server

    上面我们编写了一个TCP的客户端程序,也可以通过net包来创建一个服务器端程序,在服务器端我们需要绑定服务到指定的非激活端口,并监听此端口,当有客户端请求到达的时候可以接收到来自客户端连接的请求。net包中有相应功能的函数,函数定义如下:

    func ListenTCP(network string, laddr *TCPAddr) (*TCPListener, error)
    func (l *TCPListener) Accept() (Conn, error)

    package main
    
    import (
        "fmt"
        "net"
        "os"
        "time"
    )
    
    func main() {
        service := ":1200"
        tcpAddr, err := net.ResolveTCPAddr("tcp4", service)
        checkError(err)
        listener, err := net.ListenTCP("tcp", tcpAddr)
        checkError(err)
        for {
            conn, err := listener.Accept()
            if err != nil {
                continue
            }
            go handleClient(conn)
        }
    }
    
    func handleClient(conn net.Conn) {
        defer conn.Close()
        daytime := time.Now().String()
        conn.Write([]byte(daytime)) // don't care about return value
        // we're finished with this client
    }
    func checkError(err error) {
        if err != nil {
            fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
            os.Exit(1)
        }
    }

    通过把业务处理分离到函数handleClient,我们就可以进一步地实现多并发执行了。看上去是不是很帅,增加go关键词就实现了服务端的多并发,从这个小例子也可以看出goroutine的强大之处。

    控制TCP连接

    TCP有很多连接控制函数,我们平常用到比较多的有如下几个函数:

    func DialTimeout(net, addr string, timeout time.Duration) (Conn, error)

    设置建立连接的超时时间,客户端和服务器端都适用,当超过设置时间时,连接自动关闭。

    func (c *TCPConn) SetReadDeadline(t time.Time) error
    func (c *TCPConn) SetWriteDeadline(t time.Time) error

    用来设置写入/读取一个连接的超时时间。当超过设置时间时,连接自动关闭。

    func (c *TCPConn) SetKeepAlive(keepalive bool) os.Error

    设置keepAlive属性。操作系统层在tcp上没有数据和ACK的时候,会间隔性的发送keepalive包,操作系统可以通过该包来判断一个tcp连接是否已经断开,在windows上默认2个小时没有收到数据和keepalive包的时候认为tcp连接已经断开,这个功能和我们通常在应用层加的心跳包的功能类似。

    UDP Socket

    Go语言包中处理UDP Socket和TCP Socket不同的地方就是在服务器端处理多个客户端请求数据包的方式不同,UDP缺少了对客户端连接请求的Accept函数。其他基本几乎一模一样,只有TCP换成了UDP而已。UDP的几个主要函数如下所示:

    func ResolveUDPAddr(net, addr string) (*UDPAddr, os.Error)
    func DialUDP(net string, laddr, raddr *UDPAddr) (c *UDPConn, err os.Error)
    func ListenUDP(net string, laddr *UDPAddr) (c *UDPConn, err os.Error)
    func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err os.Error)
    func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (n int, err os.Error)

    udp客户端
    一个UDP的客户端代码如下所示,我们可以看到不同的就是TCP换成了UDP而已:
    package main
    
    import (
        "fmt"
        "net"
        "os"
    )
    
    func main() {
        if len(os.Args) != 2 {
            fmt.Fprintf(os.Stderr, "Usage: %s host:port", os.Args[0])
            os.Exit(1)
        }
        service := os.Args[1]
        udpAddr, err := net.ResolveUDPAddr("udp4", service)
        checkError(err)
        conn, err := net.DialUDP("udp", nil, udpAddr)
        checkError(err)
        _, err = conn.Write([]byte("anything"))
        checkError(err)
        var buf [512]byte
        n, err := conn.Read(buf[0:])
        checkError(err)
        fmt.Println(string(buf[0:n]))
        os.Exit(0)
    }
    func checkError(err error) {
        if err != nil {
            fmt.Fprintf(os.Stderr, "Fatal error %s", err.Error())
            os.Exit(1)
        }

    UDP服务端:

    package main
    
    import (
        "fmt"
        "net"
        "os"
        "time"
    )
    
    func main() {
        service := ":1200"
        udpAddr, err := net.ResolveUDPAddr("udp4", service)
        checkError(err)
        conn, err := net.ListenUDP("udp", udpAddr)
        checkError(err)
        for {
            handleClient(conn)
        }
    }
    func handleClient(conn *net.UDPConn) {
        var buf [512]byte
        _, addr, err := conn.ReadFromUDP(buf[0:])
        if err != nil {
            return
        }
        daytime := time.Now().String()
        conn.WriteToUDP([]byte(daytime), addr)
    }
    func checkError(err error) {
        if err != nil {
            fmt.Fprintf(os.Stderr, "Fatal error %s", err.Error())
            os.Exit(1)
        }
    }

    通过对TCP和UDP Socket编程的描述和实现,可见Go已经完备地支持了Socket编程,而且使用起来相当的方便,Go提供了很多函数,通过这些函数可以很容易就编写出高性能的Socket应用。

  • 相关阅读:
    动态生成表格 (ng-zorro)
    单例服务
    模板变量 #
    HTML 5 系列
    关于tcp nagle算法
    netty 解包头包体的一点认知
    vargent Authentication failure.记录
    关于YII2.0配置的一点问题
    关于mysql b-tree索引的一点认知
    记vagrant nginx sendfile问题
  • 原文地址:https://www.cnblogs.com/peteremperor/p/13921202.html
Copyright © 2020-2023  润新知