1. 程式人生 > >純真資料庫理解及根據IP定位地區

純真資料庫理解及根據IP定位地區

  在系統中,檢視使用者的登入資訊是一個很常見的功能。我們往往會記錄下使用者計算機的IP和地理位置,然而IP地址記錄非常容易,但是地理位置相對來說較難。開始,菜鳥是想建立一個IP地址庫,根據IP地址庫查詢相應的地理位置。後來想有沒有一個相對完整的IP地址庫供菜鳥使用了,於是找到了一個QQwry.dat(純真資料庫)。然而,QQwry.dat是什麼呢?以及怎麼使用?
一、純真資料庫(QQwry.dat)
1.基本結構
        QQwry.dat檔案在結構上分為檔案頭、記錄區和索引區3部分。我們一般在使用時先從索引區查詢記錄偏移,根據記錄偏移在從記錄區中獲取。由於記錄區是不定長的,且比較多,因此,一般採用二分查詢法進行查詢。
2.檔案頭
        QQwry.dat檔案的標頭檔案結構非常簡單,為8個位元組。前4個位元組為第一條索引的絕對偏移,後4個位元組為最後一條索引的絕對偏移。
3.記錄區
        每條IP地址的記錄區都由國家和地區組成,但是在這裡國家和地區都不太明確,相對而言的。國家可能是指一所學校,地區可能指學校中的某一系。於是我們想著IP地址的記錄格式可能為:[IP地址][國家名稱][地區名稱]。
        國家和地區可能有很多重複,因此我們我可以用重定向來節約空間。其重定向有兩種方式:一種是直接用字串表示國家或地區;另一種是一個4位元組的結構,第1個位元組表示重定向的模式,後3個位元組表示國家名稱或地區名稱的實際偏移位置。
        重定向的模式分為兩種:一種是隻有國家,沒有地區,也就是說地區記錄跟著國家記錄走了,在IP地址之後只剩下國家記錄的4個位元組,後3個位元組是一個指標,指向了實際的國家名稱,其標識位元組為0X01。另一種是既有國家又有地址,即地區記錄沒有跟著國家記錄走。在4個位元組的國家記錄後還含有地區記錄,其標識位元組為0X02。
4.索引區
        通過了解“檔案頭”,我們可以瞭解到檔案頭實際上是兩個指標,分別指向檔案的第一條索引和最後一條索引的絕對偏移。我們可以根據標頭檔案定位到索引區,然後開始查詢IP。每條索引區為7個位元組,前4個位元組表示起始IP地址,後3個位元組表示結束IP地址。如222.11.0.1-222.11.0.240,222.11.0.1表示起始IP地址,222.11.0.240表示結束IP地址。若我們要查詢的IP地址在這個IP地址的範圍內,則根據這條索引區查詢國家和地區。二、例項
1.IP地址實體,包含起始、結束IP、國家名稱和地區名稱

package com.test.ip.entity;

public class IpEntity {
    private String startIp;// 起始IP
    private String endIp;// 結尾IP
    private String country;// 國家
    private String area;// 區域

    // 構造方法:清空資料資訊
    public IpEntity() {
        super();
        this.startIp = "";
        this.endIp = "";
        this.country = "";
        this.area = "";
    }

    // getter和setter方法提供屬性對外訪問介面
    public String getStartIp() {
        return startIp;
    }

    public void setStartIp(String startIp) {
        this.startIp = startIp;
    }

    public String getEndIp() {
        return endIp;
    }

    public void setEndIp(String endIp) {
        this.endIp = endIp;
    }

    public String getCountry() {
        return country;
    }

    public void setCountry(String country) {
        this.country = country;
    }

    public String getArea() {
        return area;
    }

    public void setArea(String area) {
        this.area = area;
    }

}


2.位置實體

package com.test.ip.entity;

/**
 * 封裝IP資訊,如國家和地區
 * 
 * @author aleyn
 *
 */
public class IpLocation {
    private String country;// 國家
    private String area;// 地區

    // 構造方法
    public IpLocation() {
        this.country = "";
        this.area = "";
    }

