作者存档: 朱坤乾 - 第2页

如何判断是否为中国IP

收到一个需求,需要判断IP是否来自中国。

判断IP来源,很早之前自己也感兴趣,一直没有去了解如何实现。

今天正好搜索了一下相关的文章。

亚洲所有相关分配的IP段可以从 http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest 查询。

从里面解释后即可实现判断IP是否为中国。

没有找到java的代码,就自己写了一份,供其它朋友使用。

将下载下来的 delegated-apnic-latest 放在 classpath下即可。

import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.assertj.core.util.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.Maps;

public class IpUtil {
    private static final String FILE_NAME = "delegated-apnic-latest";

    // 只存放属于中国的ip段
    private static Map<Integer, List<int[]>> chinaIps = Maps.newHashMap();
    static {
        init();
    }

    public static void init() {
        try {
            // ip格式: add1.add2.add3.add4
            // key为 : add1*256+add2
            // value为int[2]: int[0]存的add3*256+add4的开始ip int[4]存的结束ip
            Map<Integer, List<int[]>> map = Maps.newHashMap();

            InputStream input = Thread.currentThread().getContextClassLoader().getResourceAsStream(FILE_NAME);
            List<String> lines = IOUtils.readLines(input, StandardCharsets.UTF_8);
            for (String line : lines) {
                if (line.startsWith("apnic|CN|ipv4|")) {
                    // 只处理属于中国的ipv4地址
                    String[] strs = line.split("\\|");
                    String ip = strs[3];
                    String[] add = ip.split("\\.");
                    int count = Integer.valueOf(strs[4]);

                    int startIp = Integer.parseInt(add[0]) * 256 + Integer.parseInt(add[1]);
                    while (count > 0) {
                        if (count >= 65536) {
                            // add1,add2 整段都是中国ip
                            chinaIps.put(startIp, Collections.EMPTY_LIST);
                            count -= 65536;
                            startIp += 1;
                        } else {

                            int[] ipRange = new int[2];
                            ipRange[0] = Integer.parseInt(add[2]) * 256 + Integer.parseInt(add[3]);
                            ipRange[1] = ipRange[0] + count;
                            count -= count;

                            List<int[]> list = map.get(startIp);
                            if (list == null) {
                                list = Lists.newArrayList();
                                map.put(startIp, list);
                            }

                            list.add(ipRange);
                        }
                    }
                }
            }
            chinaIps.putAll(map);
        } catch (Exception e) {
            logger.error("ERROR", e);
        }
    }

    public static boolean isChinaIp(String ip) {
        if (StringUtils.isEmpty(ip)) {
            return false;
        }
        String[] strs = ip.split("\\.");
        if (strs.length != 4) {
            return false;
        }
        int key = Integer.valueOf(strs[0]) * 256 + Integer.valueOf(strs[1]);
        List<int[]> list = chinaIps.get(key);
        if (list == null) {
            return false;
        }
        if (list.size() == 0) {
            // 整段都是中国ip
            return true;
        }
        int ipValue = Integer.valueOf(strs[2]) * 256 + Integer.valueOf(strs[3]);
        for (int[] ipRange : list) {
            if (ipValue >= ipRange[0] && ipValue <= ipRange[1]) {
                return true;
            }
        }

        return false;
    }

    private static final Logger logger = LoggerFactory.getLogger(IpUtil.class);
}

 

如果扩展这个代码后,可以增加判断IP属于哪个国家。

如果在判断IP属于哪个城市,可能得需要 http://dev.maxmind.com/zh-hans/geoip/geoip2/

 

参考:

https://www.zhihu.com/question/19794443

http://xixitalk.github.io/blog/2013/03/11/func-is-china-ip/

 

脚本获得当前所在的目录

写脚本时经常需要获取当前执行的脚本所在的目录。

我最早使用的是:

shellPath=`echo $PWD/``echo ${0%/*}`

今天在做一个远程执行脚本时才发现,如果执行脚本时使用绝对路径,上面会得到错误的路径,现修改如下,可同时支持相对路径和绝对路径执行脚本:

shellPath=`echo $PWD/``echo ${0%/*}`

# process absolute path
shellPath1=`echo $PWD/`
shellPath2=`echo ${0%/*}`
if [ ${shellPath2:0:1} == '/' ] ; then
    shellPath=${shellPath2}
fi

Unity中扩展GameObject类

今天看代码时看到一个之前不知道的技巧,可以扩展Unity的GameObject类,为GameObject添加自己定义的方法:

using UnityEngine;
using System.Collections;

public static class GameObjectEx  {

    public static T MakeSureComponent<T>(this GameObject gameObject) where T : Component
    {
        T t = gameObject.GetComponent<T>();
        if (t == null)
        {
            t = gameObject.AddComponent<T>();
        }
        return t;
    }

}

这个类添加在工程中后,所以的gameObject上都可以直接调用MakeSureCompoent这个方法,真是超级便捷.

this.m_footstepHandler = base.gameObject.MakeSureComponent<FootstepHandler>();

同理,Unity自带的其它类应该也是可以进行扩展的。

