udp編程的那些事與golang udp的實踐

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

tcp/ip大協議中,tcp編程大家應該比較熟,應用的情境也很多,但是udp在現實中,應用也不少,而在大部分博文中,都很少對udp的編程進行研究,最近研究了一下udp編程,正好做個記錄。
sheepbao 2017.06.15

tcp Vs udp

tcp和udp都是著名的傳輸協議,他們都是基於ip協議,都在OSI模型中傳輸層。tcp我們都很清楚,它提供了可靠的資料轉送,而udp我們也知道,它不提供資料轉送的可靠性,只是儘力傳輸。他們的特性決定了它們很大的不同,tcp提供可靠性傳輸,有三向交握,4次分手,相當於具有邏輯上的串連,可以知道這個tcp串連的狀態,所以我們都說tcp是連線導向的socket,而udp沒有握手,沒有分手,也不存在邏輯上的串連,所以我們也都說udp是非連線導向的socket。
我們都畏懼不知道狀態的東西,所以即使tcp的協議比udp複雜很多,但對於系統應用程式層的編程來說,tcp編程其實比udp編程容易。而udp相對比較靈活,所以對於udp編程反而沒那麼容易,但其實掌握後udp編程也並不難。

udp協議

udp的首部

        2               2       (byte)+---+---+---+---+---+---+---+---+      -|    src port   |    dst port   |      |+---+---+---+---+---+---+---+---+      8(bytes)|     length    |   check sum   |      |+---+---+---+---+---+---+---+---+      -|                               |+              data             +      |                               |+---+---+---+---+---+---+---+---+  

udp的首部真的很簡單,頭2個位元組表示的是原連接埠,後2個位元組表示的是目的連接埠,連接埠是系統層區分進程的標識。接著是udp長度,最後就是校正和,這個其實很重要,現在的系統都是預設開啟udp校正和的,所以我們才能確保udp訊息傳輸的完整性。如果這個校正和關閉了,那會讓我們絕對會很憂傷,因為udp不僅不能保證資料一定到達,還不能保證即使資料到了,這個資料是否是正確的。比如:我在發送端發送了“hello”,而接收端卻接收到了“hell”。如果真的是這樣,我們就必須自己去校正資料的正確性。還好udp預設開發了校正,我們可以保證udp的資料完整性。

udp資料的封裝

                                    +---------+                                    | 應用資料 |                                    +---------+                                                 |         |                                    v         v                          +---------+---------+                          | udp首部  | 應用資料 |                          +---------+---------+                          |                   |                          v     UDP資料報      v                +---------+---------+---------+                | ip首部   | udp首部  | 應用資料 |                +---------+---------+---------+                |                             |                v           IP資料報           v      +---------+---------+---------+---------+---------+      |乙太網路首部 | ip首部   | udp首部 | 應用資料 |乙太網路尾部 |      +---------+---------+---------+---------+---------+      |   14        20         8                   4    |      |                  -> 乙太網路幀 <-                  |

資料的封裝和tcp是一樣,應用程式層的資料加上udp首部,構成udp資料報,再加上ip首部構成ip資料報,最後加上乙太網路首部和尾部構成乙太網路幀,經過網卡發送出去。

Golang udp實踐

實踐出真知,編程就需要多實踐,才能體會其中的奧妙。

echo用戶端和服務端

echo服務,實現資料包的回顯,這是很多人網路編程起點,因為這個服務足夠簡單,但又把網路的資料流都過了一遍,這裡也用go udp實現一個echo服務。
實現用戶端發送一個“hello”,服務端接收訊息並原封不動的返回給客戶度。

server.go

