`
hzy3774
  • 浏览: 986369 次
  • 性别: Icon_minigender_1
  • 来自: 珠海
社区版块
存档分类
最新评论

NDK小实例使用QQwry实现Android端IP归属地查询

 
阅读更多

学习做一个使用NDK的小项目:

QQWry的格式和解析可以参考
http://hzy3774.iteye.com/blog/1851364

 

Github地址:https://github.com/hzy3774/AndroidIPQQWry

先设置好NDK编译器:

添加NDK编译器


 

设置编译器参数



 

*用java写好接口函数:

在C/C++将GBK转码成UTF-8比较麻烦,如果直接返回GBK的字符串在接口处会报错退出,所以直接传出字节数组。

QQWry.java:

public class QQWryAnd {

	private native void jniOpen(String datPath);

	private native byte[] jniGetVersionBytes();

	private native byte[] jniGetIpAddrBytes(String ip);

	private native byte[] jniGetIpRangeBytes(String ip);

	private native int jniGetIpCount();
	
	private native void jniClose();

}

 

 用javah命令生成对应的本地函数,并填充:

qqand.cpp

#include "IPLocator.hpp"

#ifdef __cplusplus
extern "C" {
#endif

using namespace std;

static jbyteArray string2jbyteArray(JNIEnv *env, string str);

IPLocator* ipLocator;

/*
 * Class:     com_hu_qqwryand_QQWryAnd
 * Method:    jniOpen
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_com_hu_qqwryand_QQWryAnd_jniOpen
  (JNIEnv *env, jobject, jstring path){
	const char* cpath = (const char*)env->GetStringUTFChars(path, NULL);
	LOGI("open file:\"%s\"", cpath);
	ipLocator = new IPLocator(cpath);
	env->ReleaseStringUTFChars(path, cpath);
}

/*
 * Class:     com_hu_qqwryand_QQWryAnd
 * Method:    jniGetVersionBytes
 * Signature: ()[B
 */
JNIEXPORT jbyteArray JNICALL Java_com_hu_qqwryand_QQWryAnd_jniGetVersionBytes
  (JNIEnv *env, jobject){
	string version = ipLocator->getVersion();
	return string2jbyteArray(env, version);
}

/*
 * Class:     com_hu_qqwryand_QQWryAnd
 * Method:    jniGetIpAddrBytes
 * Signature: (Ljava/lang/String;)[B
 */
JNIEXPORT jbyteArray JNICALL JNICALL Java_com_hu_qqwryand_QQWryAnd_jniGetIpAddrBytes
  (JNIEnv *env, jobject, jstring ip){
	const char* cip = env->GetStringUTFChars(ip, NULL);
	string addr = ipLocator->getIpAddr(cip);
	return string2jbyteArray(env, addr);
}

/*
 * Class:     com_hu_qqwryand_QQWryAnd
 * Method:    jniGetIpRangeBytes
 * Signature: (Ljava/lang/String;)[B
 */
JNIEXPORT jbyteArray JNICALL Java_com_hu_qqwryand_QQWryAnd_jniGetIpRangeBytes
  (JNIEnv *env, jobject, jstring ip){
	const char* cip = env->GetStringUTFChars(ip, NULL);
	string range = ipLocator->getIpRange(cip);
	return string2jbyteArray(env, range);
}

/*
 * Class:     com_hu_qqwryand_QQWryAnd
 * Method:    jniGetIpCount
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_com_hu_qqwryand_QQWryAnd_jniGetIpCount
  (JNIEnv *, jobject){
	return (int)ipLocator->getTotal();
}

/*
 * Class:     com_hu_qqwryand_QQWryAnd
 * Method:    jniClose
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_hu_qqwryand_QQWryAnd_jniClose
  (JNIEnv *, jobject){
	LOGI("jniClose()");
	delete ipLocator;
	ipLocator = 0;
}

static jbyteArray string2jbyteArray(JNIEnv *env, string str){
	const char* cstr = str.c_str();
	LOGI("string2jbyteArray(), len[%d]",strlen(cstr));
	jbyteArray ret = env->NewByteArray(strlen(cstr));
	env->SetByteArrayRegion(ret, 0, strlen(cstr), (jbyte*)cstr);
	return ret;
}

#ifdef __cplusplus
}
#endif

 实现主要查询逻辑的IPLocator.cpp(来源于网络)

#include "IPLocator.hpp"
#include <sstream>
#include <stdlib.h>

using std::cout;
using std::cerr;
using std::endl;
using std::ios;

IPLocator::IPLocator(const string& ipdb_name)
{
	unsigned char buf[8];
	ipdb.open(ipdb_name.c_str(),ios::binary);
	if(!ipdb) {
		cerr << "can not open " << ipdb_name <<endl;
		LOGE("can not open[%s]", ipdb_name.c_str());
		return;
	}
	ipdb.read((char*)buf,8);
	first_index = IPLocator::bytes2integer(buf,4);
	last_index = IPLocator::bytes2integer(buf+4,4);
	index_count = (last_index - first_index) / 7 + 1;
}

IPLocator::~IPLocator() 
{ 
	ipdb.close();
}

string IPLocator::getVersion()
{
	string version = this->getIpAddr(0xffffff00);
//	std::ostringstream oss;
//	oss << this->index_count;
//	string total_item(oss.str());
//	version =  version + " 记录总数:" + total_item + "条";
	return version;
}

unsigned int IPLocator::getTotal()
{
	return this->index_count;
}

string IPLocator::getIpAddr(const string& ip)
{
	return this->getIpAddr(this->getIpFromString(ip));
}

string IPLocator::getIpAddr(unsigned int ip)
{
	unsigned int M, L=0, R=this->index_count;
	string addr;

	while (L < R-1) {
		M = (L + R) / 2;
		this->setIpRange(M);
		if (ip == this->cur_start_ip) {
			L = M;
			break;
		}
		if (ip > this->cur_start_ip)
			L = M;
		else
			R = M;
	}
	this->setIpRange(L);
	/* version infomation, the last item */
//	if((ip & 0xffffff00) == 0xffffff00)
//		this->setIpRange(R);
	if(ip >= this->cur_start_ip && ip <= this->cur_end_ip)
		addr = this->getAddr(this->cur_start_ip_offset);
	else
//		addr = "未找到该IP的地址";
		addr = "Invalid IP";
	return addr;
}

string IPLocator::getIpRange(const string& range)
{
	return this->getIpRange(this->getIpFromString(range));
}

string IPLocator::getIpRange(unsigned int range)
{
	this->getIpAddr(range);
	return this->getIpString(this->cur_start_ip)
		+ " - " + this->getIpString(this->cur_end_ip);
}

string IPLocator::getAddr(streamsize offset)
{
	unsigned char byte;
	unsigned char buf[4];
	unsigned int country_offset;
	string country_addr,area_addr;
	
	this->readFromFile(offset+4, buf,4);
	byte = buf[0];
	if(0x01 == byte) {
		country_offset = IPLocator::bytes2integer(buf+1,3);
		this->readFromFile(country_offset,buf,4);
		byte = buf[0];
		if(0x02 == byte){
			country_addr = this->readStringFromFile(IPLocator::bytes2integer(buf+1,3));
			area_addr = this->getAreaAddr(country_offset+4);
		} else {
			country_addr = this->readStringFromFile(country_offset);
			area_addr = this->getAreaAddr(country_offset+country_addr.length()+1);
		}
	} else if(0x02 == byte) {
		this->readFromFile(offset+4+1,buf,3);
		country_offset = IPLocator::bytes2integer(buf,3);
		country_addr = this->readStringFromFile(country_offset);
		area_addr = this->getAreaAddr(offset+4+4);
	} else {
		country_addr = this->readStringFromFile(offset+4);
		area_addr = this->getAreaAddr(offset+4+country_addr.length()+1);
	}

	return country_addr + " " + area_addr;
}

string IPLocator::getAreaAddr(streamsize offset)
{
	unsigned char byte;
	unsigned char buf[4];
	unsigned int p=0;
	string area_addr;

	this->readFromFile(offset,buf,4);
	byte = buf[0];
	if(0x01 == byte || 0x02 == byte) {
		p = IPLocator::bytes2integer(buf+1,3);
		if(p)
			area_addr = this->readStringFromFile(p);
		else
			area_addr = "";
	} else 
		area_addr = this->readStringFromFile(offset);
	return area_addr;

}

void IPLocator::setIpRange(unsigned int rec_no)
{
	unsigned char buf[7];
	unsigned int offset = first_index + rec_no * 7;
	this->readFromFile(offset, buf, 7);
	this->cur_start_ip = IPLocator::bytes2integer(buf,4);
	this->cur_start_ip_offset = IPLocator::bytes2integer(buf+4,3);
	this->readFromFile(this->cur_start_ip_offset, buf, 4);
	this->cur_end_ip = IPLocator::bytes2integer(buf, 4);
}

void IPLocator::readFromFile( streamsize offset, unsigned char *buf, int len)
{
	ipdb.seekg(offset);
	ipdb.read((char*)buf,len);
}

string IPLocator::readStringFromFile(streamsize offset)
{	
	char ch;
	string str;
	ipdb.seekg(offset);
	ipdb.get(ch);
	while(ch) {
		str += ch;
		ipdb.get(ch);
	}
	return str;
}

unsigned int IPLocator::getIpFromString(const string& ip)
{
    char *result = NULL;
	unsigned int ret=0;
	char *s=strdup(ip.c_str());
    result = strtok( s, "." );
    while( result ) {
		ret <<= 8;
		ret |= (unsigned int)atoi(result);
		result = strtok( NULL, "." );
    }
	free(s);
	return ret;
}

string IPLocator::getIpString(unsigned int ip)
{
	char buf[256];
	sprintf(buf,"%d.%d.%d.%d",ip>>24,(ip>>16)&0xff,(ip>>8)&0xff,ip&0xff );
	string ipstr(buf);
	return ipstr;
}

unsigned int IPLocator::bytes2integer(unsigned char *ip, int count)
{
	int i;
	unsigned int ret;

	if(count < 1 || count > 4) 
		return 0;
	ret = ip[0];
	for (i = 0; i < count; i++)
		ret |= ((unsigned int)ip[i])<<(8*i);
	return ret;
}

 头文件IPLocator.hpp

#include <jni.h>
#include <android/log.h>

#define LOG_TAG "jniLog" // 这个是自定义的LOG的标识
#undef LOG // 取消默认的LOG

#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__) // 定义LOG类型
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) // 定义LOG类型
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__) // 定义LOG类型
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) // 定义LOG类型
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,LOG_TAG,__VA_ARGS__) // 定义LOG类型

#ifndef _IP_LOCATOR_H_
#define _IP_LOCATOR_H_

#include <string>
#include <iostream>
#include <fstream>

using std::string;
using std::streamsize;

class IPLocator
{
public:
	IPLocator(const string& ipdb_name);
	~IPLocator();
	string getVersion();
	string getIpAddr(const string& ip);
	string getIpRange(const string& ip);
	unsigned int getTotal();
private:
	string getIpAddr(unsigned int ip);
	string getIpRange(unsigned int ip);
	static unsigned int getIpFromString(const string& ip);
	static string getIpString(unsigned int ip);
	static unsigned int bytes2integer(unsigned char *ip, int count);
	void readFromFile(streamsize offset, unsigned char *buf,int len);
	string readStringFromFile(streamsize offset);
	string getAddr(streamsize offset);
	string getAreaAddr(streamsize offset);
	void setIpRange(unsigned int rec_no);
private:
	std::ifstream ipdb;
	unsigned int first_index;
	unsigned int last_index;
	unsigned int index_count;
	unsigned int cur_start_ip;
	unsigned int cur_start_ip_offset;
	unsigned int cur_end_ip;
};

#endif

 写Android.mk文件:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

#添加log模块
LOCAL_LDLIBS+= -llog

LOCAL_MODULE    := qqand
LOCAL_SRC_FILES := qqand.cpp \
				   IPLocator.cpp

include $(BUILD_SHARED_LIBRARY)

 写Android.mk文件

#使用gnu标准函数库
APP_STL := gnustl_static

#APP_ABI := armeabi armeabi-v7a x86

 编译成功,就可以使用了,完善java的接口类:

package com.hu.qqwryand;

import java.io.UnsupportedEncodingException;

/**
 * QQwry jni 接口类
 * @author Administrator
 *
 */
public class QQWryAnd {

	public QQWryAnd(String datPath) {
		this.jniOpen(datPath);
	}

	/**
	 * 
	 * @return	获取版本信息
	 */
	public String getVersion() {
		return getStr(jniGetVersionBytes());
	}

	/**
	 * 
	 * @param ip String类型IP地址
	 * @return	Ip归属地信息
	 */
	public String getIpAddr(String ip) {
		return getStr(jniGetIpAddrBytes(ip));
	}

	/**
	 * 
	 * @param ip IP地址
	 * @return Ip地址范围
	 */
	public String getIpRange(String ip) {
		return getStr(jniGetIpRangeBytes(ip));
	}
	
	/**
	 * 
	 * @return IP数据库总地址条数
	 */
	public int getIpCount(){
		return jniGetIpCount();
	}

	public void close() {
		this.jniClose();
	}
	
	private String getStr(byte[] array){	//将byte数组按GBK方式转换成String
		String str = "";
		try {
			str = new String(array, "GBK");
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return str;
	}

	private native void jniOpen(String datPath);

	private native byte[] jniGetVersionBytes();

	private native byte[] jniGetIpAddrBytes(String ip);

	private native byte[] jniGetIpRangeBytes(String ip);

	private native int jniGetIpCount();
	
	private native void jniClose();
	
	/**
	 * 加载动态库
	 */
	static {
		System.loadLibrary("qqand");
	}

}

 这样上层的接口也完成了,只需要在Activity中调用就行了。

其他还有一些文件操作:

最后的效果:



 完

  • 大小: 19.7 KB
  • 大小: 15.6 KB
  • 大小: 26 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics