究竟什麼是「最後的審判」呢?Golang牌需要一個新主人,但是現在有一大部分的Golang牌在小櫻手上,一小部分在小狼手上
現在只有打倒「審判者月」的人能成為新主人。
那麼審判者月是誰呢?那就是小櫻每天發春的對象「月城雪兔」
那麼如果這些Golang牌沒有新主人呢?那麼這些Golang牌為了不讓自己太過難過。它們一定會想辦法忘記「喜歡」的感覺。所有的人將會忘記喜歡人的感覺。
最後一天的課程,想要來實作一台 UDP 伺服器,前一篇提到 UDP 雖然無法保證資料正確,但是 UDP 延遲低,在不講求資料正確性的情況下可以使用 UDP 來傳輸資料
上次開的 port 1450 可能有人不喜歡,所以改開 689 吔不是,這個數字好像藍綠一起婊吔
伺服器示範
利用 net.ListenPacket()
來建立一個監聽,其中,第一個回傳值的型態為 net.PacketConn
可以到官網進一步了解用法:net.PacketConn - Go
package main
import(
"fmt"
"net"
)
func main(){
// 與 tcp 不同,udp 要改用 net.ListenPacket() 來做監聽
ln, err := net.ListenPacket("udp", ":689")
defer ln.Close()
if err != nil {
panic("監聽 port 689 失敗")
}
fmt.Println("SERVER")
for {
buf := make([]byte, 1024)
// ln.ReadFrom 分別回傳三個值
// buf_len 代表讀入了幾個 byte
// addr 代表客戶端的位址
buf_len, addr, err := ln.ReadFrom(buf)
if err != nil {
continue
}
go func(ln net.PacketConn, addr net.Addr, buf []byte){
fmt.Printf("用戶端位址: %s\n收到: %s\n", addr, buf)
ln.WriteTo([]byte("伺服器端已收到資料!\n"), addr)
}(ln, addr, buf[:buf_len])
}
}
用戶端示範
package main
import (
"fmt"
"net"
"time"
)
func main() {
// 吔,不是還真的開 689 啊
// 反正 689 沒人用
res, err := sendUDP("localhost:689", "封印解除!")
if err != nil {
fmt.Println(err.Error())
}else{
fmt.Println(res)
}
}
func sendUDP(addr, msg string) (string, error) {
conn, _ := net.Dial("udp", addr)
_, err := conn.Write([]byte(msg))
bs := make([]byte, 1024)
// 設定 UDP 連線期限
conn.SetDeadline(time.Now().Add(3 * time.Second))
len, err := conn.Read(bs)
if err != nil {
return "", err
}else{
return string(bs[:len]), err
}
}
UDP 傳送封包時有一定限制,建議最大不要超過 512 bytes (stack overflow 上有人這樣說的)。若是使用 TCP 則不必擔心,TCP 有自己切封包的機制。
server
package main
import(
"fmt"
"net"
"io"
"os"
)
func photo(path string, buf_channel chan []byte){
file, err := os.Open(path)
defer file.Close()
if err != nil {
fmt.Println(err)
close(buf_channel)
}
buf := make([]byte, 512)
for {
n, err := file.Read(buf)
if err != nil && err != io.EOF {
panic(err)
}
if n == 0 {
break
}
buf_channel <- buf
}
close(buf_channel)
}
func main(){
ln, err := net.ListenPacket("udp", ":689")
defer ln.Close()
if err != nil {
panic("監聽 port 689 失敗")
}
fmt.Println("SERVER")
for {
buf := make([]byte, 512)
buf_len, addr, err := ln.ReadFrom(buf)
if err != nil {
continue
}
go func(ln net.PacketConn, addr net.Addr, buf []byte){
fmt.Printf("用戶端位址: %s\n收到: %s\n", addr, buf)
// 回傳圖片!
buf_chan := make(chan []byte, 8)
go photo("demo.jpg", buf_chan)
for val := range buf_chan{
ln.WriteTo(val, addr)
}
}(ln, addr, buf[:buf_len])
}
}
client
package main
import (
"fmt"
"net"
"time"
"os"
)
func main() {
res, err := sendUDP("localhost:689", "請求傳送圖片")
if err != nil {
fmt.Println(err.Error())
}else{
fmt.Println(res)
}
}
func sendUDP(addr, msg string) (res string, err error) {
err = nil
conn, err := net.Dial("udp", addr)
if err != nil{
return "", err
}
_, err = conn.Write([]byte(msg))
if err != nil{
return "", err
}
buf := make([]byte, 512)
conn.SetDeadline(time.Now().Add(5 * time.Second))
res = ""
file, err := os.Create("output.mp3")
if err != nil{
return "", err
}
for n, err := conn.Read(buf); ; n, err = conn.Read(buf){
if err != nil{
fmt.Println(err)
break
}
file.Write(buf[:n])
}
file.Close()
return res, nil
}
UDP 在傳送時,會有傳錯的情況發生,但是沒有想到 jpg, png 都無法容錯。於是我又試了一些格式
圖片
音樂
影片
各位魔法使們能在不使用 TCP 而只用 UDP 的情況下解決這個問題嗎?
期待明年下一季: Golang魔法使 ── 網際網路篇
風啊!快點幫我抓住他(審判者月)
本文圖片來自:庫洛魔法使第二季第十一集