Go 語言使用 net 包實現 Socket 網絡編程

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

此文章首發於 Debug客棧 |https://www.debuginn.cn

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)
}

參考文章

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章