有天晚上,平常幫忙測試的那個朋友有上線,想想沒事就請他幫忙測一下。
為了簡化測試內容,我改了一下指令,設定連線ip為讀取一個文件的內容,這樣每次開就不用重新輸入(從此可見我有多懶 XD)。
稍微教他一下操作方式,馬上就開始連線了,我大概可以預知結果是如何,因為我的NAT有開轉發功能(請看上一篇),所以我會被他的測試訊息洗板。他傳來了他的IP,我打開文件,輸入,存檔,再來Shift+F12執行,小黑窗再次出現在我眼前,
「正在連接...」
我向後仰,等著視窗被洗板,突然...
「'test,lancat,153.74.53.46,52876'
'test,lancat,153.74.53.46,52876'
已連接」
我愣住了,這不是想像中的樣子啊!
我快速的在鍵盤上打字,
「看的到嗎?」
「<153.74.53.46,52876>測試」
欸欸欸欸欸欸欸??????
那個是訊息,是他傳給我的訊息,不會吧為什麼?
「你看的到嗎?我看的到」
「<153.74.53.46,52876>。。。」
「喔耶!!!」
當下我以為成功了,真的是興奮感瞬間爆發,努力了這麼久,終於成功了!
「1+2=?」我傳
沒有回音,過了一會兒
「<153.74.53.46,52876>3.1415926.........」
嗯?怎麼是圓周率?
我突然有點懷疑到底是不是成功了,打開FB,
「欸你有收到嗎?」
「我什麼都沒看到。」
我被重擊,搞了半天,根本沒有成功,只是單方面的收到他訊息...
跟他說明了下,便繼續回到台下檢查程式,有幾個我不解的地方,因為如果他收不到我的訊息,就應該會不停的洗板下去才對啊!
除非,他收的到。
這下有趣了,在什麼樣的狀況時,我能夠收到他,而他既能收到我的回覆又不能收到我的訊息呢?我仔細看著剛才的紀錄,「153.74.53.46,52876...欸等等,52876是哪來的?」
我的Server預設是4啊,怎麼收到的訊息是從52876這個port來的呢?(有關port請看起步1、2),我檢查程式碼,發現我負責發送訊息的socket物件跟接收的是不一樣的!
「難道,打洞還需要是同一個port嗎?」
我改變了程式碼,將程式所有的發送接收統一由同一個socket來負責,並且固定綁定(bind)到4。
import socket,_thread,time
def server(ADDR,ip): #伺服器程式
global mode,data #讀取全域變數
s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)#建立socket物件
s.bind(ADDR) #綁定IP
print(ADDR)
addr = ip,4 #設定目的位址
_thread.start_new_thread(test,(s ,addr,ADDR))#開啟新線程不停傳送訊息到對方
data , addr = s.recvfrom(1024) #讀取收到的訊息
mode = False #利用全域變數讓另一個線程
print(data)
s.sendto('ok'.encode('UTF-8'),addr) #發送訊息"ok"給對方
s.sendto(data,(addr[0],4))
_thread.start_new_thread(user,(s ,addr))#開啟新線程來讀取使用者輸入並發送
while True:
data,addr = s.recvfrom(1024) #等待接收訊息
print (str(addr)+data.decode('UTF-8')) #印出訊息內容
s.close
def user(s,addr): #發送訊息給對方的函數
while True:
data=input() #讀取使用者輸入
if data == 'end': #如果輸入"end"就結束
break
data=data.encode('UTF-8')
s.sendto(data,addr) #發送訊息
s.close
def test(s,addr,ADDR): #不停發送信息的函數
global mode#讀取全域變數
global data
if mode:
print('準備連接'+str(addr))
x = 0
while mode and x <= 1000: #重複執行直到server說停或者過了100秒
s.sendto(data,addr)
time.sleep(0.1)
x+=1
if mode: #判斷是否成功
print('連線失敗')
else:
print('已連接')
HOST,PORT = socket.gethostname(),4 #取得自己的ip
HOST = socket.gethostbyname(HOST)
ADDR = (HOST,PORT)
mode = True
data = ('test,'+'lancat,'+str(ADDR[0])+','+str(ADDR[1])).encode()
#預設發送信息,內容無意義
time.sleep(0.5)
f=open('C:\\ip.txt','r') #讀取對方ip
ip = f.read()
f.close()
server(ADDR,ip) #啟動server函數
這次更長了,剛才稍微看了一下,發現還有一些廢話...,沒測試過不敢放上程式碼,所以照舊。
大概跟大家講重點,就是這次主線程是server,不像上次是server和user一起開始,並且user和test函數使用的socket都是server創造並綁定的(就是那個s),什麼意思呢?代表不管是傳送還是接收都是同一個port負責,據我推論,之前之所以失敗,因為test發送過去被對方server接收並回傳ok,所以洗板結束,但是可能連接的只有單方面,例如我朋友的訊息連到我的server,所以這裡就通了,但是我的user並沒有連到他的server,所以我可以接收他的,但他收不到我的,至於為什麼我可以一開始接收?因為我NAT有設定過!
這下子都真相大白了!我立馬再找他測試。
「1+2=?」
「<153.74.53.46,4> 3」
這次是真的成功了。
後記:這次的文章比較難寫,因為太多抽象的概念,而且很多因為是自己領悟的不知道真的專有名詞是什麼,希望大家能看得懂。XD
