项目地址:


https://github.com/hequan2017/pcdn-p2p-peering-bandwidth


点对点 互拉带宽
打包

    gox -osarch="linux/amd64" -ldflags "-s -w" -output="server"

    cd peer && gox -osarch="linux/amd64" -ldflags "-s -w" -output="peer"

server

    ./server

第一台

    ./peer --hequanid="321" --serverip="" --serverport=10000 --clientport=9999 --network="eth0" --tohequanid="123" --time=10 --uploadrate=1 --downloadrate=1

第二台

    ./peer --hequanid="123" --serverip="" --serverport=10000 --clientport=9999 --network="eth0" --tohequanid="321" --time=10 --uploadrate=1 --downloadrate=1
package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net"
	"sync"
	"time"
)

type Config struct {
	Hequanid   string `json:"hequanid"`
	ServerIP   string `json:"serverip"`
	ServerPort int    `json:"serverport"`
	ClientPort int    `json:"clientport"`
	Network    string `json:"network"`
	Tohequanid string `json:"tohequanid"`
}

type Peer struct {
	Addr      net.UDPAddr
	Timestamp time.Time
}

var (
	configs = make(map[string]Config)
	peers   = make(map[string]Peer)
	mu      sync.Mutex
)

func main() {
	listener, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 9999})
	if err != nil {
		fmt.Println(err)
		return
	}
	log.Printf("本地地址: <%s> \n", listener.LocalAddr().String())

	go cleanUpConfigs()

	for {
		data := make([]byte, 1024)
		n, remoteAddr, err := listener.ReadFromUDP(data)
		if err != nil {
			fmt.Printf("读取错误: %s", err)
			continue
		}
		log.Printf("接收到 <%s> 的数据: %s\n", remoteAddr.String(), data[:n])

		var config Config
		err = json.Unmarshal(data[:n], &config)
		if err != nil {
			log.Printf("JSON反序列化错误: %s", err)
			continue
		}

		mu.Lock()
		configs[config.Hequanid] = config
		peers[config.Hequanid] = Peer{Addr: *remoteAddr, Timestamp: time.Now()}
		mu.Unlock()

		mu.Lock()
		targetConfig, exists := configs[config.Tohequanid]
		if exists {
			targetPeer, addrExists := peers[targetConfig.Hequanid]
			if addrExists {
				message := fmt.Sprintf("给 %s 的 目标server: %s", config.Hequanid, targetPeer.Addr.String())
				message1 := fmt.Sprintf("给 %s 的 目标server: %s", config.Tohequanid, remoteAddr.String())
				sourcePeer := peers[config.Tohequanid]
				listener.WriteToUDP([]byte(targetPeer.Addr.String()), remoteAddr)
				listener.WriteToUDP([]byte(remoteAddr.String()), &sourcePeer.Addr)
				log.Printf(" \n %s \n %s \n", message, message1)
				delete(configs, config.Hequanid)
				delete(configs, config.Tohequanid)
			}
		}
		log.Printf("当前configs数量: %d ", len(configs))
		mu.Unlock()
	}
}

func cleanUpConfigs() {
	for {
		time.Sleep(30 * time.Minute)
		mu.Lock()
		for id, peer := range peers {
			if time.Since(peer.Timestamp) > 30*time.Minute {
				log.Printf("删除超时配置: %s", id)
				delete(configs, id)
				delete(peers, id)
			}
		}
		mu.Unlock()
	}
}



package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net"
	"os"
	"strconv"
	"strings"
	"time"
)

type Config struct {
	Hequanid   string `json:"hequanid"`
	ServerIP   string `json:"serverip"`
	ServerPort int    `json:"serverport"`
	ClientPort int    `json:"clientport"`
	Network    string `json:"network"`
	Tohequanid string `json:"tohequanid"`
}

const HAND_SHAKE_MSG = "我是打洞消息"

func parseAddr(addr string) net.UDPAddr {
	t := strings.Split(addr, ":")
	port, _ := strconv.Atoi(t[1])
	return net.UDPAddr{
		IP:   net.ParseIP(t[0]),
		Port: port,
	}
}

type SpeedInfo struct {
	MaxDownloadRate float64 `json:"maxDownloadRate"`
}

