课后作业-UDP Socket实现ack机制 |青训营笔记

76 阅读2分钟

实验介绍

首先感谢掘金的其他大佬的笔记,我参考的是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
		}

	}
}