固定链接:https://www.zybuluo.com/Tyhj/note/1018828

今天突然想到一个存在很久的疑问,服务器和普通电脑有什么不同呢?在我看来最大的区别就是服务器有固定的IP,自己电脑的IP是变化。

就我们寝室来讲,首先你在Windows上面获取的192.168.xx.xx这个是本地IP,是路由器分配的,连到同一个路由器上的电脑可以通过这个来访问(同一个局域网内)其他电脑,前提是访问的电脑提供了服务,同理,在同一个局域网内,把一台电脑作为服务器,其他电脑根据IP来访问是没问题的(有时候电脑开启了防火墙也会访问不到,关了就好了)。

那么外网怎么访问呢?首先PC的外网IP是变化,但是一般不重启路由器什么的,不会经常变化。通过这个网站我们可以看到电脑当前的外网IP。而且,你会发现同一个路由器下面的电脑外网IP都是一样的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
//获取外网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能不能访问到PC呢,其实没有这样简单。首先,这个外网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好像没文档,看看示例代码能猜出用法,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
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秒生效。这样纸搞,理论上讲,你就具备把一台电脑作为服务器的技术了,我感觉还是很有用的。

但是,事情没有这样简单,这么流行收费软件是有原因的。我做完上面的步骤还是不行,外网还是没办法访问服务器,我检查了很久,发现路由器显示的IP和我获取到的外网IP不一样,理论上都应该是外网IP,应该一样的。用代码获取到的IP肯定是外网IP,那路由器上显示的IP就不是外网IP,我百度了一下:

1
2
3
4
5
6
7
如果你在路由器中查看到的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直接崩了。

1
centos7重启ssh服务的命令为 service sshd restart

那其实可以看到,我们的电脑没有固定的IP,服务商甚至都不给我们外网IP,其实我们去找服务商买一个固定的IP,那这个连接的电脑就可以当做服务器使用了。我去淘宝上看了一下,还真的有固定IP买,一个小盒子300块,使用固定IP一个月20,连上电脑和路由器就好了,感觉可以试一下,比起使用其他地方买的还是好得多。

现在的大部分技术是IPV4,所以静态IP稀缺,导致需要付费使用静态IP,在以后IPV6的使用,几乎可以让地球上每一个人都有一个属于自己的静态IP。

Contents