func bidirectionalHole(log *log.Logger, srcAddr *net.UDPAddr, anotherAddr *net.UDPAddr, MachineID, ToMachineID string, uploadRate float64, downloadRate float64) {

	var uploadRateFixed = 0.00
	var uploadRateDynamics = 0.00

	conn, err := net.DialUDP("udp", srcAddr, anotherAddr)
	if err != nil {
		fmt.Println(err)
	}
	defer conn.Close()
	// 向另一个peer发送一条udp消息(对方peer的nat设备会丢弃该消息,非法来源),用意是在自身的nat设备打开一条可进入的通道,这样对方peer就可以发过来udp消息
	if _, err = conn.Write([]byte(HAND_SHAKE_MSG)); err != nil {
		log.Println("send handshake:", err)
	}
	go func() {
		for {
			pattern := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
			data := bytes.Repeat(pattern, 1*1024*1024/len(pattern))
			packetSize := 1400 // 根据实际情况设置包的大小
			startTime := time.Now()
			upload := uploadRate
			uploadRateFixed = uploadRate

			if uploadRateFixed != 0.00 && uploadRateDynamics != 0.00 {
				if uploadRateFixed > uploadRateDynamics*1.1 {
					upload = uploadRateDynamics * 1.1
					fmt.Printf("本机固定上传带宽 大于 对方实际跑的下载带宽,开始降低本机上传网速!!!\n本机固定上传带宽:%.2f  |   对方实际跑的下载带宽:%.2f    |    本机限速上传:%.2f\n", uploadRateFixed, uploadRateDynamics, upload)
				}
			}
			_ = sendLargeData(log, conn, data, packetSize, upload)               // 调用函数
			totalBytesSent := sendLargeData(log, conn, data, packetSize, upload) // 调用函数
			elapsedTime := time.Since(startTime)                                 // 计算发送数据所花费的总时间

			uploadSpeedMbps := (float64(totalBytesSent) * 8) / (1024 * 1024) / elapsedTime.Seconds()
			fmt.Printf("%s --> %s -------------上传速率: %.2f Mbps\n", MachineID, ToMachineID, uploadSpeedMbps)
			//log.Printf("%s --> %s -------------上传速率: %.2f Mbps\n", MachineID, ToMachineID, uploadSpeedMbps)

		}
	}()
	for {
		bytesPerSecond := downloadRate * 1024 * 1024 / 8 // 将速率从兆字节转换为字节
		bufferSize := 1400                               // 定义缓冲区大小
		data := make([]byte, bufferSize)
		readInterval := time.Duration(bufferSize) * time.Second / time.Duration(bytesPerSecond)
		var totalBytes int64         // 累计接收到的字节数
		totalStartTime := time.Now() // 获取当前时间
		for {
			startTime := time.Now() // 获取当前时间
			n, _, err1 := conn.ReadFromUDP(data)
			if err1 != nil {
				//log.Printf("Error during read: %s\n", err1)
				continue // 如果发生错误则跳过当前循环
			}
			// 成功读取数据
			totalBytes += int64(n)
			// 计算读取操作所需的时间,然后暂停直到这个时间过去
			time.Sleep(readInterval - time.Since(startTime))

			elapsed := time.Since(totalStartTime)
			if elapsed >= 5*time.Second { // 如果超过或等于10秒,计算并打印速率
				speedMbps := (float64(totalBytes) * 8) / (1024 * 1024) / elapsed.Seconds() // 转换为Mbps
				fmt.Printf("%s <-- %s -------------下载速率: %.2f Mbps \n", MachineID, ToMachineID, speedMbps)
				// 重置计数器和计时器
				totalBytes = 0
				totalStartTime = time.Now()
				speedInfo := SpeedInfo{MaxDownloadRate: speedMbps}
				infoData, _ := json.Marshal(speedInfo)
				conn.Write(infoData) // 发送当前上传速率信息

				if elapsed >= 300*time.Second {
					log.Printf("%s <-- %s -------------下载速率: %.2f Mbps \n", MachineID, ToMachineID, speedMbps)
				}
			}

			// 尝试解析作为JSON的速度信息
			if int64(n) < 1000 {
				var speedInfo SpeedInfo
				if err := json.Unmarshal(data[:n], &speedInfo); err == nil {
					// 成功解析为速度信息
					fmt.Printf("%s -->>>> %s ----对方服务器-------下载速率: %.2f Mbps \n", MachineID, ToMachineID, speedInfo.MaxDownloadRate)
					uploadRateDynamics = speedInfo.MaxDownloadRate
					if elapsed >= 600*time.Second {
						log.Printf("%s -->>>> %s ----对方服务器-------下载速率: %.2f Mbps \n", MachineID, ToMachineID, speedInfo.MaxDownloadRate)
					}
				}
			}
		}
	}
}

