udp程式設計的那些事與golang udp的實踐
udp程式設計的那些事與golang udp的實踐
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 main
import (
"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 address
udpAddr, err := net.ResolveUDPAddr("udp", *addr)
if err != nil {
log.Fatalln("Error: ", err)
}
// Build listining connections
conn, err := net.ListenUDP("udp", udpAddr)
if err != nil {
log.Fatalln("Error: ", err)
}
defer conn.Close()
// Interacting with one client at a time
recvBuff := make([]byte, 1024)
for {
log.Println("Ready to receive packets!")
// Receiving a message
rn, 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 main
import (
"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 Address
remoteAddr, err := net.ResolveUDPAddr("udp", *raddr)
if err != nil {
log.Fatalln("Error: ", err)
}
// Make a connection
tmpAddr := &net.UDPAddr{
IP: net.ParseIP("127.0.0.1"),
Port: 0,
}
conn, err := net.DialUDP("udp", tmpAddr, remoteAddr)
// Exit if some error occured
if 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 server
buf := 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 main
import (
"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 Address
localAddr, 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 connections
conn, err := net.ListenUDP("udp", localAddr)
// Exit if some error occured
if 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 server
buf := 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倒是有很大的不同。
參考
相關文章
- 網路程式設計原理與UDP實現程式設計UDP
- 網路程式設計中TCP與UDP程式設計TCPUDP
- 基於UDP程式設計UDP程式設計
- 網路程式設計-UDP程式設計UDP
- 【網路程式設計】Tcp/Udp程式設計TCPUDP
- 通過 Socket 實現 UDP 程式設計 入門UDP程式設計
- Python網路程式設計——IP、UDPPython程式設計UDP
- Java網路程式設計之UDPJava程式設計UDP
- Java 網路程式設計(TCP程式設計 和 UDP程式設計)Java程式設計TCPUDP
- 網路程式設計UDP協議方式程式設計UDP協議
- 【網路程式設計】TCPIP-5-UDP程式設計TCPUDP
- python UDP程式設計是什麼意思?PythonUDP程式設計
- Python網路程式設計實現TCP和UDP連線Python程式設計TCPUDP
- 學會Zynq(11)RAW API的TCP和UDP程式設計APITCPUDP程式設計
- 基於TCP/UDP的Socket程式設計,HTTP/HTTPS協議TCPUDP程式設計HTTP協議
- UDP與TCP的區別UDPTCP
- TCP與UDP的區別TCPUDP
- 網路程式設計之 Udp接收資料程式設計UDP
- Java網路程式設計UDP通訊原理Java程式設計UDP
- UDP與TCPUDPTCP
- TCP與UDPTCPUDP
- Java&Python的TCP&UDP通訊-網路程式設計JavaPythonTCPUDP程式設計
- Golang 併發程式設計實踐Golang程式設計
- MQTT是TCP還是UDP?TCP與UDP區別MQQTTCPUDP
- 聊聊UDP、TCP和實現一個簡單的JAVA UDP小DemoUDPTCPJava
- Linux Socket C語言網路程式設計:UDP SocketLinuxC語言程式設計UDP
- Java網路程式設計--UDP傳送接收資料Java程式設計UDP
- Java 基於UDP 實現單播、組播、廣播 Socket 程式設計JavaUDP程式設計
- UdpUDP
- Swift 面向協議程式設計的那些事Swift協議程式設計
- 介面設計的那些事
- UDP&TCP Linux網路應用程式設計詳解UDPTCPLinux程式設計
- UDP協議網路Socket程式設計(java實現C/S通訊案例)UDP協議程式設計Java
- 002 Rust 網路程式設計,實現 UDP 伺服器和客戶端Rust程式設計UDP伺服器客戶端
- HTTP與UDP/TCP區別HTTPUDPTCP
- socket udpUDP
- Golang 實現客戶端與伺服器端UDP協議連線通訊Golang客戶端伺服器UDP協議
- 一文搞懂TCP與UDP的區別TCPUDP
- 從UDP的”連線性”說起–告知你不為人知的UDPUDP