查找了下是哪个天才想到的这个办法,google找到最早的一篇是这个:

https://forum.unity3d.com/threads/howto-add-method-to-the-gameobject-class-ie-add-custom-variables-to-every-instance.89791/

手机上的聊天

手机上的聊天:有语音SDK,可以语音文本互转。也有语音SDK直接支持自建语音频道。

有文本翻译API,可以翻译为多国语种文字:https://www.microsoft.com/en-us/translator/home.aspx

各种翻译,目前已知的商业API有微软的,百度的,行云的,另外还它一些商业API提交翻译功能。

微软的 MicrosoftTranslatorAPI  可以检测出使用的语种。

记一次排查服务器被控制

一个朋友联系我说服务器被当做肉鸡了,在 /etc/init.d 目录有几个脚本无法删除,删除后自动生成。

联系云服务器技术客服,回复说是被当做肉鸡了。

我连上服务器查看了下那个脚本:

#chkconfig: 12345 90 90
#description: acdnfhruvx
#BEGIN INIT INFO
#Provides: acdnfhruvx
#Required-Start:
#Required-Stop:
#Default-Start: 1 2 3 4 5
#Default-Stop:
#Short-Description: acdnfhruvx
#END INIT INFO
case $1 in
start)
/bin/acdnfhruvx
;;
stop)
;;
*)
/bin/acdnfhruvx   
;;
esac

感觉没有任何有用的线索。

查看 conrtab -e ,也仅有一个正常的业务逻辑脚本。

本来以为他是常驻内存的进程来定时检测文件是否被删除,但查看history, ps 等也没有得到任何有用的线索。

打开 /etc/crontab 脚本时,发现异常了:

SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
HOME=/

# For details see man 4 crontabs

# Example of job definition:
# .---------------- minute (0 - 59)
# |  .------------- hour (0 - 23)
# |  |  .---------- day of month (1 - 31)
# |  |  |  .------- month (1 - 12) OR jan,feb,mar,apr ...
# |  |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# |  |  |  |  |
# *  *  *  *  * user-name command to be executed

*/3 * * * * root /etc/cron.hourly/gcc4.sh

这个 gcc4.sh 是什么鬼,而且每3分钟执行一次,如此高频率就不仅仅是可疑,简单就是在自己脸上贴上我是木马。

打开 /etc/cron.hourly/gcc4.sh 后就很明了了:自动执行一个二进程代码文件。

把gcc4.sh 文件备份后修改名字,再去 /etc/init.d 目录下清除可疑的自启动脚本,删除后没有重新创建出来。

再去 修复 /etc/crontab ,把最后一行删除。把 gcc4.sh 删除,把里面提到的 so 文件删除。

OK,结束。

自己学习写的第一个shader,画个圆


学习到如果需要显示一个圆型的图片,需要自己来写shader,这里是我学习写的第一个shader。

以后会边学习边将写的shader记录下来,也可以不断补充自己学习能力。

Shader "Custom/Circle2" {
	Properties{
		_Color("Color", Color) = (1,0,0,0)
	}
	SubShader{
 
		Tags{ "Queue" = "Transparent" "RenderType" = "Transparent" "IgnoreProjector" = "True" }
		Pass{
			ZWrite Off
			Blend SrcAlpha OneMinusSrcAlpha

			CGPROGRAM

			#pragma vertex vert
			#pragma fragment frag
			#include "UnityCG.cginc"

			fixed4 _Color; // low precision type is usually enough for colors

			struct fragmentInput {
				float4 pos : SV_POSITION;
				float2 uv : TEXCOORD0;
			};

			fragmentInput vert(appdata_base v)
			{
				fragmentInput o;

				o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
				o.uv = v.texcoord.xy - fixed2(0.5,0.5);
 
				return o;
			}

			fixed4 frag(fragmentInput i) : SV_Target{
				float distance = sqrt(pow(i.uv.x, 2) + pow(i.uv.y,2));
		
				if (distance > 0.5f) {
					if (distance < 0.51f) {
						return fixed4(_Color.r* distance, _Color.g* distance, _Color.b* distance, 0.6f);
					}
					else {
						return fixed4(0, 0, 0, 0);
					}
				}
				else {
					return fixed4(_Color.r * distance, _Color.g * distance, _Color.b * distance,1);
				}
			}
			ENDCG
		}
	}
}

开源java recast navmesh寻路库

自己之前写的在java服务器上使用导航网格寻路的库。

底层使用c++提供接口,java层通过jni调用c++接口。

https://github.com/zhukunqian/unity_navmesh_for_java

更多信息直接在github上交流吧。

unity官方出的内存查看工具

https://bitbucket.org/Unity-Technologies/memoryprofiler

Cannot open Eclipse Marketplace 错误

国内网络环境果真复杂。

今天使用eclipse遇到这个问题,推测这个问题只出现在国内。

http://blog.csdn.net/cashcat2004/article/details/43819517

按国内同僚的解决方法:

在eclipse.ini中添加

找来找去没找到解决办法,在stackoverflow中终于看到描述,在eclipse.ini中增加
 -Djava.NET.preferIPv4Stack=true

重启eclipse后顺利连上。