func sendLargeData(log *log.Logger, conn *net.UDPConn, data []byte, packetSize int, uploadRate float64) int64 {
	totalLen := len(data)
	bytesPerSecond := uploadRate * 1024 * 1024 / 8 // 将 Mbps 转换为字节/秒
	packetInterval := time.Second * time.Duration(packetSize) / time.Duration(bytesPerSecond)
	var totalBytesSent int64 = 0
	for i := 0; i < totalLen; i += packetSize {
		packetStartTime := time.Now() // 每个包的发送开始时间
		end := i + packetSize
		if end > totalLen {
			end = totalLen
		}
		n, err := conn.Write(data[i:end])
		if err != nil {
			//log.Printf("Failed to send packet: %s\n", err)
			continue
		}
		totalBytesSent += int64(n) // 更新发送的总字节数

		// 确保控制发送速率
		time.Sleep(packetInterval - time.Since(packetStartTime))
	}
	return totalBytesSent
}
func main() {

	logFile, err := os.OpenFile("peer.log", os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666)
	if err != nil {
		log.Fatal("Failed to open log file:", err)
	}
	defer logFile.Close()

	// 创建一个新的日志记录器
	logger := log.New(logFile, "", log.Ldate|log.Ltime)

	// 定义命令行参数
	hequanid := flag.String("hequanid", "", "Identifier of the machine")
	serverIP := flag.String("serverip", "", "IP address of the server")
	serverPort := flag.Int("serverport", 0, "Port number of the server")
	clientPort := flag.Int("clientport", 0, "Client port number to use")
	network := flag.String("network", "", "Network interfaces to use")
	tohequanid := flag.String("tohequanid", "", "to Target machine identifier")
	timeout := flag.Int("time", 0, "Timeout in minutes after which the program will exit")
	uploadRate := flag.Float64("uploadrate", 0, "uploadrate rate in Mbps")
	downloadRate := flag.Float64("downloadrate", 0, "downloadrate rate in Mbps")
	// 解析命令行参数
	flag.Parse()
	// 设置超时定时器,到时间后退出程序

	time.AfterFunc(time.Duration(*timeout)*time.Minute, func() {
		logger.Printf("----------------程序运行超过 %d 分钟,已经自动退出。-------------------\n", *timeout)
		os.Exit(0)
	})

	logger.Printf("------------------程序运行超过 %d 分钟,将会自动退出。-------------------------\n", *timeout)

	// 参数验证
	if *hequanid == "" || *serverIP == "" || *serverPort == 0 || *clientPort == 0 || *network == "" || *tohequanid == "" || *timeout == 0 || *uploadRate == 0 || *downloadRate == 0 {
		logger.Println("All parameters are required and must not be empty")
		flag.Usage()
		os.Exit(1) // 退出程序
	}

	networkList := strings.Split(*network, ",")

	for _, v := range networkList {
		go func(v string) {
			iface, _ := net.InterfaceByName(v)
			addrs, _ := iface.Addrs()
			for _, addr := range addrs {
				if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
					if ipnet.IP.To4() != nil {
						logger.Printf("IPv4 address: %s\n", ipnet.IP.String())
						// 使用解析的参数建立UDP连接
						srcAddr := &net.UDPAddr{IP: ipnet.IP.To4(), Port: *clientPort}
						dstAddr := &net.UDPAddr{IP: net.ParseIP(*serverIP), Port: *serverPort}
						conn, err := net.DialUDP("udp", srcAddr, dstAddr)
						if err != nil {
							//logger.Println("Error dialing UDP:", err)
							continue
						}
						defer conn.Close()
						// 连接成功,打印配置信息
						logger.Printf("Connected to %s from %s\n", dstAddr, srcAddr)
						//logger.Printf("hequanid: %s, Network: %s, Tohequanid: %s  \n", *hequanid, *network, *tohequanid)

						tag := Config{
							Hequanid:   *hequanid + "_" + v,
							ServerIP:   *serverIP,
							ServerPort: *serverPort,
							ClientPort: *clientPort,
							Network:    *network,
							Tohequanid: *tohequanid + "_" + v,
						}

						tagStr, _ := json.Marshal(tag)
						if _, err = conn.Write(tagStr); err != nil {
							//logger.Printf("error during read: %s", err)
							continue
						}
						data := make([]byte, 1024)
						n, remoteAddr, err := conn.ReadFromUDP(data)
						if err != nil {
							//logger.Printf("error during read: %s", err)
						}
						conn.Close()
						anotherPeer := parseAddr(string(data[:n]))
						logger.Printf("打洞开始: local:%s server:%s another:%s\n\n", srcAddr, remoteAddr, anotherPeer.String())

						// 开始打洞

						bidirectionalHole(logger, srcAddr, &anotherPeer, *hequanid+"_"+v, *tohequanid+"_"+v, *uploadRate, *downloadRate)
					}
				}
			}
		}(v)
	}
	select {}
}