当前位置: 首页 > news >正文

自由通讯的魔法:Go从零实现UDP/P2P 聊天工具

嘿,各位网络巫师们!今天我们要揭秘一个神奇的魔法——如何让两个不同网络下的客户端,绕过所有阻碍,直接进行秘密通信!这个项目将带你探索 Go 语言实现的 UDP 模式下的p2p技术,让你也能成为网络通信的魔法师!

项目概述

这是一个简单但强大的 P2P 通信系统,由两位得力助手组成:

  1. 红娘服务器:扮演月老角色,为两个素未谋面的客户端牵线搭桥
  2. 通信客户端:一旦拿到对方的联系方式,就能甩开红娘,直接私聊(坏笑)

技术魔法原理

P2P的秘密

想象一下:两个客户端就像住在两个封闭小区里的人,小区门口有保安(NAT 设备)严格检查访客。直接敲门是行不通的,但我们可以用一个巧妙的方法——

  1. 两个人都先给小区外的红娘服务器打电话:“我想认识新朋友!”
  2. 红娘记下了两人的家庭住址(公网 IP 和端口)
  3. 红娘告诉双方:“对方住在某某小区几号门”
  4. 两人同时尝试给对方打电话(虽然可能都打不通)
  5. 关键来了!这次拨号虽然失败,但会在各自小区门口留下记录:“这个人可以接来自某某小区的电话”
  6. 当他们再次尝试联系时——奇迹发生了!电话居然接通了!
  7. 从此,两人可以自由聊天,再也不需要红娘从中传话了!

代码实现分析

红娘服务器实现(幕后牵线人)

package mainimport ("fmt""log""net""time"
)func main() {listener, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 9981})if err != nil {fmt.Println(err)return}log.Printf("本地地址: <%s> \n", listener.LocalAddr().String())peers := make([]net.UDPAddr, 0, 2)  // 最多介绍两个有缘人data := make([]byte, 1024)for {n, remoteAddr, err := listener.ReadFromUDP(data)if err != nil {fmt.Printf("error during read: %s", err)}log.Printf("<%s> %s\n", remoteAddr.String(), data[:n])peers = append(peers, *remoteAddr)// 凑齐一对就开始介绍if len(peers) == 2 {log.Printf("进行UDP穿透,建立 %s <--> %s 的连接\n", peers[0].String(), peers[1].String())listener.WriteToUDP([]byte(peers[1].String()), &peers[0])  // 告诉第一个人第二个人的地址listener.WriteToUDP([]byte(peers[0].String()), &peers[1])  // 告诉第二个人第一个人的地址time.Sleep(time.Second * 8)  // 给两人一点时间熟悉一下log.Println("中转服务器退出,仍不影响peers间通信")  // 红娘功成身退return}}
}

服务器端的主要功能:

  1. 在 UDP 端口 9981 上监听连接
  2. 维护一个 peers 数组,最多存储两个客户端的地址信息
  3. 当有客户端发送数据时,将其地址添加到 peers 数组中
  4. peers 数组中有两个客户端时,执行以下操作:
    • 将第一个客户端的地址信息发送给第二个客户端
    • 将第二个客户端的地址信息发送给第一个客户端
    • 等待 8 秒,确保两个客户端有足够的时间完成穿透
    • 服务器退出,但不影响两个客户端之间已建立的 P2P 连接

客户端实现(想要交友的小伙伴)

package mainimport ("fmt""log""net""os""strconv""strings""time"
)var tag string  // 我的昵称const HAND_SHAKE_MSG = "我是打招呼消息"  // 破冰开场白func main() {// 当前进程标记字符串,便于显示tag = os.Args[1]srcAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 7777} // 注意端口必须固定,这是我的固定电话dstAddr := &net.UDPAddr{IP: net.ParseIP("*.*.*.*"), Port: 9981} // 红娘服务器地址(已隐藏真实IP)conn, err := net.DialUDP("udp", srcAddr, dstAddr)if err != nil {fmt.Println(err)}// 向红娘自我介绍if _, err = conn.Write([]byte("hello, I'm new peer:" + tag)); err != nil {log.Panic(err)}// 等待红娘介绍对象data := make([]byte, 1024)n, remoteAddr, err := conn.ReadFromUDP(data)if err != nil {fmt.Printf("error during read: %s", err)}conn.Close()  // 拿到联系方式后,就不需要红娘了anotherPeer := parseAddr(string(data[:n]))  // 解析对方地址fmt.Printf("local:%s server:%s another:%s\n", srcAddr, remoteAddr, anotherPeer.String())// 开始穿透(破冰行动)bidirectionHole(srcAddr, &anotherPeer)
}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,}
}func bidirectionHole(srcAddr *net.UDPAddr, anotherAddr *net.UDPAddr) {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 {time.Sleep(10 * time.Second)if _, err = conn.Write([]byte("from [" + tag + "]")); err != nil {log.Println("send msg fail", err)}}}()for {data := make([]byte, 1024)n, _, err := conn.ReadFromUDP(data)if err != nil {log.Printf("error during read: %s\n", err)} else {log.Printf("收到数据:%s\n", data[:n])}}
}

