实验介绍
首先感谢掘金的其他大佬的笔记,我参考的是Go语言实现UDP socket的ack机制和丢包重传 | 青训营这篇笔记,我的代码与大佬基本类似(老偷懒了)
然后是我对这次实验的一个理解,既然是UDP Socket实现ack机制,我们可以将实验拆成两个部分:
- 实现UDP Socket编程
- 实现ack机制
1.实现UDP Socket编程
对于Socket的编程我标出重要的几个代码,对Socket编程的流程简单的过一下
Server:
1.服务器对目标IP地址的端口开启监听
listen, err := net.ListenUDP("udp", &net.UDPAddr{ IP: net.IPv4(127, 0, 0, 1), Port: 8080, })
2.接收UDP发送过来的数据
n, addr, err := listen.ReadFromUDP(data[:])
3.发送数据
_, err = listen.WriteToUDP(data, addr)
Client:
1.开启目标地址端口的UDP连接
connection, err := net.DialUDP("udp", nil, &net.UDPAddr{ IP: net.IPv4(127, 0, 0, 1), Port: 8080, })
2.发送数据
_, err = connection.Write(data)
3.接收数据
n, _, err := connection.ReadFromUDP(data)
2.实现ack机制
大概思路:对于ack机制,主要是从客户端发送至服务器端中有一个seq号,如果服务器端成功接收则放回一个ack号,ack = seq + 1。在这里,我们通过构建一个结构体Message,Message中包括一个int类型的Seq元素和我们需要发送的信息string类型的Msg元素,由于Socket是通过字节数组进行传输,我们还构建两个函数对结构体进行转变以及解析
Server:
package main
import (
"fmt"
"net"
"strconv"
"strings"
)
type Message struct {
Seq int
Msg string
}
func encode(msg Message) []byte {
return []byte(fmt.Sprintf("%d-%s", msg.Seq, msg.Msg))
}
func decode(data []byte) Message {
parts := strings.Split(string(data), "-")
seq, _ := strconv.Atoi(parts[0]) //string转换为int
msg := parts[1]
return Message{Seq: seq, Msg: msg}
}
func main() {
listen, err := net.ListenUDP("udp", &net.UDPAddr{
IP: net.IPv4(127, 0, 0, 1),
Port: 8080,
})
if err != nil {
fmt.Println("监听失败, 错误:", err)
return
} else {
fmt.Println("服务器成功启动")
}
defer listen.Close()
for {
var data [1024]byte
n, addr, err := listen.ReadFromUDP(data[:]) // 接收数据
if err != nil {
fmt.Println("UDP读取失败, 错误:", err)
continue
}
message := decode(data[:n])
fmt.Printf("Seq:%d Msg:%v \n", message.Seq, message.Msg)
ack := Message{Seq: message.Seq + 1, Msg: "ACK"}
_, err = listen.WriteToUDP(encode(ack), addr) // 发送数据
if err != nil {
fmt.Println("写入UDP失败, 错误:", err)
continue
}
}
}
Client:
package main
import (
"fmt"
"net"
"strconv"
"strings"
"time"
)
type Message struct {
Seq int
Msg string
}
func encode(msg Message) []byte {
return []byte(fmt.Sprintf("%d-%s", msg.Seq, msg.Msg))
}
func decode(data []byte) Message {
parts := strings.Split(string(data), "-")
seq, _ := strconv.Atoi(parts[0]) //string转换为int
msg := parts[1]
return Message{Seq: seq, Msg: msg}
}
func main() {
input := []string{"Message 1", "Message 2", "Message 3", "Message 4", "Message 5"}
socket, err := net.DialUDP("udp", nil, &net.UDPAddr{
IP: net.IPv4(127, 0, 0, 1),
Port: 8080,
})
if err != nil {
fmt.Println("连接服务端失败,err:", err)
return
}
defer socket.Close()
var seq int = 0
for _, msg := range input {
seq++
message := Message{Seq: seq, Msg: msg}
_, err = socket.Write(encode(message)) // 发送数据
if err != nil {
fmt.Println("发送数据失败,err:", err)
return
}
data := make([]byte, 4096)
socket.SetReadDeadline(time.Now().Add(5 * time.Second))
n, _, err := socket.ReadFromUDP(data) // 接收数据
if err != nil {
fmt.Println("接收数据失败,err:", err)
return
}
ack := decode(data[:n])
if ack.Seq == seq+1 {
fmt.Printf("Seq:%d Ack:%d Message:%s \n", seq, ack.Seq, ack.Msg)
} else {
fmt.Printf("丢包...")
return
}
}
}