原文鏈接:https://www.jianshu.com/p/3c43afeb9cb1
IP地址
今天突然想到一個存在很久的疑問,服務器和普通電腦有什么不同呢?在我看來最大的區別就是服務器有固定的IP,自己電腦的IP是變化。
就我們寢室來講,首先你在Windows上面獲取的192.168.xx.xx這個是本地IP,是路由器分配的,連到同一個路由器上的電腦可以通過這個來訪問(同一個局域網內)其他電腦,前提是訪問的電腦提供了服務,同理,在同一個局域網內,把一台電腦作為服務器,其他電腦根據IP來訪問是沒問題的(有時候電腦開啟了防火牆也會訪問不到,關了就好了)。
那么外網怎么訪問呢?首先PC的外網IP是變化,但是一般不重啟路由器什么的,不會經常變化。通過這個網站我們可以看到電腦當前的外網IP。而且,你會發現同一個路由器下面的電腦外網IP都是一樣的。
通過IP訪問自己電腦
那通過這個外網IP能不能訪問到自己電腦呢,其實沒有這樣簡單。首先,這個外網IP可以算作是路由器的IP,所以意思就是只能訪問到路由器,想要訪問到路由器下的電腦上,那么要進行端口映射。端口映射很簡單,路由器基本自帶的功能,路由器設置一下,比如你的電腦本地IP是192.168.31.198(可以在路由器設置固定地址),你的程序端口是8080,那么添加一條端口映射規則,外部、內部端口設置8080,內部IP設置192.168.31.198,就可以了。或者開啟DMZ,開啟DMZ功能可以將內網某一個設備的IP映射到外網,方便從外網訪問到該設備,就是相當於把這個設備當做路由器一樣,外網可以直接訪問。
使用域名訪問
那理論上這樣做外網是可以訪問自己的電腦了,但是作為服務器,你的IP始終在變化,那沒法用。比如APP,可以想一些辦法,比如IP變了,我們下發通知APP相應改變,但是服務器IP都變了,APP根本沒法連接服務器,就無法更改內容;可以把IP存在其他服務器上,自己電腦IP變了,就發送到其他服務器,APP每次都從其他服務器先獲取IP,這樣有點麻煩了,還需要其他服務器。
其實有很多軟件可以做到這件事包括我聽的有點多的花生殼,但是收費,不收費就限制你的流量什么的,算了我還是不用了。但是它的解決方案比較有意思,它是賣一個域名給你,通過動態解析域名來實現。具體就是,域名需要解析到一個公網IP才能使用,使用方法和IP地址沒什么兩樣就是好記。當IP改變的時候我重新解析域名到新的IP地址,那不管外網IP怎么變我的域名永遠是指向我的電腦的外網IP的,可以通過域名來訪問我們的電腦
域名動態解析
動態解析叫DDNS,域名不貴,我在阿里雲買了兩個,一年50塊,我網上查了一下,阿里是有API調用來解析域名的,看看文檔,申請APPKey什么的。然后下載它的SDK,運行,非常棒,寫個程序,隔幾分鍾獲取一次電腦的外網IP,然后獲取阿里的解析記錄的IP,一樣則證明IP沒有變,不處理,不一樣說明IP變了,重新設置解析,DDNS完事。SDK好像沒文檔,看看示例代碼能猜出用法,如下:
private static IAcsClient client = null; String regionId = "cn-hangzhou"; //必填固定值,必須為“cn-hanghou” String accessKeyId = "xxxxxxx"; // your accessKey String accessKeySecret = "xxxxxxx";// your accessSecret public void updateDns() { IClientProfile profile = DefaultProfile.getProfile(regionId, accessKeyId, accessKeySecret); client = new DefaultAcsClient(profile); DescribeSubDomainRecordsRequest recordsRequest = new DescribeSubDomainRecordsRequest(); recordsRequest.setSubDomain("one.yorhp.com");//設置域名 DescribeSubDomainRecordsResponse recordsResponse; //request.setProtocol(ProtocolType.HTTPS); //指定訪問協議 //request.setAcceptFormat(FormatType.JSON); //指定api返回格式 //request.setMethod(MethodType.POST); //指定請求方法 //request.setRegionId("cn-hangzhou");//指定要訪問的Region,僅對當前請求生效,不改變client的默認設置。 try { recordsResponse = client.getAcsResponse(recordsRequest); List<DescribeSubDomainRecordsResponse.Record> recordList = recordsResponse.getDomainRecords(); for (DescribeSubDomainRecordsResponse.Record record : recordList) { String oldIp = record.getValue(); String outter_ip = IpAddress.getV4IP(); if (!oldIp.equals(outter_ip)) { UpdateDomainRecordRequest udr_req = new UpdateDomainRecordRequest(); udr_req.setRecordId(record.getRecordId()); udr_req.setRR(record.getRR()); udr_req.setValue(outter_ip); udr_req.setType(record.getType()); udr_req.setTTL(record.getTTL()); udr_req.setPriority(record.getPriority()); udr_req.setLine(record.getLine()); UpdateDomainRecordResponse udr_resp = new UpdateDomainRecordResponse(); udr_resp = client.getAcsResponse(udr_req); System.out.println("重新解析域名成功:"+outter_ip); } else { System.out.println("域名未改變:"+outter_ip); } } } catch (ServerException e) { e.printStackTrace(); } catch (ClientException e) { e.printStackTrace(); }
其中還有個解析生效時間的問題,阿里上一般是10分鍾,也就是說你的電腦作為服務器可能會崩潰10分鍾,那不好。可以升級一下解析,好像是買一次就好了,我將近600天,50塊,每次解析1秒生效。這樣紙搞,理論上講,你就具備把一台電腦作為服務器的技術了,我感覺還是很有用的。
Java獲取IP地址
通過下面的代碼可以獲取外網IP,應該就是通過訪問另一個服務器上的接口來獲取自己的IP地址,然后再返給我們,有個問題是"http://ip.chinaz.com"
這個接口是別人的,隨時可能會被關掉,所以需要在網上找找其他方法,原理應該都是一樣的,只是返回數據不同,解析方式不同而已
//獲取外網IP public static String getV4IP() { String ip = ""; String chinaz = "http://ip.chinaz.com"; StringBuilder inputLine = new StringBuilder(); String read = ""; URL url = null; HttpURLConnection urlConnection = null; BufferedReader in = null; try { url = new URL(chinaz); urlConnection = (HttpURLConnection) url.openConnection(); in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), "UTF-8")); while ((read = in.readLine()) != null) { inputLine.append(read + "\r\n"); } //System.out.println(inputLine.toString()); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (in != null) { try { in.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } Pattern p = Pattern.compile("\\<dd class\\=\"fz24\">(.*?)\\<\\/dd>"); Matcher m = p.matcher(inputLine.toString()); if (m.find()) { String ipstr = m.group(1); ip = ipstr; } return ip; }
運營商分配IP
但是,事情沒有這樣簡單,這么流行收費軟件是有原因的。我做完上面的步驟還是不行,外網還是沒辦法訪問服務器,我檢查了很久,發現路由器顯示的IP和我獲取到的外網IP不一樣,理論上都應該是外網IP,應該一樣的。用代碼獲取到的IP肯定是外網IP,那路由器上顯示的IP就不是外網IP,我百度了一下:
如果你在路由器中查看到的WAN口IP地址,和外網的IP地址不一樣。這種情況是寬帶運營商,給你分配的一個內網IP地址;即你路由器WAN口IP地址是一個內網IP地址,很多個寬帶用戶,共同使用一個外網IP地址上網。
之所以出現寬帶運營商,給大家分配內網IP地址,讓多個寬帶賬號共享一個外網IP地址上網,應該是IPv4地址不夠用的原因。所以,寬帶運營商才會才去這種措施,讓多個用戶共享一個外網IP地址。
這情況實際上和我們自己使用路由器上網一樣的,我們電腦、手機上獲取的是路由器分配的一個內網IP地址,最總多台電腦、手機共同使用路由器中的WAN口IP地址上網。
一般來說,WAN口IP和外網IP地址不一樣,並不會影響到我們的正常上網;不過在一些特殊網絡環境下,會影響到用戶的正常使用。例如在路由器中設置端口映射的時候,由於路由器的WAN口和外網IP地址不一樣,會導致端口映射失敗。
看到沒有,有這種情況,就是你的路由器本來就不是用的外網IP,相當於在你的路由器上面還有一個路由器,而且我們沒法在那里設置端口映射。有人說可以打電話叫服務商給你換一個外網IP,我感覺我學校是沒什么可能,我也沒試過,我感覺家里或者公司應該可以。
就是說如果你去剛才那個網站看了你的公網IP如果和你的路由器主界面設置賬號那里顯示的IP一樣的,那好恭喜你,上面那樣搞沒問題,很簡單,也非常好,你想想,阿里一個1G,1核,帶寬1M的服務器都是59一個月,你自己電腦帶寬100M,性能也好,多好,還免費。所以我有興趣來搞這個東西。
其他方法
那搞了一天白搞了?那不可能,可以看出來之前那個辦法已經沒有辦法實現了,真的是沒有辦法直接訪問自己的電腦了,那還有另一種說法,端口映射內網穿透。這篇文章寫的很詳細了,端口映射內網穿透方案探索。
其實呢,我看了一下,方法基本上就是通過一些服務來轉發請求吧,大概就是你的電腦一直連接另一個服務器,當另一個服務器有一些特定的請求的時候轉發給你的電腦,基本道理我覺得是這樣吧。那其實和之前那個方法真的是天壤之別了。速度肯定取決於這兩台服務器中最慢的一台了,反正感覺沒什么優勢。
我現在實現了最簡單的使用ssh端口轉發來做內網穿透。因為非常簡單,我試了一下。按照這篇文章使用ssh端口轉發來做內網穿透
,要下載一個xshell軟件,免費的。這樣的確可以映射成功,但是真的垃圾,玩玩做個網站什么的可以,作為什么文件服務器那不用想了,我寫了個下載文件的接口,xshell直接崩了。
centos7重啟ssh服務的命令為 service sshd restart
IPV6
那其實可以看到,我們的電腦沒有固定的IP,服務商甚至都不給我們外網IP,其實我們去找服務商買一個固定的IP,那這個連接的電腦就可以當做服務器使用了。
現在的大部分技術是IPV4,所以靜態IP稀缺,導致需要付費使用靜態IP,在以后IPV6的使用,幾乎可以讓地球上每一個人都有一個屬於自己的靜態IP。
在杭州辦了電信寬帶,發現有外網IP,直接映射一下的確成功了;祝大家也能成功
總結
重點就在於你的寬帶賬戶有沒有被分配公網IP,有的話,公網IP就相當於被設置在了你家接入網線的第一個路由器或者貓上面;誰都可以訪問這個公網IP,設置一下端口映射就可以把相應端口的訪問請求轉發到這個路由器下的電腦上,電腦自己來做處理,自己電腦就相當於一台服務器了。有些路由器是Linux系統的,安裝一些硬盤什么的可以直接就作為服務器了,都不需要再映射