p2p的基石: 快樂udp打洞試驗

因疫情在家辦公,遠程操作公司電腦。突然想起來試一試很久以前一直想做的試驗: udp打洞。畢竟男人鱔變,最愛鑽洞。

試驗步驟記錄如下:

注:家裏的電腦記爲PC_A,公司電腦記爲PC_B

1.利用stun服務,檢測兩邊電腦的NAT類型,並得到兩邊的內網,外網地址。

這裏使用https://github.com/ccding/go-stun這個庫。由於go-stun並沒有在控制檯輸出本機udp的端口,故需要到他的源碼中加上一句:

fmt.Println("local addr:", conn.LocalAddr().String())
func (c *Client) Discover() (NATType, *Host, error) {
	if c.serverAddr == "" {
		c.SetServerAddr(DefaultServerAddr)
	}
	serverUDPAddr, err := net.ResolveUDPAddr("udp", c.serverAddr)
	if err != nil {
		return NATError, nil, err
	}
	// Use the connection passed to the client if it is not nil, otherwise
	// create a connection and close it at the end.
	conn := c.conn
	if conn == nil {
		conn, err = net.ListenUDP("udp", nil)
        // 在此處新增以打印出本機的端口
		fmt.Println("local addr:", conn.LocalAddr().String())
		if err != nil {
			return NATError, nil, err
		}
		defer conn.Close()
	}
	return c.discover(conn, serverUDPAddr)
}

重新打包生成go-stun.exe後,分別在PC_A,PC_B中執行以下命令

 go-stun.exe -s stun.sipgate.net:10000

其中stun.sipgate.net:10000爲免費的公網stun服務器,這裏面一定要挑選能用的,因爲某些不可描述原因,大部分的stun服務器是無法訪問的.

PC_A輸出如下:

PC_B輸出如下:

幸運的是,PC_A, PC_B都是屬於端口限制型NAT,理論上打洞能夠成功。

2.編寫python代碼

經過上一步,我們有:

PC_A 內部端口 52280 外部地址 xxx.xxx.0.195

PC_B 內部端口 64320 外部地址 xxx.xxx.169.96

那麼,py程序的大概工作便是:

1.建立一個udp socket,綁定至本機的內部端口

2.起一個發送線程,每隔若干時間,向對方的外部地址發送消息

3.主線程起一個循環,接收對方發來的消息

PC_A, PC_B的py腳本應該完全一致的,區別僅是一些參數。

PC_A的py腳本如下:

import socket
import time
from threading import Thread

local_port = 52280
remote_ip, remote_port = "xxx.xxx.169.96", 64320
msg = "hello, i am aaaaaaaaaaaa"
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(("0.0.0.0", local_port))


class SendThread(Thread):

    def run(self):
        while True:
            s.sendto(msg.encode(), (remote_ip, remote_port))
            print("sent")
            time.sleep(1)


st = SendThread()
st.start()

while True:
    data = s.recv(1024)  # 一次接收1024字節
    print(data.decode())  # decode()解碼收到的字節

類似的,PC_B的py腳本如下:

import socket
import time
from threading import Thread

local_port = 64320
remote_ip, remote_port = "xxx.xxx.0.195", 52280
msg = "hello, i am bbbbbbbb"
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(("0.0.0.0", local_port))


class SendThread(Thread):

    def run(self):
        while True:
            s.sendto(msg.encode(), (remote_ip, remote_port))
            print("sent")
            time.sleep(1)


st = SendThread()
st.start()

while True:
    data = s.recv(1024)  # 一次接收1024字節
    print(data.decode())  # decode()解碼收到的字節

3.開始打洞!

分別在PC_A, PC_B運行py腳本

PC_A的控制檯輸出:

sent
sent
sent
sent
sent
hello, i am bbbbbbbb
sent
hello, i am bbbbbbbb
sent
hello, i am bbbbbbbb
sent
hello, i am bbbbbbbb
sent
hello, i am bbbbbbbb
sent

PC_B的控制檯輸出:

sent
hello, i am aaaaaaaaaaaa
sent
hello, i am aaaaaaaaaaaa
sent
hello, i am aaaaaaaaaaaa
sent
hello, i am aaaaaaaaaaaa
sent
hello, i am aaaaaaaaaaaa
sent
hello, i am aaaaaaaaaaaa
sent
hello, i am aaaaaaaaaaaa
sent
hello, i am aaaaaaaaaaaa

顯而易見,打出了一個完美的洞

4.後記 

一條簡簡單單的hello消息,卻是代表着文件傳輸,遠程桌面的無限可能

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章