    public IpLocation getCopy() {
        IpLocation ipLocation = new IpLocation();
        ipLocation.setCountry(this.getCountry());
        ipLocation.setArea(this.getArea());
        return ipLocation;
    }

    // getter、setter
    public String getCountry() {
        return country;
    }

    public void setCountry(String country) {
        this.country = country;
    }

    public String getArea() {
        return area;
    }

    public void setArea(String area) {
        // 若為區域網,純真IP地址庫的地區會顯示CZ88.NET,去除
        if (area.trim().equals("CZ88.NET")) {
            this.area = "區域網";
        } else {
            this.area = area;
        }
    }

}


3.轉換工具類

package com.test.ip.util;

import java.util.StringTokenizer;

/**
 * 轉換工具
 * 
 * @author aleyn
 *
 */
public class ConvertUtils {
    private static StringBuffer sb = new StringBuffer();

    /**
     * IP字串轉位元組陣列
     * 
     * @param ip
     * @return
     */
    public static byte[] getIpArray(String ip) {
        byte[] buffer = new byte[4];
        StringTokenizer stringTokenizer = new StringTokenizer(ip, ".");
        try {
            buffer[0] = (byte) (Integer.parseInt(stringTokenizer.nextToken()) & 0xFF);
            buffer[1] = (byte) (Integer.parseInt(stringTokenizer.nextToken()) & 0xFF);
            buffer[2] = (byte) (Integer.parseInt(stringTokenizer.nextToken()) & 0xFF);
            buffer[3] = (byte) (Integer.parseInt(stringTokenizer.nextToken()) & 0xFF);
        } catch (Exception e) {
            e.printStackTrace(); 
        }
        return buffer;
    }

    /**
     * IP位元組陣列換字串
     * 
     * @param ip
     * @return
     */
    public static String getIpString(byte[] ip) {
        sb.delete(0, sb.length());
        sb.append(ip[0] & 0xFF);
        sb.append(".");
        sb.append(ip[1] & 0xFF);
        sb.append(".");
        sb.append(ip[2] & 0xFF);
        sb.append(".");
        sb.append(ip[3] & 0xFF);
        return sb.toString();
    }

    /**
     * 根據某種編碼將IP位元組陣列轉為字串
     * 
     * @param ip
     * @param offset
     * @param length
     * @param encode
     * @return
     */
    public static String getIpString(byte[] ip, int offset, int length,
            String encode) {
        try {
            return new String(ip, offset, length, encode);
        } catch (Exception e) {
            return new String(ip, offset, length);
        }
    }
}


4.讀取國家或地區工具類

package com.test.ip.util;

import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;

public class ReadUtils {
    /**
     * 從offset未知讀取一個4位元組為一個long java格式為big-endian
     * 
     * @param offset
     * @return
     */
    public static long readLongByFour(RandomAccessFile randomAccessFile,
            long offset) {
        long result = 0;
        try {
            randomAccessFile.seek(offset);
            result |= (randomAccessFile.readByte() & 0xFF);
            result |= ((randomAccessFile.readByte() << 8) & 0xFF00);
            result |= ((randomAccessFile.readByte() << 16) & 0xFF0000);
            result |= ((randomAccessFile.readByte() << 24) & 0xFF000000);
            return result;
        } catch (Exception e) {
            return -1;
        }
    }

    /**
     * 從offset位置開始讀取3個位元組為一個long
     * 
     * @param randomAccessFile
     * @param offset
     * @param buffer
     * @return
     */
    public static long readLongByThree(RandomAccessFile randomAccessFile,
            long offset, byte[] buffer) {
        long result = 0;
        try {
            randomAccessFile.seek(offset);
            randomAccessFile.readFully(buffer);
            result |= (buffer[0] & 0xFF);
            result |= ((buffer[1] << 8) & 0xFF00);
            result |= ((buffer[2] << 16) & 0xFF0000);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            return -1;
        }
    }

    /**
     * 從當前位置讀取3個位元組為一個long
     * 
     * @param randomAccessFile
     * @param buffer
     * @return
     */
    public static long readLongByThree(RandomAccessFile randomAccessFile,
            byte[] buffer) {
        long result = 0;
        try {
            randomAccessFile.readFully(buffer);
            result |= (buffer[0] & 0xFF);
            result |= ((buffer[1] << 8) & 0xFF00);
            result |= ((buffer[2] << 16) & 0xFF0000);
            return result;
        } catch (Exception e) {
            return -1;
        }
    }

    /**
     * 從記憶體對映的offset位置,開始3個位元組讀取一個int
     * 
     * @param offset
     * @return
     */
    public static int readIntByThree(MappedByteBuffer mappedByteBuffer,
            int offset) {
        mappedByteBuffer.position(offset);
        return mappedByteBuffer.getInt() & 0x00FFFFFF;
    }

    /**
     * 從記憶體對映的當前位置,開始3個位元組讀取一個int
     * 
     * @param mappedByteBuffer
     * @return
     */
    public static int readIntByThree(MappedByteBuffer mappedByteBuffer) {
        return mappedByteBuffer.getInt() & 0x00FFFFFF;
    }

    /**
     * 從offset位置讀取4個位元組的IP地址放入IP陣列中,讀取後的IP地址格式為big-endian
     * 
     * @param offset
     * @param ip
     */
    public static void readIp(RandomAccessFile randomAccessFile, long offset,
            byte[] ip) {
        try {
            randomAccessFile.seek(offset);
            randomAccessFile.readFully(ip);
            byte temp = ip[0];
            ip[0] = ip[3];
            ip[3] = temp;
            temp = ip[1];
            ip[1] = ip[2];
            ip[2] = temp;
        } catch (Exception e) {
            e.printStackTrace(); 
        }
    }

    /**
     * 從offset位置讀取一個以0結束的字串
     * 
     * @param offset
     * @return
     */
    public static String readString(RandomAccessFile randomAccessFile,
            long offset, byte[] buffer) {
        try {
            randomAccessFile.seek(offset);
            int i;
            for (i = 0, buffer[i] = randomAccessFile.readByte(); buffer[i] != 0; buffer[++i] = randomAccessFile
                    .readByte())
                ;
            if (i != 0) {
                return ConvertUtils.getIpString(buffer, 0, i, "GBK");
            }
        } catch (Exception e) {
            e.printStackTrace(); 
        }
        return "";
    }

    /**
     * 從offset位置讀取一個以0結束的字串
     * 
     * @param offset
     * @return
     */
    public static String readString(MappedByteBuffer mappedByteBuffer,
            int offset, byte[] buffer) {
        try {
            mappedByteBuffer.position(offset);
            int i;
            for (i = 0, buffer[i] = mappedByteBuffer.get(); buffer[i] != 0; buffer[++i] = mappedByteBuffer
                    .get())
                ;
            if (i != 0)
                return ConvertUtils.getIpString(buffer, 0, i, "GBK");
        } catch (IllegalArgumentException e) {
            e.printStackTrace(); 
        }
        return "";
    }

    /**
     * 從offset位置讀取4個位元組的IP地址放入IP陣列中,讀取後的IP地址格式為big-endian
     * 
     * @param mappedByteBuffer
     * @param offset
     * @param ip
     */
    public static void readIp(MappedByteBuffer mappedByteBuffer, int offset,
            byte[] ip) {
        try {
            mappedByteBuffer.position(offset);
            mappedByteBuffer.get(ip);
            byte temp = ip[0];
            ip[0] = ip[3];
            ip[3] = temp;
            temp = ip[1];
            ip[1] = ip[2];
            ip[2] = temp;
        } catch (Exception e) {
            e.printStackTrace(); 
        }
    }

    /**
     * 比較兩個位元組的大小
     * 
     * @param b1
     * @param b2
     * @return
     */
    private static int compareByte(byte b1, byte b2) {
        if ((b1 & 0xFF) > (b2 & 0xFF)) // 比較是否大於
            return 1;
        else if ((b1 ^ b2) == 0)// 判斷是否相等
            return 0;
        else
            return -1;
    }

