一、IP地址的概念
IP地址由4个字节,32位二进制组成的一个逻辑上的地址。将32位分为4组,每一组是8个二进制。每一个位都代表不同的数字,即128、64、32、16、8、4、2、1,每个字节(八位二进制)将二进制转成十进制,IP地址通常用十进制分为四组来表示。
外网IP和内网IP的区别
1、外网IP是全球唯一的IP地址,仅分配给某一台网络设备。内网IP是由路由器分配给每一台设备内部使用的IP地址;
2、外网IP任何一台设备都可以ping通。内网IP只有在同一环境的内部设备才能ping通;
3、外网用户无法直接访问到内网用户,内网用户可以访问外网用户,因为内网的所有用户都是通过同一个外网IP进行上网的;
二、IP地址的网络号和主机号
从逻辑上将IP地址分为网络号和主机号两种,网络号代表着某一个完整的范围,主机号代表着某一个独立的主机,我们通常用这种方式来标识IP地址。就是在这一个范围里面的某一台独立的主机。由于一个IP地址总共是4个字节,也就是32位,所以它由网络号和主机号来组成的话,它的网络号的范围越大,它容纳的主机就越多,它的网络号越小,它容纳的主机号就越少。
如IP地址:192.168.12.54
域名:除了顶级域名,还有二级域名(SLD,second-level domain),就是最靠近顶级域名左侧的字段。如:zh.wikipedia.org中,wikipedia就是二级域名(有资料认为, 在顶级域名后面, 还存在一级域名, 那么zh就是二级域名)。
三、IP地址的分类
Internet组织机构定义了五种IP地址,能够给计算机使用的IP地址有A、B、C三类地址(针对公网IP),DE是作为保留的。A类要求第一个字节即前八位中第一位一定是0,B类要求第一个字节即前八位中前两位一定是10,C类要求第一个字节即前八位中前三位位一定是110,所以A类第一个字节的范围是0~127,因为后面七位相加为127。B类第一个字节的范围为128~191,C类第一个字节的范围为192~224,所以可以根据地址192.168.12.54可以判断它是C类地址。
注意:根据第一个字节的范围来判断是A\B\C三类地址中的哪一个是针对公网IP的,如果是内网IP则不是,内网IP可以随便设置,如内网的IP可以设置10.10.53.207。
为什么要将IP地址划分为ABC三类呢?因为它还会对网络号和主机号进行区分。A类会让第一个字节作为网络号,后面三个字节作为主机号,由于网络号最大为255,它就有1-255种可能,就可以划分255个网段。A类可以容纳255x255x255=16581375个主机,也就是后面三个字节相乘的主机数。这个范围非常大,即1600多万的IP地址,足够一个非常大的直辖市区使用了。如果没有这么高的需求,比如只有几百个、几千个或几万个IP地址的需求,肯定会造成IP地址的范围性的浪费,所以又划分了一个B类。B类是前面两个字节作为网络号,后面两个字节作为主机号。B类网络有255x255个网络,每个网络有255x255个IP地址。C类将前三个字节作为网络号,后面一个字节作为主机号,所以C类会有16581375个网段,每个网段会给256个IP地址。
IP地址的分类是为了解决实际生活种一些应用的问题,也可以杜绝IP地址不必要的浪费,
四、IP地址的子网掩码
划分子网后,通过使用掩码,把子网隐藏起来,使得从外部看网络没有变化,这就是子网掩码。
为什么会出现子网掩码呢?
IP地址由四组组成,但是你怎么区分哪些是网络号,哪些是主机号呢?子网掩码就是配合IP地址来区分网络号和主机号的。
子网掩码也是32位二进制数,也分为4组,每组也是8位。其对应网络地址的所有位置都为1,对应于主机地址的所有位置都为0。
A类网络的默认子网掩码是255.0.0.0,B类网络的默认子网掩码是255.255.0.0,C类网络的默认子网掩码是255.255.255.0。
如192.168.12.54是C类地址,C类网络的子网掩码是255.255.255.0,由于C类前三个字节作为网络号,
11111111 11111111 11111111 00000000
192.168.12.54的二进制表示:
11000000 10101000 00001100 00110110
将子网掩码和IP地址按位进行逻辑“与”运算,得到IP地址的网络地址,剩下的部分就是主机地址,从而区分出任意IP地址中的网络地址和主机地址。
11000000 10101000 00001100 00000000
十进制形式为:192.168.12.0
子网掩码常用点分十进制表示,我们还可以用CIDR的网络前缀法表示掩码,即“/<网络地址位数>;”。如138.96.0.0/16表示B类网络138.96.0.0的子网掩码为255.255.0.0。
子网掩码告知路由器,IP地址的前多少位是网络地址,后多少位(剩余位)是主机地址,使路由器正确判断任意IP地址是否是本网段的,从而正确地进行路由。
例如,有两台主机,主机一的IP地址为222.21.160.6,子网掩码为255.255.255.192,主机二的IP地址为222.21.160.73,子网掩码为255.255.255.192。主机一要给主机二发送数据,先要判断两个主机是否在同一网段。
同一网段:同一网段指的是IP地址和子网掩码相与得到相同的网络地址。
C类地址判断前三位是否相同,即可确定2个IP地址是否在同一网段内,但本例中的222.21.160.6与222.21.160.73不在同一网段,因为这两个C类IP地址已经做了子网划分就不能只判断前三个字节是否相同就确认这两个IP是否在同一网段。其中222.21.160.6在222.21.160.1-222.21.160.63 段,222.21.160.73在222.21.160.64-222.21.160.127 段,所以不在同一网段,如果要通信需要通过路由器转发。
如果子网掩码为255.255.254.0时,255.255.254.0表示前23位是网络号,后9位是主机号
网段计算如下:
子网掩码:255.255.254.0
IP:10.34.8.7
11111111 11111111 11111110 00000000
00001010 00100010 00001000 00000111
按位与结果:
00001010 00100010 00001000 00000000
网段:10.34.8.0,10.34.9.0
五、子网划分的基本概念
以192.168.52.12为例,它是一个C类地址,由四个字节组成,C类网络的前三个字节都是网络号,后面一个字节为主机号,C类网络有1600多万个网段,每个网段可以连接256台主机。如果一个公司需要100个IP地址,但是又不需要256个这么多,如果给一个C类的网段就会造成156个IP的浪费,为了解决这个问题就出现了子网划分的概念。
C类是三个字节的网络号,最后一个字节是主机号,我们可以在主机号中按位进行子网络的划分,就可以让网络变得更加的灵活。即在已有的主机号中再划分一个子网号,将一个C类的网段划分成逻辑上一个更小的地址。将主机号的最前面一位作为子网号,剩下的主机号只剩下7位了。子网号可能是1,也可能是0,当子网号为1的时候,为256个IP地址,当为0的时候为128个IP地址。如果进行这样的一个划分,那128离100的目标就更近了。如果公司只需要50个IP地址,则将前两位作为子网号,剩下6位作为主机号,前面两位会出现四种情况:00、01、10、11,后面6位对应64个IP地址,即每个子网可以连接64台设备,这样就变得更加的灵活了。
A类第一个字节为网络号,一个字节为8,B类前两个字节为网络号,两个字节为16,C类前三个字节为网络号,三个字节为24,如果再划一位的话,我将这个子网掩码就变成了9、17、25,
子网计算方式:
以C类地址192.168.1.0为例,子网划分都是在主机号上面按位给网络号划分一个新的子网号,现在的主机号是子网号+主机号。
我们把最后一个字节的十进制转成二进制,即00000000,比如说我们需要两个网络的话,我们会将前面第一位作为子网号,第一位代表的两个网络是0和1;如果需要4个的话,就会划分前两位,就会出现00 01 10 11这四种可能;如果需要8个网络的话,就划分前三位,需要18个的话,就划分前4位。我们根据自己划分网络的需求来进行基本的划分,因为C类一个网段可以连接256台主机,如果划分为两个网段,可以连接128台设备,如果划分位4个网段,就可以连接64台设备,如果划分为8个网段,就可以连接32台设备。
前面第一位划分为子网号,后面7为主机号。如果第一位为0,范围为0~127,共128个IP;如果第一位为1,范围为128-~255;这样的话,第一个子网网段的范围:192.168.1.0~127,第二个子网段的范围为:192.168.1.128~255。
如果划分为4个网段的话,00代表0,01代表64,10代表128,11代表192,即第一个网段的范围为:192.168.1.0~63,第二个网段的范围为:192.168.1.64~127,第三个网段的范围为:192.168.1.128~191,第四个网段的范围为:192.168.1.192~255,每个网段允许连接64个设备。
原有的网络号为24(3个字节)位,现将主机号的前两位划分为子网号(即多了2位),后面6位为主机号,我们将子网号一起算入到网络号中,那么这一个IP地址的子网掩码长度为26(位),现在的子网掩码不是255.255.255.0了,由于加了2位,而是255.255.255.192,192为128+64,这个子网掩码中所有转换成二进制的1对应的IP地址都是网络位,子网位也是算在网络位中的。
注意:我们通过外网访问别人的服务器,别人获取的是我们的公网的IP,而不是你内网的IP。
五、java获取IP的 工具类
public class HttpUtils { private static Logger logger = LoggerFactory.getLogger(HttpUtils.class); /** * get * * @param host * @param path * @param method * @param headers * @param querys * @return * @throws Exception */ public static HttpResponse doGet(String host, String path, String method, Map<String, String> headers, Map<String, String> querys) throws Exception { HttpClient httpClient = wrapClient(host); HttpGet request = new HttpGet(buildUrl(host, path, querys)); for (Map.Entry<String, String> e : headers.entrySet()) { request.addHeader(e.getKey(), e.getValue()); } return httpClient.execute(request); } /** * post form * * @param host * @param path * @param method * @param headers * @param querys * @param bodys * @return * @throws Exception */ public static HttpResponse doPost(String host, String path, String method, Map<String, String> headers, Map<String, String> querys, Map<String, String> bodys) throws Exception { HttpClient httpClient = wrapClient(host); HttpPost request = new HttpPost(buildUrl(host, path, querys)); for (Map.Entry<String, String> e : headers.entrySet()) { request.addHeader(e.getKey(), e.getValue()); } if (bodys != null) { List<NameValuePair> nameValuePairList = new ArrayList<NameValuePair>(); for (String key : bodys.keySet()) { nameValuePairList.add(new BasicNameValuePair(key, bodys.get(key))); } UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(nameValuePairList, "utf-8"); formEntity.setContentType("application/x-www-form-urlencoded; charset=UTF-8"); request.setEntity(formEntity); } return httpClient.execute(request); } /** * Post String * * @param host * @param path * @param method * @param headers * @param querys * @param body * @return * @throws Exception */ public static HttpResponse doPost(String host, String path, String method, Map<String, String> headers, Map<String, String> querys, String body) throws Exception { HttpClient httpClient = wrapClient(host); HttpPost request = new HttpPost(buildUrl(host, path, querys)); for (Map.Entry<String, String> e : headers.entrySet()) { request.addHeader(e.getKey(), e.getValue()); } if (StringUtils.isNotBlank(body)) { request.setEntity(new StringEntity(body, "utf-8")); } return httpClient.execute(request); } /** * Post stream * * @param host * @param path * @param method * @param headers * @param querys * @param body * @return * @throws Exception */ public static HttpResponse doPost(String host, String path, String method, Map<String, String> headers, Map<String, String> querys, byte[] body) throws Exception { HttpClient httpClient = wrapClient(host); HttpPost request = new HttpPost(buildUrl(host, path, querys)); for (Map.Entry<String, String> e : headers.entrySet()) { request.addHeader(e.getKey(), e.getValue()); } if (body != null) { request.setEntity(new ByteArrayEntity(body)); } return httpClient.execute(request); } /** * Put String * @param host * @param path * @param method * @param headers * @param querys * @param body * @return * @throws Exception */ public static HttpResponse doPut(String host, String path, String method, Map<String, String> headers, Map<String, String> querys, String body) throws Exception { HttpClient httpClient = wrapClient(host); HttpPut request = new HttpPut(buildUrl(host, path, querys)); for (Map.Entry<String, String> e : headers.entrySet()) { request.addHeader(e.getKey(), e.getValue()); } if (StringUtils.isNotBlank(body)) { request.setEntity(new StringEntity(body, "utf-8")); } return httpClient.execute(request); } /** * Put stream * @param host * @param path * @param method * @param headers * @param querys * @param body * @return * @throws Exception */ public static HttpResponse doPut(String host, String path, String method, Map<String, String> headers, Map<String, String> querys, byte[] body) throws Exception { HttpClient httpClient = wrapClient(host); HttpPut request = new HttpPut(buildUrl(host, path, querys)); for (Map.Entry<String, String> e : headers.entrySet()) { request.addHeader(e.getKey(), e.getValue()); } if (body != null) { request.setEntity(new ByteArrayEntity(body)); } return httpClient.execute(request); } /** * Delete * * @param host * @param path * @param method * @param headers * @param querys * @return * @throws Exception */ public static HttpResponse doDelete(String host, String path, String method, Map<String, String> headers, Map<String, String> querys) throws Exception { HttpClient httpClient = wrapClient(host); HttpDelete request = new HttpDelete(buildUrl(host, path, querys)); for (Map.Entry<String, String> e : headers.entrySet()) { request.addHeader(e.getKey(), e.getValue()); } return httpClient.execute(request); } private static String buildUrl(String host, String path, Map<String, String> querys) throws UnsupportedEncodingException { StringBuilder sbUrl = new StringBuilder(); sbUrl.append(host); if (!StringUtils.isBlank(path)) { sbUrl.append(path); } if (null != querys) { StringBuilder sbQuery = new StringBuilder(); for (Map.Entry<String, String> query : querys.entrySet()) { if (0 < sbQuery.length()) { sbQuery.append("&"); } if (StringUtils.isBlank(query.getKey()) && !StringUtils.isBlank(query.getValue())) { sbQuery.append(query.getValue()); } if (!StringUtils.isBlank(query.getKey())) { sbQuery.append(query.getKey()); if (!StringUtils.isBlank(query.getValue())) { sbQuery.append("="); sbQuery.append(URLEncoder.encode(query.getValue(), "utf-8")); } } } if (0 < sbQuery.length()) { sbUrl.append("?").append(sbQuery); } } return sbUrl.toString(); } private static HttpClient wrapClient(String host) { HttpClient httpClient = new DefaultHttpClient(); if (host.startsWith("https://")) { sslClient(httpClient); } return httpClient; } private static void sslClient(HttpClient httpClient) { try { SSLContext ctx = SSLContext.getInstance("TLS"); X509TrustManager tm = new X509TrustManager() { public X509Certificate[] getAcceptedIssuers() { return null; } public void checkClientTrusted(X509Certificate[] xcs, String str) { } public void checkServerTrusted(X509Certificate[] xcs, String str) { } }; ctx.init(null, new TrustManager[] { tm }, null); SSLSocketFactory ssf = new SSLSocketFactory(ctx); ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); ClientConnectionManager ccm = httpClient.getConnectionManager(); SchemeRegistry registry = ccm.getSchemeRegistry(); registry.register(new Scheme("https", 443, ssf)); } catch (KeyManagementException ex) { throw new RuntimeException(ex); } catch (NoSuchAlgorithmException ex) { throw new RuntimeException(ex); } } /** * 获取ip地址 * * @param request * @return */ public static String getIpAddr(HttpServletRequest request) { String ipAddress; try { ipAddress = request.getHeader("x-forwarded-for"); if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) { ipAddress = request.getHeader("Proxy-Client-IP"); } if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) { ipAddress = request.getHeader("WL-Proxy-Client-IP"); } if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) { ipAddress = request.getRemoteAddr(); if (ipAddress.equals("127.0.0.1")) { // 根据网卡取本机配置的IP InetAddress inet = null; try { inet = InetAddress.getLocalHost(); } catch (UnknownHostException e) { e.printStackTrace(); logger.error("获取IP地址发生异常:", e); } ipAddress = inet.getHostAddress(); } } // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割 if (ipAddress != null && ipAddress.length() > 15) { // "***.***.***.***".length() // = 15 if (ipAddress.indexOf(",") > 0) { ipAddress = ipAddress.substring(0, ipAddress.indexOf(",")); } } } catch (Exception e) { ipAddress = ""; } return ipAddress; } }
调用工具类
先引入HttpServletRequest
@Autowired private HttpServletRequest httpServletRequest;
再调用工具类的getIpAddr方法:
String ip = HttpUtils.getIpAddr(httpServletRequest);
注意:该方法获取的IP,如果是内网访问,那么获取的IP为内网IP;如果是外网访问,那么获取的IP为外网IP。