[关闭]
@Tyhj 2018-01-15T20:24:33.000000Z 字数 5242 阅读 13586

用自己电脑搭建外网可访问的服务器

linux


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

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

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

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

  1. //获取外网IP
  2. public static String getV4IP() {
  3. String ip = "";
  4. String chinaz = "http://ip.chinaz.com";
  5. StringBuilder inputLine = new StringBuilder();
  6. String read = "";
  7. URL url = null;
  8. HttpURLConnection urlConnection = null;
  9. BufferedReader in = null;
  10. try {
  11. url = new URL(chinaz);
  12. urlConnection = (HttpURLConnection) url.openConnection();
  13. in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), "UTF-8"));
  14. while ((read = in.readLine()) != null) {
  15. inputLine.append(read + "\r\n");
  16. }
  17. //System.out.println(inputLine.toString());
  18. } catch (MalformedURLException e) {
  19. e.printStackTrace();
  20. } catch (IOException e) {
  21. e.printStackTrace();
  22. } finally {
  23. if (in != null) {
  24. try {
  25. in.close();
  26. } catch (IOException e) {
  27. // TODO Auto-generated catch block
  28. e.printStackTrace();
  29. }
  30. }
  31. }
  32. Pattern p = Pattern.compile("\\<dd class\\=\"fz24\">(.*?)\\<\\/dd>");
  33. Matcher m = p.matcher(inputLine.toString());
  34. if (m.find()) {
  35. String ipstr = m.group(1);
  36. ip = ipstr;
  37. }
  38. return ip;
  39. }

那通过这个外网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. private static IAcsClient client = null;
  2. String regionId = "cn-hangzhou"; //必填固定值,必须为“cn-hanghou”
  3. String accessKeyId = "xxxxxxx"; // your accessKey
  4. String accessKeySecret = "xxxxxxx";// your accessSecret
  5. public void updateDns() {
  6. IClientProfile profile = DefaultProfile.getProfile(regionId, accessKeyId, accessKeySecret);
  7. client = new DefaultAcsClient(profile);
  8. DescribeSubDomainRecordsRequest recordsRequest = new DescribeSubDomainRecordsRequest();
  9. recordsRequest.setSubDomain("one.yorhp.com");//设置域名
  10. DescribeSubDomainRecordsResponse recordsResponse;
  11. //request.setProtocol(ProtocolType.HTTPS); //指定访问协议
  12. //request.setAcceptFormat(FormatType.JSON); //指定api返回格式
  13. //request.setMethod(MethodType.POST); //指定请求方法
  14. //request.setRegionId("cn-hangzhou");//指定要访问的Region,仅对当前请求生效,不改变client的默认设置。
  15. try {
  16. recordsResponse = client.getAcsResponse(recordsRequest);
  17. List<DescribeSubDomainRecordsResponse.Record> recordList = recordsResponse.getDomainRecords();
  18. for (DescribeSubDomainRecordsResponse.Record record : recordList) {
  19. String oldIp = record.getValue();
  20. String outter_ip = IpAddress.getV4IP();
  21. if (!oldIp.equals(outter_ip)) {
  22. UpdateDomainRecordRequest udr_req = new UpdateDomainRecordRequest();
  23. udr_req.setRecordId(record.getRecordId());
  24. udr_req.setRR(record.getRR());
  25. udr_req.setValue(outter_ip);
  26. udr_req.setType(record.getType());
  27. udr_req.setTTL(record.getTTL());
  28. udr_req.setPriority(record.getPriority());
  29. udr_req.setLine(record.getLine());
  30. UpdateDomainRecordResponse udr_resp = new UpdateDomainRecordResponse();
  31. udr_resp = client.getAcsResponse(udr_req);
  32. System.out.println("重新解析域名成功:"+outter_ip);
  33. } else {
  34. System.out.println("域名未改变:"+outter_ip);
  35. }
  36. }
  37. } catch (ServerException e) {
  38. e.printStackTrace();
  39. } catch (ClientException e) {
  40. e.printStackTrace();
  41. }

其中还有个解析生效时间的问题,阿里上一般是10分钟,也就是说你的电脑作为服务器可能会崩溃10分钟,那不好。可以升级一下解析,好像是买一次就好了,我将近600天,50块,每次解析1秒生效。这样纸搞,理论上讲,你就具备把一台电脑作为服务器的技术了,我感觉还是很有用的。

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

  1. 如果你在路由器中查看到的WANIP地址,和外网的IP地址不一样。这种情况是宽带运营商,给你分配的一个内网IP地址;即你路由器WANIP地址是一个内网IP地址,很多个宽带用户,共同使用一个外网IP地址上网。
  2. 之所以出现宽带运营商,给大家分配内网IP地址,让多个宽带账号共享一个外网IP地址上网,应该是IPv4地址不够用的原因。所以,宽带运营商才会才去这种措施,让多个用户共享一个外网IP地址。
  3. 这情况实际上和我们自己使用路由器上网一样的,我们电脑、手机上获取的是路由器分配的一个内网IP地址,最总多台电脑、手机共同使用路由器中的WANIP地址上网。
  4. 一般来说,WANIP和外网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。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注