    /**
     * 將要查詢的IP和起始的IP進行比較
     * 
     * @param ip
     * @param beginIp
     * @return
     */
    public static int compareIp(byte[] ip, byte[] beginIp) {
        for (int i = 0; i < 4; i++) {
            int j = compareByte(ip[i], beginIp[i]);
            if (j != 0) {
                return j;
            }
        }
        return 0;
    }
}


5.提示訊息

package com.test.ip.util;/**
 * 提示資訊
 * 
 * @author aleyn
 *
 */
public interface Message {
    public static final String BAD_IP_FILE = "IP地址庫檔案錯誤";
    public static final String UNKNOW_COUNTRY = "未知國家";
    public static final String UNKNOW_AREA = "未知區域";
}


6.IP檢視器

package com.test.ip.viewer;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteOrder;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.test.ip.entity.IpEntity;
import com.test.ip.entity.IpLocation;
import com.test.ip.util.ConvertUtils;
import com.test.ip.util.LogFactory;
import com.test.ip.util.Message;
import com.test.ip.util.ReadUtils;

public class IpViewer {
    private String fileName = "qqwry.dat";// 純真IP資料庫名
    private String fileDir = null;// 儲存的資料夾

    // 固定常量,如記錄長度
    private static final int IP_RECORD_LENGTH = 7;
    private static final byte REDIRECT_MODE_ONE = 0x01;
    private static final byte REDIRECT_MODE_TWO = 0x02;

    // 快取已經查詢過的IP地址,避免二次查詢,加快速度
    private Map<String, IpLocation> cache;
    // 隨機檔案訪問類
    private RandomAccessFile randomAccessFile;
    // 記憶體對映檔案
    private MappedByteBuffer mappedByteBuffer;
    // 起始地區的開始和結束絕對偏移量
    private long begin, end;

    // 為提高效率,臨時變數
    private IpLocation ipLocation;
    private byte[] buffer;
    private byte[] bufferOne;
    private byte[] bufferTwo;

    /**
     * 初始化
     */
    public IpViewer() {
        // 獲取檔案路徑和名稱
        String path = IpViewer.class.getResource("").getPath() + fileName;
        fileName = path;
        // 初始化變數
        this.cache = new HashMap<String, IpLocation>();
        this.ipLocation = new IpLocation();
        this.buffer = new byte[100];
        this.bufferOne = new byte[3];
        this.bufferTwo = new byte[4];

        try {
            randomAccessFile = new RandomAccessFile(fileName, "r");
        } catch (FileNotFoundException e) {
            // 若檔案找不到,在當前目錄下重新搜尋,並將檔名全部改為小寫(有些系統只能識別小寫)
            String name = new File(fileName).getName().toLowerCase();
            File[] files = new File(fileDir).listFiles();
            for (int i = 0; i < files.length; i++) {
                // 判斷是否是檔案
                if (files[i].isFile()) {
                    // 判斷檔名稱
                    if (files[i].getName().toLowerCase().equals(name)) {
                        try {
                            randomAccessFile = new RandomAccessFile(files[i],
                                    "r");
                        } catch (FileNotFoundException fileNotFoundException) {
                            e.printStackTrace();
                            fileName = null;
                        }
                        break;
                    }
                }
            }
        }

        // 若檔案開啟成功,讀取檔案頭資訊
        if (randomAccessFile != null) {
            try {
                begin = ReadUtils.readLongByFour(randomAccessFile, 0);
                end = ReadUtils.readLongByFour(randomAccessFile, 4);
                if (begin == -1 || end == -1) {
                    randomAccessFile.close();
                    randomAccessFile = null;
                }
            } catch (Exception e) {
                e.printStackTrace();
                randomAccessFile = null;
            }
        }
    }

