在服务器端,经常会统计服务器上的资源被哪些国家或者地区访问的比较多,但我们唯一能记录的只有客户端下载时的ip地址,那我们怎么通过ip地址得到客户所在的国家或地区名呢?下面作以详解:
1.获取客户ip地址:
获取客户端的IP地址的通用方法是:request.getRemoteAddr(),这种方法在大部分情况下都是有效的。但是在通了 Apache,Squid等反向代理软件(例如:nginx反向代理服务器)就不能获取到客户端的真实IP地址了,也就是说客户端和服务之间增加了中间层。例如得到的是127.0.0.1 或 192.168.1.110,不是客户的真实ip,而是反向代理服务器(中间层)的ip地址;
此时我们可以这样获取ip地址
public String getRemortIP(HttpServletRequest request) {
if (request.getHeader("x-forwarded-for") == null) {
return request.getRemoteAddr();
}
return request.getHeader("x-forwarded-for");
}
可是当我访问http://www.5a520.cn /index.jsp/ 时,返回的IP地址始终是unknown,也并不是如上所示的127.0.0.1 或 192.168.1.110了,而我访问http://192.168.1.110:2046/index.jsp 时,则能返回客户端的真实IP地址,写了个方法去验证。原因出在了Squid上。squid.conf 的配制文件 forwarded_for 项默认是为on,如果 forwarded_for 设成了 off 则:X-Forwarded-For: unknown:
此时我们可以这样获取ip地址
public String getIpAddr(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
可是,如果通过了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串Ip值,究竟哪个才是真正的用户端的真实IP呢?
答案是取X-Forwarded-For中第一个非unknown的有效IP字符串。
通过上面的讲解我们可以获取客户端的ip地址
2.通过ip地址获取国家或地区:
首先:ip地址是由四个八位的二进制组成(例如192.168.15.206 等价于 11000000.10101000.00001111.11001110),中间用点号隔开,我们可以把中间的点号去掉之后(即11000000101010000000111111001110),将其转化为十进制数值(即3232239566),那么这个十进制数就是唯一的,那么也就是说我可以通过这个十进制数代替ip地址:
下面是转化代码:返回值即为十进制数值;
public class CountryCode {
public static void main(String[] args) {
// TODO Auto-generated method stub
String countryCode = getCountryCode("112.91.81.82"); //169.23.123.89
System.out.println(countryCode);
}
public static String getCountryCode(String IPAddress){
StringBuffer buffer = new StringBuffer(IPAddress);
int index = buffer.indexOf(",");
if(index > 0){
int length = buffer.length();
buffer = buffer.delete(index, length);
}
StringTokenizer tokens = new StringTokenizer(buffer.toString(), ".", false);
long answer = 0L;
int counter = 3;
while(tokens.hasMoreTokens() && counter >= 0){
long read = new Long(tokens.nextToken()).longValue();
long calculated = new Double(read *(Math.pow(256, counter))).longValue();
answer += calculated;
counter --;
}
Long IPValue = new Long(answer);
return IPValue.toString();
}
}
其次:既然已经知道了ip地址所对应的数值唯一,那么如果我们有这么一张表,表中包含地区,及地区所在的ip对应的数值范围,那通过这张表,给定任一个ip不就能得到其所对应的国家或地区了吗?简单吧。
我们先下载这张表(地址:http://software77.net/geo-ip/)
下载完毕之后,打开表,向后拉几页之后,就是上面所说的对照表了(包括ipfrom ipto country等列项),给定任一个ip将其转化成十进制数值,在表中查找,如果他在某个ip范围区间内,那么对应的地区就是ip所在地区。
其实原理也就是上面所说的,但是我们不可能每次都从这张表里一个一个找吧,因此下面就是如何把张表中我们需要的数据列导入到数据库中的问题,以方便通过java代码查找,而不是人工查找。
1.将表中所需要的项存入一个txt文件中:代码如下
public class FileChanger{
private StringBuffer buffer;
public FileChanger(String inputFile, String outputFile){
buffer = new StringBuffer();
go(inputFile, outputFile);
}
private void go(String inputFile, String outputFile){
try{
FileReader fileReader = new FileReader(inputFile);
BufferedReader reader = new BufferedReader(fileReader);
if(reader.ready()){
String read = reader.readLine();
while(read != null){
StringTokenizer tokens = new StringTokenizer(read, ",", false);
String from = tokens.nextToken();
String to = tokens.nextToken();
// A few entries we are not interested in before we get to
// the real country code
String countryCode = tokens.nextToken();
countryCode = tokens.nextToken();
countryCode = tokens.nextToken();
// Replace any occurences of UK with GB
if(countryCode.equalsIgnoreCase("\"UK\""))
countryCode = "GB";
String line = from + "\t" + to + "\t" + countryCode + "\n";
// Remove all quotes
line = line.replaceAll("\"", "");
buffer = buffer.append(line);
read = reader.readLine();
}
fileReader.close();
reader.close();
PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(outputFile)));
out.write(buffer.toString());
out.flush();
out.close();
}
}
catch(Exception e){
System.out.println("Exception: " + e.getMessage());
}
}
public static void main(String[] args){
FileChanger fileChanger = new FileChanger("D:\\IpToCountry.csv", "D:\\IpToCountry.txt");
}
}
进入mysql: mysql -uroot -p
mysql : show databases;//必须有分号;
mysql : use 要导入的数据的数据库名;
mysql : load data local infile "**.txt" into table 要导入的数据库的表名(列名);
至此基本结束,可能会出现以下错误,可以自行上网查找