用自己电脑搭建外网可访问的服务器(转)
原文链接: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系统的,安装一些硬盘什么的可以直接就作为服务器了,都不需要再映射