    /**
     * 給定一個不完全的地點名稱,得到包含該地點的IP範圍記錄
     * 
     * @param str
     * @return
     */
    public List<IpEntity> getIpEntityDebug(String str) {
        List<IpEntity> list = new ArrayList<IpEntity>();
        long endOffset = end + 4;
        for (long offset = begin + 4; offset < endOffset; offset += IP_RECORD_LENGTH) {
            // 讀取結束IP偏移量
            long temp = ReadUtils.readLongByThree(randomAccessFile, offset,
                    bufferOne);
            // 若temp不等於-1,則讀取IP資訊
            if (temp != -1) {
                IpLocation ipLocation = this.getIpLocation(temp);
                // 判斷是否包含改地名,若包含,擇新增到list
                if (ipLocation.getCountry().indexOf(str) != -1
                        || ipLocation.getArea().indexOf(str) != -1) {
                    IpEntity ipEntity = new IpEntity();
                    ipEntity.setCountry(ipLocation.getCountry());
                    ipEntity.setArea(ipLocation.getArea());
                    // 獲取起始IP
                    ReadUtils.readIp(randomAccessFile, offset - 4, bufferTwo);
                    ipEntity.setStartIp(ConvertUtils.getIpString(bufferTwo));
                    // 獲取結束IP
                    ReadUtils.readIp(randomAccessFile, temp, bufferTwo);
                    ipEntity.setEndIp(ConvertUtils.getIpString(bufferTwo));
                    list.add(ipEntity);
                }
            }
        }
        return list;
    }