package mainimport ("flag""fmt""log""net")var addr = flag.String("addr", ":10000", "udp server bing address")func init() {log.SetFlags(log.LstdFlags | log.Lshortfile)flag.Parse()}func main() {//Resolving addressudpAddr, err := net.ResolveUDPAddr("udp", *addr)if err != nil {log.Fatalln("Error: ", err)}// Build listining connectionsconn, err := net.ListenUDP("udp", udpAddr)if err != nil {log.Fatalln("Error: ", err)}defer conn.Close()// Interacting with one client at a timerecvBuff := make([]byte, 1024)for {log.Println("Ready to receive packets!")// Receiving a messagern, rmAddr, err := conn.ReadFromUDP(recvBuff)if err != nil {log.Println("Error:", err)return}fmt.Printf("<<< Packet received from: %s, data: %s\n", rmAddr.String(), string(recvBuff[:rn]))// Sending the same message back to current client_, err = conn.WriteToUDP(recvBuff[:rn], rmAddr)if err != nil {log.Println("Error:", err)return}fmt.Println(">>> Sent packet to: ", rmAddr.String())}}

client1.go

package mainimport ("flag""fmt""log""net")var raddr = flag.String("raddr", "127.0.0.1:10000", "remote server address")func init() {log.SetFlags(log.LstdFlags | log.Lshortfile)flag.Parse()}func main() {// Resolving AddressremoteAddr, err := net.ResolveUDPAddr("udp", *raddr)if err != nil {log.Fatalln("Error: ", err)}// Make a connectiontmpAddr := &net.UDPAddr{IP:   net.ParseIP("127.0.0.1"),Port: 0,}conn, err := net.DialUDP("udp", tmpAddr, remoteAddr)// Exit if some error occuredif err != nil {log.Fatalln("Error: ", err)}defer conn.Close()// write a message to server_, err = conn.Write([]byte("hello"))if err != nil {log.Println(err)} else {fmt.Println(">>> Packet sent to: ", *raddr)}// Receive response from serverbuf := make([]byte, 1024)rn, rmAddr, err := conn.ReadFromUDP(buf)if err != nil {log.Println(err)} else {fmt.Printf("<<<  %d bytes received from: %v, data: %s\n", rn, rmAddr, string(buf[:rn]))}}

client2.go

package mainimport ("flag""fmt""log""net")var (laddr = flag.String("laddr", "127.0.0.1:9000", "local server address")raddr = flag.String("raddr", "127.0.0.1:10000", "remote server address"))func init() {log.SetFlags(log.LstdFlags | log.Lshortfile)flag.Parse()}func main() {// Resolving AddresslocalAddr, err := net.ResolveUDPAddr("udp", *laddr)if err != nil {log.Fatalln("Error: ", err)}remoteAddr, err := net.ResolveUDPAddr("udp", *raddr)if err != nil {log.Fatalln("Error: ", err)}// Build listening connectionsconn, err := net.ListenUDP("udp", localAddr)// Exit if some error occuredif err != nil {log.Fatalln("Error: ", err)}defer conn.Close()// write a message to server_, err = conn.WriteToUDP([]byte("hello"), remoteAddr)if err != nil {log.Println(err)} else {fmt.Println(">>> Packet sent to: ", *raddr)}// Receive response from serverbuf := make([]byte, 1024)rn, remAddr, err := conn.ReadFromUDP(buf)if err != nil {log.Println(err)} else {fmt.Printf("<<<  %d bytes received from: %v, data: %s\n", rn, remAddr, string(buf[:rn]))}}

這裡實現echo的服務端和用戶端,和tcp的差不多,但是有一些小細節需要注意。
對於server端,先net.ListenUDP建立udp一個監聽,返回一個udp串連,這裡需要注意udp不像tcp,建立tcp監聽後返回的是一個Listener,然後阻塞等待接收一個新的串連,這樣區別是因為udp一個非連線導向的協議,它沒有會話管理。同時也因為udp是非連線導向的協議,當接收到訊息後,想把訊息返回給當前的用戶端時,是不能像tcp一樣,直接往conn裡寫的,而是需要指定遠端地址。
對於client端,類似tcp先Dial,返回一個串連,對於發送訊息用Write,接收訊息用Read,當然udp也可以用ReadFromUDP,這樣可以知道從哪得到的訊息。但其實client也可以用另一種方式寫,如client2.go程式,先建立一個監聽,返回一個串連,用這個串連發送訊息給服務端和從伺服器接收訊息,這種方式和tcp倒是有很大的不同。

參考

golang doc

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.