客户端的主要功能:

  1. 从命令行参数获取标签(tag),用于标识自己
  2. 在固定端口 7777 上创建 UDP 连接(注意:端口必须固定,程序自定义的端口
  3. 连接到服务器(...:9981)并发送自己的标识信息
  4. 从服务器接收另一个客户端的地址信息
  5. 关闭与服务器的连接
  6. 调用 bidirectionHole 函数进行双向通讯:
    • 创建一个连接到另一个客户端的 UDP 连接
    • 发送一条握手消息,虽然可能被对方的 NAT 丢弃,但会在自己的 NAT 上打开一条通道
    • 启动一个 goroutine,每 10 秒向对方发送一条消息
    • 主 goroutine 持续监听并打印来自对方的消息

使用魔法指南

如何启动红娘服务器

  1. 在一台有公网 IP 的服务器上,施展以下咒语:
go build -o server.exe main.go  # 铸造魔法道具
./server.exe  # 启动红娘服务

如何让两个客户端建立私聊

  1. 在两个不同的网络环境(比如不同的家庭网络)中,分别启动客户端:
go build -o client.exe main.go  # 铸造魔法道具
./client.exe 小明  # 第一个客户端,名为小明
./client.exe 小红  # 第二个客户端,名为小红
  1. 接下来,魔法就会自动发生!两个客户端会自动联系红娘,获取对方的地址,完成 P2P,然后就可以直接聊天啦!

魔法注意事项

⚠️ 重要提示:以下是施展魔法的关键要点! ⚠️

  1. 客户端的端口必须固定的!这就像是魔法通讯的专用频道,一旦改变,魔法就会失效!
  2. 你需要在客户端代码中填入真实的红娘服务器 IP 地址,替换掉 *.*.*.*
  3. 红娘服务器是个热心人,但她只在介绍阶段工作。一旦两位客户端建立了私聊,红娘就会功成身退,而私聊不会受到影响
  4. 记得在实际部署时修改服务器 IP 地址,代码中的 *.*.*.* 只是个占位符

魔法升级建议

如果你想让这个魔法更加强大,可以考虑以下升级路径:

  1. 添加错误重试魔法,让连接更加稳定可靠
  2. 添加心跳检测咒语,及时发现魔法连接是否中断
  3. 让红娘服务器能够同时介绍多对朋友,而不仅仅是一对
  4. 添加通信加密魔法,保护私聊内容不被偷听

魔法师总结

这个项目用简单的 Go 代码展示了 UDP 穿透这个网络魔法的实现方法。通过这个魔法,我们能够让两个被防火墙隔离的客户端建立直接的秘密通信通道!

这种魔法在很多地方都有应用:

  • 在线游戏中,玩家之间的低延迟通信
  • 视频通话软件,让通话更加流畅清晰
  • 文件共享工具,实现点对点高速传输

虽然示例代码看起来简单,但它包含了 UDP 穿透魔法的核心秘密。掌握了这个魔法,你就可以进一步探索更复杂的 P2P 通信系统,成为真正的网络魔法师!

现在,快去施展你的魔法吧!🧙‍♂️✨

往期部分文章列表

  • Go语言实现的简易远程传屏工具:让你的屏幕「飞」起来
  • 当你的程序学会了“诈尸“:Go 实现 Windows 进程守护术
  • 验证码识别API:告别收费接口,迎接免费午餐
  • 用 Go 给 Windows 装个"顺风耳":两分钟写个录音小工具
  • 无奈!我用go写了个MySQL服务
  • 使用 Go + govcl 实现 Windows 资源管理器快捷方式管理器
  • 用 Go 手搓一个 NTP 服务:从"时间混乱"到"精准同步"的奇幻之旅
  • 用 Go 手搓一个 Java 构建工具:当 IDE 不在身边时的自救指南
  • 深入理解 Windows 全局键盘钩子(Hook):拦截 Win 键的 Go 实现
  • 用 Go 语言实现《周易》大衍筮法起卦程序
  • Go 语言400行代码实现 INI 配置文件解析器:支持注释、转义与类型推断
  • 高性能 Go 语言带 TTL 的内存缓存实现:精确过期、自动刷新、并发安全
  • Golang + OpenSSL 实现 TLS 安全通信:从私有 CA 到动态证书加载
http://www.dtcms.com/a/532240.html

相关文章:

  • Cortex-M3-STM32F1 开发:(十二)HAL 库开发 ➤ SysTick 系统滴答定时器
  • go-ethereum core之以太网虚拟机EVM
  • 自己怎么免费做网站网站开发 合同
  • 网站如何做脚注一般使用的分辨率的显示密度是多少dpi )
  • 嵌入式开发中ln命令使用指南
  • C++模板进阶及特化实战指南
  • zenm自己做网站淮北建设网
  • 网站title keyword descriptionwordpress 分类筛选
  • 网站系统设计目标企业融资方案范本
  • 《AI 应用层革命(二)——从应用到生态:当智能体开始重塑世界》
  • 使用 Python 元类与属性实现惰性加载:Effective Python 第47条
  • 环广西世巡赛开战!维乐Senso Prime 坐垫助你竞速
  • DeepSeek讲“南辕北辙”者的志向
  • 做网站在线视频如何添加湘潭网站seo
  • 智能文本抽取:通过OCR、自然语言处理等多项技术,将非结构化文档转化为可读、可分析的数据资产
  • 许昌哪个网站做苗木网站建设怎么让百度搜索到
  • 代码训练LeetCode(49)插入区间
  • wordpress做游戏网站国家新闻大事
  • 【Macos】安装 macFUSE 和 SSHFS 实现在 Finder 中挂载服务器目录
  • 【高并发服务器】十、Connection连接管理模块设计与实现
  • 内网网站建设流程高佣联盟做成网站怎么做
  • Canvas 复杂交互步骤:从事件监听 to 重新绘制全流程
  • 【js】class中constructor如何接收动态值,如timeRange
  • Gorm(四)删除操作
  • XSLT `<sort>` 标签详解
  • h5游戏免费下载:读心术
  • 免费建站有哪些网站注册公司需要怎么注册
  • GDB Server使用方法(基于vscode的可视化调试)
  • Retrieval Augmented Time Series Forecasting 论文笔记
  • Vscode中选择Conda环境