樹莓派-家庭NAS(1) http://www.lxweimin.com/p/9be7ada37863
樹莓派-家庭NAS(2) http://www.lxweimin.com/p/91405ca824b8
樹莓派-家庭NAS(3) http://www.lxweimin.com/p/80777ed85246
內網穿透選型
上一篇文章中介紹了,家用NAS樹莓派的整體方案。在整體解決方案中說明的搭建整體方案的第一步是解決訪問問題。本文將主要介紹訪問解決方法。
內部網絡穿透技術可以分為NAT、DDNS、反向代理和VPN。這里就不介紹這些方式,可以查看參考中的內容進行了解。這里說明他們的大概工作原理用于選型工作。
-
NAT技術:
內網中的機器怎樣訪問公網上的網站?內網機器發送的數據可以經過Router轉發的公網上,那公網上返回的數據怎么到達內網的機器內,這個過程就是NAT技術。主要技術是通過動態端口映射技術完成。
NAT技術 -
DDNS技術:
DDNS即動態域名解析,是將用戶的動態IP地址映射到一個固定的域名解析服務上,用戶每次連接網絡的時候,客戶端程序就會通過信息傳遞把該主機的動態IP地址傳送給位于服務商主機上的服務器程序,服務程序負責提供DNS服務并實現動態域名解析。就是說DDNS捕獲用戶每次變化的IP地址,然后將其與域名相對應,這樣域名就可以始終解析到非固定IP的服務器上,互聯網用戶通過本地的域名服務器獲得網站域名的IP地址,從而可以訪問網站的服務。
DDNS技術 -
反向代理:
反向代理就是經常說的,有一臺公網服務器和私網服務器在一個網絡內。公網服務器上構建一個服務,把用戶請求轉發到死亡服務器上就可以了。
反向代理技術 -
VPN技術:
VPN技術就是將兩個私網通過隧道技術組合成一個子網。
VPN技術
方案選擇
本次我們的方向是家用NAS,所以,網絡環境也是家庭環境。所以,可能需要組合各種內網穿越技術才可以滿足整體要求。所以,現在開始組織方案。
在本方案中家用網絡使用的是電信網,電信網是有公網IP的。在沒有公網IP的通信服務商,請自行選擇其他方案。然后,再使用NAT技術的端口靜態映射,將樹莓派上的服務發布出去。使用DDNS技術的域名動態解析將路由器域名發布到域名上。
實現
- 先把樹莓派的內網IP設置為靜態IP。
- 實現上圖中的第七步,在路由器上設置端口映射。因為自家使用的路由器不一樣,我就不截圖了。幾乎所有的家用路由器都是支持端口映射的。
- 在樹莓派上編寫代碼,實現上圖中1,2,3步。我使用的是NETGEAR R6200V2,所以根據路由器的特點進行了公網IP的獲取工作。
- 配置定時執行過程。
定時執行(cron)代碼:
*/2 * * * * root /home/pi/update_public_ip.py > /dev/null 2>&1 &
更新域名IP代碼(python):
#!/usr/bin/python2.7
#-*-coding:utf-8-*-
import os
import sys
import httplib
import urllib2
import urllib
import base64
import cookielib
# 獲取網管地址,即路由器地址
def getGateway():
return "172.25.1.1"
# 獲取路由器上的公網IP。因為實在路由器上撥號上網的,所以從路由器上獲取公網IP
def getPublicIP(ip, user, password):
base64string = base64.b64encode('%s:%s' % (user, password))
headers = {"Host": ip,
"User-Agent": "Mozilla/5.0 (Windows NT 5.1; rv:52.0) Gecko/20100101 Firefox/52.0",
"Accept": "*/*",
"Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3",
"Accept-Encoding": "gzip, deflate",
"Referer": "https://172.25.1.1/RST_st_poe.htm",
"Connection": "keep-alive",
"Authorization":"Basic %s" % base64string}
# 登錄服務器,獲取Cookie
conn = httplib.HTTPConnection(ip, 80)
conn.request("GET", "", None, headers)
response = conn.getresponse()
cookie = response.getheader("set-cookie")
headers["Cookie"] = cookie
conn.close()
# 獲取公網IP地址所在頁面
conn = httplib.HTTPConnection(ip, 80)
conn.request("GET", "RST_st_poe.htm", None, headers)
response = conn.getresponse()
result = response.read()
# 解析頁面中內容,分解出IP地址
start_index = result.find("IP地址</B></td>")
start_index = result.find("<TD NOWRAP>", start_index)
end_index = result.find("</td>", start_index)
result = result[start_index:end_index]
result = result[len("<TD NOWRAP>"):]
conn.close()
# 登出路由器
conn = httplib.HTTPConnection(ip, 80)
conn.request("GET", "LGO_logout.htm", None, headers)
response = conn.getresponse()
conn.close()
# 返回IP地址
return result
# 更新二級域名的IP
def updateDomain(ip):
os.system("curl \"http://update.dnsexit.com/RemoteUpdate.sv?login=XXXXXX&password=XXXXXX&host=XX.XXX.X&myip=%s\"" % ip)
# 更新二級域名的主流程
if __name__ == "__main__":
public_ip = getPublicIP(getGateway(), "XXXXX", "XXXXX")
print "public ip : %s" % public_ip
updateDomain(public_ip)