Go 語言使用.NET 包實現 Socket 網路程式設計

Meng小羽發表於2020-06-10

友情提示:此篇文章大約需要閱讀 10分鐘12秒,不足之處請多指教,感謝你的閱讀。? 訂閱本站

TCP/IP

TCP/IP 傳輸協議,即傳輸控制/網路協議,也叫作網路通訊協議。它是在網路的使用中的最基本的通訊協議。TCP/IP 傳輸協議對網際網路中各部分進行通訊的標準和方法進行了規定。並且,TCP/IP 傳輸協議是保證網路資料資訊及時、完整傳輸的兩個重要的協議。TCP/IP 傳輸協議是嚴格來說是一個四層的體系結構,應用層、傳輸層、網路層和資料鏈路層都包含其中。

TCP/IP 協議簇常見通訊協議

  • 應用層:TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet 等等
  • 傳輸層:TCP,UDP
  • 網路層:IP,ICMP,OSPF,EIGRP,IGMP
  • 資料鏈路層:SLIP,CSLIP,PPP,MTU

Socket

兩個程式如果需要進行通訊最基本的一個前提能能夠唯一的標示一個程式,在本地程式通訊中我們可以使用 PID 來唯一標示一個程式,但 PID 只在本地唯一,網路中的兩個程式 PID 衝突機率很大,這時候我們需要另闢它徑了,我們知道 IP 層的 ip 地址可以唯一標示主機,而 TCP 層協議和埠號可以唯一標示主機的一個程式,這樣我們可以利用 ip 地址+協議+埠號唯一標示網路中的一個程式。

能夠唯一標示網路中的程式後,它們就可以利用 socket 進行通訊了,什麼是socket 呢?我們經常把 socket 翻譯為套接字,socket 是在應用層和傳輸層之間的一個抽象層,它把 TCP/IP 層複雜的操作抽象為幾個簡單的介面供應用層呼叫已實現程式在網路中通訊。

socket是一種”開啟—讀/寫—關閉”模式的實現,伺服器和客戶端各自維護一個”檔案”,在建立連線開啟後,可以向自己檔案寫入內容供對方讀取或者讀取對方內容,通訊結束時關閉檔案。

Socket 是實現“開啟–讀/寫–關閉”這樣的模式,以使用 TCP 協議通訊的 socket 為例。如下圖所示:

TCP 實現

一個 TCP 客戶端進行 TCP 通訊的流程如下:

  1. 建立與服務端的連結
  2. 進行資料收發
  3. 關閉連結

server 端

package main

import (
    "bufio"
    "fmt"
    "net"
)

func process(conn net.Conn) {
    // 處理完關閉連線
    defer conn.Close()

    // 針對當前連線做傳送和接受操作
    for {
        reader := bufio.NewReader(conn)
        var buf [128]byte
        n, err := reader.Read(buf[:])
        if err != nil {
            fmt.Printf("read from conn failed, err:%v\n", err)
            break
        }

        recv := string(buf[:n])
        fmt.Printf("收到的資料:%v\n", recv)

        // 將接受到的資料返回給客戶端
        _, err = conn.Write([]byte("ok"))
        if err != nil {
            fmt.Printf("write from conn failed, err:%v\n", err)
            break
        }
    }
}

func main() {
    // 建立 tcp 服務
    listen, err := net.Listen("tcp", "127.0.0.1:9090")
    if err != nil {
        fmt.Printf("listen failed, err:%v\n", err)
        return
    }

    for {
        // 等待客戶端建立連線
        conn, err := listen.Accept()
        if err != nil {
            fmt.Printf("accept failed, err:%v\n", err)
            continue
        }
        // 啟動一個單獨的 goroutine 去處理連線
        go process(conn)
    }
}

client 端

package main

import (
    "bufio"
    "fmt"
    "net"
    "os"
    "strings"
)

func main() {
    // 1、與服務端建立連線
    conn, err := net.Dial("tcp", "127.0.0.1:9090")
    if err != nil {
        fmt.Printf("conn server failed, err:%v\n", err)
        return
    }
    // 2、使用 conn 連線進行資料的傳送和接收
    input := bufio.NewReader(os.Stdin)
    for {
        s, _ := input.ReadString('\n')
        s = strings.TrimSpace(s)
        if strings.ToUpper(s) == "Q" {
            return
        }

        _, err = conn.Write([]byte(s))
        if err != nil {
            fmt.Printf("send failed, err:%v\n", err)
            return
        }
        // 從服務端接收回復訊息
        var buf [1024]byte
        n, err := conn.Read(buf[:])
        if err != nil {
            fmt.Printf("read failed:%v\n", err)
            return
        }
        fmt.Printf("收到服務端回覆:%v\n", string(buf[:n]))
    }
}

UDP 實現

UDP 協議(User Datagram Protocol)中文名稱是使用者資料包協議,是OSI(Open System Interconnection,開放式系統互聯)參考模型中一種無連線的傳輸層協議,不需要建立連線就能直接進行資料傳送和接收,屬於不可靠的、沒有時序的通訊,但是UDP協議的實時性比較好,通常用於影片直播相關領域。

server 端

package main

import (
    "fmt"
    "net"
)

func main() {
    // 建立 utp 伺服器
    listen, err := net.ListenUDP("udp", &net.UDPAddr{
        IP:   net.IPv4(0, 0, 0, 0),
        Port: 9090,
    })
    if err != nil {
        fmt.Printf("listen failed error:%v\n", err)
        return
    }
    defer listen.Close() // 使用完關閉服務

    for {
        // 接收資料
        var data [1024]byte
        n, addr, err := listen.ReadFromUDP(data[:])
        if err != nil {
            fmt.Printf("read data error:%v\n", err)
            return
        }
        fmt.Printf("addr:%v\t count:%v\t data:%v\n", addr, n, string(data[:n]))
        // 傳送資料
        _, err = listen.WriteToUDP(data[:n], addr)
        if err != nil {
            fmt.Printf("send data error:%v\n", err)
            return
        }
    }
}

client 端

package main

import (
    "fmt"
    "net"
)

func main() {
    // 建立服務
    listen, err := net.DialUDP("udp", nil, &net.UDPAddr{
        IP:   net.IPv4(0, 0, 0, 0),
        Port: 9090,
    })
    if err != nil {
        fmt.Printf("listen udp server error:%v\n", err)
    }
    defer listen.Close()

    // 傳送資料
    sendData := []byte("Hello server")
    _, err = listen.Write(sendData) // 傳送資料
    if err != nil {
        fmt.Println("傳送資料失敗,err:", err)
        return
    }

    // 接收資料
    data := make([]byte, 4096)
    n, remoteAddr, err := listen.ReadFromUDP(data) // 接收資料
    if err != nil {
        fmt.Println("接收資料失敗,err:", err)
        return
    }
    fmt.Printf("recv:%v addr:%v count:%v\n", string(data[:n]), remoteAddr, n)
}

參考文章

本作品採用《CC 協議》,轉載必須註明作者和本文連結
Meng小羽

相關文章