我的P2P聊天程式到目前,已經可以讓互相知道外網IP的兩人互相連結。但對於一般使用者來說,要曉得自己的IP已不容易,還要知道對方的!變成還是得要經由其他聊天軟體先交換IP,使用上不方便。於是接下來P2P程式的目標,就是做出能夠自動依據使用者輸入的聊天室名稱,來配對使用者。

構想是這樣的,A B兩人要聊天,A先打開了程式,設定聊天室名稱為「room」,程式會去訪問網路上紀錄各個聊天室的試算表,去得知聊天室『room』是否存在。當聊天室『room』不存在,A的程式便會以A的電腦作為聊天室『room』的伺服器。再來,B也開啟了程式並連結到『room』,程式一樣到網路上的試算表去尋找『room』,發現已經有人開啟了,再來就會根據在試算表上的IP來連結到A的電腦。

因此,要能夠自動配對,一定要有個穩定的伺服器,並提供存取資料的服務。說到這裡,大家應該都會想到雲端硬碟,沒錯,Google、dropbox等雲端硬碟服務,都有提供API供開發者使用,只要去申請一個項目(多半不用錢),就可以讓程式透過API去控制這些雲端硬碟。舉個例子,如果想要製作一個幫使用者清空雲端硬碟垃圾桶的程式(好沒意義),就去裝Google drive的API,然後申請個專案,就可以使用Google drive的服務。

而我這個程式有個不一樣之處,我的程式並不是存取「使用者」的資料,而是存取我在雲端的一個文件,所以申請專案後,還須辦一個機器人帳號(service account),這樣程式存取的就是這個機器人帳戶的文件,假如我金鑰被盜,頂多也就機器人帳號的資料被存取,我本帳的資料都活得好好的。

1.與Pydrive的苦戰

一開始就想使用Google,畢竟Google的空間大,我平常也都使用這家,所以就去查Google的API。最早的想法是用編輯權限連結,再去模擬網頁操作來存取,但後來發現它有API,當然就直接用API了。

Google API的中文資料不多,所以找的很辛苦,可能是因為像我這樣的獨立開發者很少想碰這種吧!後來只好看他原文教學,但我不知為何從第一步就卡住了,而且冒出了很多奇怪的報錯,後來一度以為是版本問題,所以放棄了Google。

Google沒有我就去找Dropbox,看一些網路上的評論,Dropbox對python是很友善的,所以就去辦了人生第一個Dropbox帳戶,但跟Google一樣,中文教學少之又少,一個下午過去還是什麼都沒有,最後就不了了之了。

2.再次挑戰

自從自動配對失敗之後,點對點聊天程式就慢慢沒做了,改去做了其他的專案,但過了一個月,想到可以用這個作品去參加科展,於是又翻了出來,仍然是卡在自動配對,於是我再度的去研究Google API

我這個時候才發現Google版本不合是個誤解,耐心的從頭開始做,吃力看原文教學還有文檔,終於弄出了一點成果(成功的登入),但翻了翻文檔,就是找不到操作機器人帳戶的函數。

直到我找到了最關鍵的一篇教學:使用Python上傳資料到Google試算表 – 高中資訊科技概論教師黃建庭的教學網站

這裡頭的使用的並不是Pydrive,而是更基本的Oauth2還有專門存取google試算表的gspreadoauth2是一種用常用的登入模式,像是玩一個遊戲,要辦帳號時,選擇以facebook帳號登入,就是使用這種協定。

這篇教學的內容跟我的需求幾乎完全重疊,所以我不花多少力氣就完成了登入並存取。

3.模組化

為了方便日後的存取,我製作一個專門用來存取資料的Class,主要功能有搜尋、寫入、移除等,完成了模組後,基本上自動配對已經完成了。

後來因應程式翻新,我重寫了負責網路的文檔,並用class架構包覆,以利未來的開發(之前是指用函數),把網路取得IP的部份跟寫好的模組結合,自動配對就完成了。

4.錯誤

後來又修了許多地方,最後釋出第1測試版時,在學校的電腦測試,發生了奇怪的錯誤。

一開始用隨身碟Linux開,完全無法連線,明明都可以用瀏覽器,但是在命令列操作時就會卡住,點Ctrl C斷開來看,發現都是卡在最初要連線的地方。由於懷疑是系統沒更新,用sudo apt update + upgrade,卻也會卡住。

後來開原來的Windows測試,發現還是不能用,雖然可以連線到Google drive,但是非常~慢,要寫入一個操作,需要五分鐘,這以電腦的標準看,都以經過五天了。詭異的是,好不容易取得了目標IP,連過去卻被「遠端主機強制關閉了一個現存的連線」。發生這樣錯誤的,包括二年級導師室、班上教室,但是電腦教室還有教務處電腦卻可以運行。而且我有一個朋友跟他測試時也發生這樣的錯誤。

經事後分析,估計原因是防火牆的緣故,可能學校教師用電腦有裝比較安全的防火牆,所以我的程式被封鎖了。也有可能是那邊NAT種類不同的原因,不過點對點軟體本來就不可能萬能,因為要克服各式各樣的NAT和防火牆,沒有辦法把每一種都打洞(如果真的可以防火牆業者也要檢討了)。

 

第一次試用版的commit:https://github.com/lancatlin/P2P-/tree/71331540be8f90ced919416aca523cb9946369f9

後記:

專心於開發,很久沒有更新部落格了,這次講的主題是我苦戰了一個月自動配對,事實上遇到的問題比文章中描述的還多,不過最終還是解決了!!

感謝時常幫助我的兩位網友!

 

自動配對的模組程式碼:

from oauth2client.service_account import ServiceAccountCredentials as SAC
import gspread,key

class GetIP:
    def __init__(self,sheet):
        k = key.key()
        k.read()
        scopes = ['https://spreadsheets.google.com/feeds']
        service = SAC.from_json_keyfile_name('key.json',scopes)
        gc = gspread.authorize(service)
        k.remove()
        self.sheet = gc.open(sheet).sheet1
        self.all = self.sheet.get_all_records()
    def search(self,name):
        all = self.all
        for i in all:
            if i['Name'] == name:
                return {'wan':i['wan'],'lan':i['lan'],'port':i['port']}
        return None
    def set_IP(self,name,wan,lan,port):
        self.sheet.append_row([name,wan,lan,port])
        self.all = self.sheet.get_all_records()
        print(self.all)
    def clear(self,name):
        all = self.sheet.get_all_records()
        all_len = len(all)
        print(all,all_len,name)
        for i in range(all_len):
            if all[i]['Name'] == name:
                self.sheet.delete_row(i+2)
                print(i+1)
                break
    def get_all(self):
        return self.sheet.get_all_records()
if __name__ == '__main__':
    test = GetIP('client')
print(test.get_all())
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 Cat Lan 的頭像
    Cat Lan

    Lancat Server 懶貓伺服器

    Cat Lan 發表在 痞客邦 留言(0) 人氣()