关键字:udp、打洞、流程、关键、失败、拒绝、原因
时间:2021年10月

一、流程
1、客户端A向服务器发送任意数据,服务器记录客户端A的公网IP和端口,暂不应答客户端A;
2、客户端B向服务器发送任意数据,服务器把之前记录的客户端A的公网IP和端口发送给客户端B,同时把客户端B的公网端口和IP发送给A;
3、客户端A对收到服务器发来的客户端B的外网IP和端口立即发送数据,客户端B对收到服务器发来的客户端A的外网IP和端口立即发送数据;
4、客户端A和客户端B正常收发对方数据。

二、关键点
1、家用宽带基本都是可以UDP打洞的(Symmetric NAT很少见),没有成功通常是代码问题;
2、客户端本地端口可以固定也可以随机,但必须保证发送数据到服务器和发送数据到另一客户端使用相同的端口;
3、尽量保证两个客户端对发第一条数据在同一时刻,否则出现连接拒绝(refused)的概率很大。
4、实际打洞时,会有一定概率连接拒绝的情况,重新打洞即可,最好不要固定本地端口。

提供以打洞成功的源码,仅供参考:https://gitee.com/gitee211012/udp_hole_punching.git

关键字:golang、goroutine、channel、要点
时间:2018年8月

Golang编程的关键

golang编程语言相比其他语言的关键有两个,一个是goroutine,另一个是channel

关于goroutine

goroutine,相较于其他编程语言,可以理解为创建创建线程。但其他语言你需要控制线程的总数不能过多,否则CPU会大量消耗到线程切换上。但goroutine则完全不用顾虑,你可以任意调用。举个例子:

for i:=0;i<100000;i++ {
    go time.Sleep(time.Second * 300)
}

设想一下:如果你使用其他语言,创建了10万个线程,每个线程进行300秒的sleep,CPU会是什么状态?

关于channel

channel好比是阻塞队列,当队列为空时,取数据是会阻塞等待的;当队列满时,放入数据时是会阻塞等待的。

c := make(chan int, 10)
for i:=0;i<20;i++ {
    go func() {
        c <- i
        fmt.Printf("%d\n", i) // 通道c中放满10个数据后,则进入阻塞状态,此处不再打印。
    }()
}
time.Sleep(time.Second * 10)
for j:=0;j<10;j++ {
    <- c // 从通道c中取出数据后,则继续打印i。
 
}
c := make(chan int, 10)
for i:=0;i<10;i++ {
    go func() {
        n <- c
        fmt.Printf("%d\n", i) // 通道c中有数据后,才会执行此处的打印。
    }()
}
time.Sleep(time.Second * 10)
for j:=0;j<10;j++ {
    c <- j // 向通道c中放入数据,则开始打印i。
}

其他

像sync.WaitGroup等都是通过channel实现。