Go語言TCP/UDP編程

一、TCP/UDP協議

TCP (Transmission Control Protocol)和UDP(User Datagram Protocol)協議屬於傳輸層協議。其中TCP提供IP環境下的數據可靠傳輸,它提供的服務包括數據流傳送、可靠性、有效流控、全雙工操作和多路複用。通過面向連接、端到端和可靠的數據包發送。通俗說,它是事先爲所發送的數據開闢出連接好的通道,然後再進行數據發送;而UDP則不爲IP提供可靠性、 流控或差錯恢復功能。一般來說,TCP對應的是可靠性要求高的應用,而UDP對應的則是可靠性要求低、傳輸流量大,傳輸速度快的應用

TCP支持的應用協議主要 有:Telnet、FTP、SMTP等;UDP支持的應用層協議主要有:NFS(網絡文件系統)、SNMP(簡單網絡管理協議)、DNS(主域名稱系 統)、TFTP(通用文件傳輸協議)等。

TCP/IP協議與低層的數據鏈路層和物理層無關,這也是TCP/IP的重要特點。

二、TCP 和 UDP 區別

  1. 面向連接vs無連接
    TCP 有三次握手的連接過程,UDP 適合消息的多播發布,從單個點向多個點傳輸消息。
  2. 可靠性
    TCP 利用握手, 確認(ACK) 和重傳的機制,提供了可靠性保證,而 UDP 可能丟失,不知道到底有沒有接收。
  3. 有序性
    TCP 利用序列號保證了消息包的的順序交付,到達可能無序,但 TCP 會排序。
  4. 速度
    TCP 速度比較慢,因爲要創建連接,保證消息的可靠性和有序性等,需要做額外的很多事情,UDP 更適合對速度比較敏感的應用,比如在線視頻媒體,電視廣播,多人在線遊戲等。
  5. 頭字節大小
    TCP 20字節,UDP 8字節

三、 Go語言TCP客戶端與服務端實現

Go語言實現的tcp服務端通信模型

TCPServer

package main

import (
	"fmt"
	"io"
	"net"
	"os"
)

func main(){
	//服務器端一般不定位具體的客戶端套接字
	tcpaddr, err := net.ResolveTCPAddr("tcp",":8848")
	if err != nil {
		fmt.Fprintf(os.Stderr,"Server ResolveTCPAddr fail,err :%v", err)
		return
	}

	serverfd, err := net.ListenTCP("tcp",tcpaddr)
	if err != nil {
		fmt.Fprintf(os.Stderr,"Server ListenTCP fail,err :%v", err)
		return
	}

	defer serverfd.Close()

	for{
		conn, err := serverfd.AcceptTCP()
		if err != nil {
			fmt.Fprintf(os.Stderr,"Server Accept fail,err :%v",err)
			return
		}

		go handleClient(conn)
	}
}

func handleClient(conn net.Conn){
	defer conn.Close()

	fmt.Printf("Welcome %v connect",conn.RemoteAddr().String())

	for{
		buf := make([]byte,1024)
		_, err := conn.Read(buf)
		if err != nil{
			fmt.Fprintf(os.Stderr,"Server read fail,err: %v",err)
			break
		}

		fmt.Printf("Read Buf:%s",string(buf))

		_, err = conn.Write(buf)
		if err != nil {
			fmt.Fprintf(os.Stderr,"Server write fail")
			break
		}
	}
}

Go server端程序模型爲:

//go-tcpsock/server.go
func HandleConn(conn net.Conn) {
    defer conn.Close()

 for {
        // read from the connection
        // ... ...
        // write to the connection
        //... ...
    }
}

func main() {
    listen, err := net.Listen("tcp", ":8888")
    if err != nil {
        fmt.Println("listen error: ", err)
        return
    }

    for {
        conn, err := listen.Accept()
        if err != nil {
            fmt.Println("accept error: ", err)
            break
        }

        // start a new goroutine to handle the new connection
        go HandleConn(conn)
    }
}

Go語言實現的TCP客戶端通信模型

TCPClient

package main

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

func main(){
	tcpaddr, err := net.ResolveTCPAddr("tcp","127.0.0.1:8848")
	if err != nil {
		fmt.Fprintf(os.Stderr,"Client ResolveTCPAddr fail, err: %v",err)
		return
	}

	conn, err := net.DialTCP("tcp",nil,tcpaddr)
	if err != nil {
		fmt.Fprintf(os.Stderr,"Client DialTCP fail, err: %v",err)
		return
	}

	defer conn.Close()

	reader := bufio.NewReader(os.Stdin)
	for{
		buf, err := reader.ReadString('\n')
		if err != nil{
			fmt.Fprintf(os.Stderr,"ReadString fail,err :%v",err)
			return
		}

		_, err = conn.Write([]byte(buf))
		if err != nil {
			fmt.Fprintf(os.Stderr,"Write fail,err: %v",err)
			return
		}

		//readBuf 記住需要分配內存
		readBuf := make([]byte,1024)
		_, err = conn.Read(readBuf)
		if err != nil {
			fmt.Fprintf(os.Stderr,"Read fail,err :%v",err)
			return
		}

		fmt.Printf("read buf:%s\n",string(readBuf))

	}
}

四、Go語言UDP服務端與客戶端實現

Go語言udp服務端實現

package main

import (
	"fmt"
	"net"
	"os"
)

func main() {
	addr, err := net.ResolveUDPAddr("udp", ":8848")
	if err != nil {
		fmt.Fprintf(os.Stderr, "udp server ResolveUDPAddr fail,err :%v", err)
		return
	}

	conn, err := net.ListenUDP("udp", addr)
	if err != nil {
		fmt.Fprintf(os.Stderr, "udp server ListenUDP fail,err :%v", err)
		return
	}
	defer conn.Close()

	for {
		buf := make([]byte, 1024)
		_, clientAddr, err := conn.ReadFromUDP(buf)
		if err != nil {
			fmt.Fprintf(os.Stderr, "udp server ReadFromUDP fail,err: %v", err)
			continue
		}

		fmt.Printf("Recv Msg:%s\n", string(buf))

		go func() {
			_, err = conn.WriteToUDP(buf, clientAddr)

			if err != nil {
				fmt.Printf("udp server WriteToUDP fail,err: %v\n", err)
				return
			}
		}()
	}
}

Go語言udp客戶端實現

package main

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

func main(){
	if len(os.Args) != 2 {
		fmt.Fprintf(os.Stderr,"Usage:%v host:port",os.Args[0])
		return
	}

	addr, err := net.ResolveUDPAddr("udp",os.Args[1])
	if err != nil {
		fmt.Fprintf(os.Stderr,"udp client ResolveUDPAddr fail,err :%v",err)
		return
	}

	conn, err := net.DialUDP("udp",nil,addr)
	if err != nil {
		fmt.Fprintf(os.Stderr,"udp client DialUDP fail,err :%v",err)
		return
	}

	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		buf := scanner.Text()

		fmt.Printf("input buf:%s\n",buf)

		_, err := conn.Write([]byte(buf))
		if err != nil {
			fmt.Fprintf(os.Stderr,"udp client Write fail,err :%v",err)
			return
		}

		readBuf := make([]byte,1024)
		_, err = conn.Read(readBuf)
		if err != nil{
			fmt.Fprintf(os.Stderr,"udp client read fail,err :%v",err)
			return
		}

		fmt.Printf("Read buf:%s\n",readBuf)
	}
}

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