c/c++ 与 lua之前互调的性能测试

完整测试代码如下:

测试目标主要是看看c与lua之前互调的性能如何。

测试结论:c调用lua与lua调用c基本只有一个数量级的性能损失。

从c中调用lua,使用缓存ref的方式,可以提升30%的性能。

#include <lua.h>
#include <lauxlib.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>
#include "skynet.h"

// 取当前毫秒值
static int
ltime(lua_State *L) {
	struct timeval tv;
	gettimeofday(&tv,NULL);
	lua_pushinteger(L,tv.tv_sec*1000+tv.tv_usec/1000);
	return 1;
}

// 供lua调用,内部无业务逻辑
static int
ltest1(lua_State *L) {
        return 0;
}

// 供lua调用, 内部int相加并返回
static int
ltest2(lua_State *L) {
	int a1=luaL_checkinteger(L,1);
	int a2=luaL_checkinteger(L,2);
	lua_pushinteger(L,a1+a2);
	return 1;
}
// 内部调用lua中方法
static int
ltest3(lua_State *L) {
	lua_getglobal(L,"global_test_func1");
	lua_call(L,0,0);
	return 0;
}
// 内部调用lua中方法,并取得lua中返回值
static int
ltest4(lua_State *L) {
	lua_getglobal(L,"global_test_func2");
	lua_pushinteger(L,1);
	lua_pushinteger(L,2);
	lua_call(L,2,1);
	int sum = (int)lua_tointeger(L, -1); 
	lua_pop(L, 1); 
	lua_pushinteger(L,sum);
	return 1;
}
// 内部调用lua方法10000次
static int
ltest5(lua_State *L) {
	int sum=0;	

	for(int m=0;m<10000000;m++){
        	lua_getglobal(L,"global_test_func2");
        	lua_pushinteger(L,1);
        	lua_pushinteger(L,2);
        	lua_call(L,2,1);
        	sum=(int)lua_tointeger(L, -1);
		lua_pop(L, 1); 
	}

        lua_pushinteger(L,sum);
        return 1;
}
// 和test5一起对比
static int
ltest6(lua_State *L) {
        for(int m=0;m<10000000;m++){
                lua_getglobal(L,"global_test_func2");
                lua_pushinteger(L,1);
                lua_pushinteger(L,2);
                lua_call(L,2,1);
                lua_pop(L, 1);
        }
        lua_pushinteger(L,3);
        return 1;
}
// 和test5一起做对比
static int
ltest7(lua_State *L) {
	int func2_handler;
	lua_getglobal(L,"global_test_func2");
	func2_handler=luaL_ref(L,LUA_REGISTRYINDEX);

	for(int m=0;m<10000000;m++){
		lua_rawgeti(L,LUA_REGISTRYINDEX,func2_handler);
		lua_pushinteger(L,1);
		lua_pushinteger(L,2);
		lua_call(L,2,1);
		lua_pop(L,1);
	}
	
	//luaL_unref(L,LUA_REGISTRYINDEX);
	lua_pushinteger(L,3);
	return 1;
}

// 和test5一起做对比一下
int add(int a,int b){
	return a+b;
}
static int
ltest8(lua_State *L) {
	for(int m=0;m<10000000;m++){
		add(1,2);
	}
	return 0;
}


// full userdata

// light userdata

int
luaopen_perf(lua_State *L) {
        luaL_checkversion(L);
        luaL_Reg l[] = {
		{ "time",  ltime  },
                { "test1", ltest1 }, // 从lua中直接调用c,内部无逻辑
                { "test2", ltest2 },
		{ "test3", ltest3 },
		{ "test4", ltest4 },
		{ "test5", ltest5 },
		{ "test6", ltest6 },
		{ "test7", ltest7 },
		{ "test8", ltest8 },
                { NULL, NULL}
        };
        luaL_newlib(L,l);

        return 1;
}

lua代码:

require "common"

local perf = require "perf"


function global_test_func1()
--	print("global_test_func1")
end
function global_test_func2(a,b)
	return a+b
end
local t1
local t2

t1=perf.time()
for i=1,10000000 do
	perf.test1()
end
t2=perf.time()
print("test1:"..(t2-t1))

t1=perf.time()
for i=1,10000000 do
	perf.test2(1,2)
end
t2=perf.time()
print("test2:"..(t2-t1))

t1=perf.time()
for i=1,10000000 do
	perf.test3()
end
t2=perf.time()
print("test3:"..(t2-t1))

t1=perf.time()
for i=1,10000000 do
	perf.test4()
end
t2=perf.time()
print("test4:"..(t2-t1))

t1=perf.time()
perf.test5()
t2=perf.time()
print("test5:"..(t2-t1))

t1=perf.time()
perf.test6()
t2=perf.time()
print("test6:"..(t2-t1))

t1=perf.time()
perf.test7()
t2=perf.time()
print("test7:"..(t2-t1))

t1=perf.time()
perf.test8()
t2=perf.time()
print("test8:"..(t2-t1))

运行结果:

test1:470
test2:777
test3:1619
test4:1941
test5:1490
test6:1437
test7:675
test8:40