    public List<IpEntity> getIpEntity(String s) {

        List<IpEntity> list = new ArrayList<IpEntity>();
        try {
            // 對映IP資訊檔案到記憶體中
            if (mappedByteBuffer == null) {
                FileChannel fc = randomAccessFile.getChannel();
                mappedByteBuffer = fc.map(FileChannel.MapMode.READ_ONLY, 0,
                        randomAccessFile.length());
                mappedByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
            }

            int endOffset = (int) end;
            for (int offset = (int) begin + 4; offset <= endOffset; offset += IP_RECORD_LENGTH) {
                int temp = ReadUtils.readIntByThree(mappedByteBuffer, offset);
                if (temp != -1) {
                    IpLocation location = this.getIpLocation(temp);
                    // 地點是否包含要查詢的地點名稱
                    if (location.getCountry().indexOf(s) != -1
                            || location.getArea().indexOf(s) != -1) {
                        IpEntity entity = new IpEntity();
                        entity.setCountry(location.getCountry());
                        entity.setArea(location.getArea());
                        // 得到起始IP
                        ReadUtils.readIp(mappedByteBuffer, offset, bufferTwo);
                        entity.setStartIp(ConvertUtils.getIpString(bufferTwo));
                        // 得到結束IP
                        ReadUtils.readIp(mappedByteBuffer, temp, bufferTwo);
                        entity.setEndIp(ConvertUtils.getIpString(bufferTwo));
                        // 新增該記錄
                        list.add(entity);
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace(); 
        }
        return list;
    }

    /**
     * 獲取IP所在的地區資訊
     */
    public IpLocation getIpLocation(String ip) {
        IpLocation location = new IpLocation();
        location.setCountry(this.getCountry(ip));
        location.setArea(this.getArea(ip));
        return location;
    }

    /**
     * 根據IP地區偏移量,獲取IP資訊
     * 
     * @param offset
     * @return
     */
    private IpLocation getIpLocation(long offset) {
        try {
            // 跳過4位元組IP
            randomAccessFile.seek(offset + 4);
            // 讀取第一個位元組,判斷是否是標識位元組
            byte result = randomAccessFile.readByte();
            if (result == REDIRECT_MODE_ONE) {
                // 讀取國家偏移量
                long countryOffset = ReadUtils.readLongByThree(
                        randomAccessFile, bufferOne);
                // 跳轉至偏移處
                randomAccessFile.seek(countryOffset);
                // 再次檢查識別符號
                result = randomAccessFile.readByte();
                if (result == REDIRECT_MODE_TWO) {
                    ipLocation.setCountry(ReadUtils.readString(
                            randomAccessFile, ReadUtils.readLongByThree(
                                    randomAccessFile, bufferOne), buffer));
                    randomAccessFile.seek(countryOffset + 4);
                } else {
                    ipLocation.setCountry(ReadUtils.readString(
                            randomAccessFile, countryOffset, buffer));
                }
                // 讀取地區識別符號
                ipLocation.setArea(this.readArea(randomAccessFile
                        .getFilePointer()));
            } else if (result == REDIRECT_MODE_TWO) {
                ipLocation.setCountry(ReadUtils.readString(randomAccessFile,
                        ReadUtils.readLongByThree(randomAccessFile, bufferOne),
                        buffer));
                ipLocation.setArea(this.readArea(offset + 8));
            } else {
                ipLocation.setCountry(ReadUtils.readString(randomAccessFile,
                        randomAccessFile.getFilePointer() - 1, buffer));
                ipLocation.setArea(this.readArea(randomAccessFile
                        .getFilePointer()));
            }
            return ipLocation;
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 根據IP地區偏移量,獲取IP資訊
     * 
     * @param offset
     * @return
     */
    private IpLocation getIpLocation(int offset) {
        // 跳過4位元組IP
        mappedByteBuffer.position(offset + 4);
        // 讀取第一個位元組,判斷是否是標識字元
        byte result = mappedByteBuffer.get();
        if (result == REDIRECT_MODE_ONE) {
            // 讀取國家偏移量
            int countryOffset = ReadUtils.readIntByThree(mappedByteBuffer);
            // 跳轉至偏移處
            mappedByteBuffer.position(countryOffset);
            // 再次檢查識別符號
            result = mappedByteBuffer.get();
            if (result == REDIRECT_MODE_TWO) {
                ipLocation.setCountry(ReadUtils.readString(mappedByteBuffer,
                        ReadUtils.readIntByThree(mappedByteBuffer), buffer));
                mappedByteBuffer.position(countryOffset + 4);
            } else {
                ipLocation.setCountry(ReadUtils.readString(mappedByteBuffer,
                        countryOffset, buffer));
            }
            // 設定地區標識
            ipLocation.setArea(this.readArea(mappedByteBuffer.position()));
        } else if (result == REDIRECT_MODE_TWO) {
            ipLocation.setCountry(ReadUtils.readString(mappedByteBuffer,
                    ReadUtils.readIntByThree(mappedByteBuffer), buffer));
            ipLocation.setArea(this.readArea(offset + 8));
        } else {
            ipLocation.setCountry(ReadUtils.readString(mappedByteBuffer,
                    mappedByteBuffer.position() - 1, buffer));
            ipLocation.setArea(this.readArea(mappedByteBuffer.position()));
        }
        return ipLocation;
    }

    /**
     * 獲取國家名稱字串
     * 
     * @return
     */
    public String getCountry(String ip) {
        return this.getCountry(ConvertUtils.getIpArray(ip));
    }

    /**
     * 獲取國家地區
     * 
     * @param ip
     * @return
     */
    public String getCountry(byte[] ip) {
        // 檢測IP地址檔案是否正常
        if (randomAccessFile == null) {
            return Message.BAD_IP_FILE;
        }
        // 儲存IP,轉IP位元組陣列為字串
        String ipStr = ConvertUtils.getIpString(ip);
        // 先從cache中檢查ip,若沒有在檢測檔案
        if (cache.containsKey(ipStr)) {
            IpLocation ipLocation = cache.get(ipStr);
            return ipLocation.getCountry();
        } else {
            IpLocation ipLocation = this.getIpLocation(ip);
            cache.put(ipStr, ipLocation.getCopy());
            return ipLocation.getCountry();
        }
    }

    /**
     * 根據IP搜尋IP檔案
     * 
     * @param ip
     * @return
     */
    public IpLocation getIpLocation(byte[] ip) {
        IpLocation location = null;
        long offset = this.getLocateIp(ip);
        if (offset != -1) {
            location = this.getIpLocation(offset);
        }
        if (location == null) {
            location = new IpLocation();
            location.setCountry(Message.UNKNOW_COUNTRY);
            location.setArea(Message.UNKNOW_AREA);
        }
        return location;
    }

    /**
     * 根據IP內容,定位IP地址所在的國家,返回一個偏移量
     * 
     * @param ip
     * @return
     */
    public long getLocateIp(byte[] ip) {
        long m = 0;
        int n;
        ReadUtils.readIp(randomAccessFile, begin, bufferTwo);
        n = ReadUtils.compareIp(ip, bufferTwo);
        if (n == 0)
            return begin;
        else if (n < 0)
            return -1;

        // 二分查詢法查詢IP
        for (long i = begin, j = end; i < j;) {
            m = this.getMiddleOffset(i, j);
            ReadUtils.readIp(randomAccessFile, m, bufferTwo);
            n = ReadUtils.compareIp(ip, bufferTwo);
            if (n > 0) {
                i = m;
            } else if (n < 0) {
                if (m == j) {
                    j -= IP_RECORD_LENGTH;
                    m = j;
                } else {
                    j = m;
                }
            } else {
                return ReadUtils.readLongByThree(randomAccessFile, m + 4,
                        bufferOne);
            }
        }
        m = ReadUtils.readLongByThree(randomAccessFile, m + 4, bufferOne);
        ReadUtils.readIp(randomAccessFile, m, bufferTwo);
        n = ReadUtils.compareIp(ip, bufferTwo);
        if (n <= 0)
            return m;
        else
            return -1;

    }

    /**
     * 根據IP位元組陣列獲取地區名稱
     * 
     * @param ip
     * @return
     */
    private String getArea(byte[] ip) {
        // 檢查IP檔案是否正常
        if (randomAccessFile == null)
            return Message.BAD_IP_FILE;
        // 儲存IP,轉換位元組陣列IP為字串
        String ipStr = ConvertUtils.getIpString(ip);
        // 現在cache中搜索結果,若沒有,再從檔案中查詢
        if (cache.containsKey(ipStr)) {
            IpLocation location = cache.get(ipStr);
            return location.getArea();
        } else {
            IpLocation location = this.getIpLocation(ip);
            cache.put(ipStr, location.getCopy());
            return location.getArea();
        }
    }

    /**
     * 根據IP獲取地區名稱
     * 
     * @param ip
     * @return
     */
    private String getArea(String ip) {
        return this.getArea(ConvertUtils.getIpArray(ip));
    }

    /**
     * 從offset位置讀取地區資訊
     * 
     * @param offset
     * @return
     */
    private String readArea(long offset) {
        try {
            randomAccessFile.seek(offset);
            byte result = randomAccessFile.readByte();
            if (result == REDIRECT_MODE_ONE || result == REDIRECT_MODE_TWO) {
                long areaOffset = ReadUtils.readLongByThree(randomAccessFile,
                        offset + 1, bufferOne);
                if (areaOffset == 0)
                    return Message.UNKNOW_AREA;
                else
                    return ReadUtils.readString(randomAccessFile, areaOffset,
                            buffer);

            } else {
                return ReadUtils.readString(randomAccessFile, offset, buffer);
            }
        } catch (Exception e) {
            e.printStackTrace(); 
        }
        return "";
    }

    private String readArea(int offset) {
        mappedByteBuffer.position(offset);
        byte result = mappedByteBuffer.get();
        if (result == REDIRECT_MODE_ONE || result == REDIRECT_MODE_TWO) {
            int areaOffset = ReadUtils.readIntByThree(mappedByteBuffer);
            if (areaOffset == 0)
                return Message.UNKNOW_AREA;
            else
                return ReadUtils.readString(mappedByteBuffer, areaOffset,
                        buffer);
        } else {
            return ReadUtils.readString(mappedByteBuffer, offset, buffer);
        }
    }

    /**
     * 獲取begin和end中間偏移量
     * 
     * @param begin
     * @param end
     * @return
     */
    private long getMiddleOffset(long begin, long end) {
        long middle = (end - begin) / IP_RECORD_LENGTH;
        middle >>= 1;
        if (middle == 0)
            middle = 1;
        return begin + middle * IP_RECORD_LENGTH;
    }

}


7.測試程式

package com.test.ip;

import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;

import com.test.ip.viewer.IpViewer;

public class Test {
    public static void main(String[] args){
        String ip = "63.251.90.8";
        IpViewer ipViewer = new IpViewer();
        System.out.println(ipViewer.getIpLocation(ip).getCountry()+":"+ipViewer.getIpLocation(ip).getArea());
    }
}  


原文:https://blog.csdn.net/aleyns/article/details/78849835