分类存档: 未分类

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/

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

一个hashmap算法

阅读云风开源的AOI实现时,看到里面有一个hashmap算法实现。

https://github.com/cloudwu/aoi

这里记录下分享下其实现

map内部存储是使用数组实现即  map_slot,初始分配16个。容量不足时,以翻倍容量增加,并将对象重新进行分配至新的内存空间。

这里画图举例时以初始容量为8进行原理说明:

1、初始状态,所有slot处于未分配

2、插入A,使用hash算法计算出应该分配在哪个slot上(注:可以考虑添加扰动函数避免比较差的hash算法,这里针对aoi的情况,直接使用了对size取模)

假设对A取模后将其放入第三个slot

3、继续插入B和C,其中B和A取模后 不在相同的slot中。

然后是取模后分配到相同的slot的情况:

实际上两个对象不会存在相同的slot中。

这里如果出现分配的slot中已经存在对象了,比如此时,C分配的第三个slot已经存在A了。

则从数组最尾部向前查找第一个为空的slot,将C放入,并将A.next指向C所在的slot

 

4、继续插入一个和A相同slot的对象时,继续从尾部向前查找一个空的slot,并将A.next指向新分配的slot,新分配的slot.next指向原A.next ( 即C)

5、如果遇到分配的slot被其它slot的对象占用如何处理。

比如,我这里想分配E,取模后应该分配在第七个格子,但第七个格子已经被D占用。

这里采用的方式,是将D取出( 因为D取模不应该分配在第七个格子,只是因为第三个格子被占用了,所以才从尾部查找的一个空闲的格子放D),取出D后将E放后,如下图:

 

这里D被取出应该如何处理,再执行一个第1步,将D放hashmap即可。

 

游戏中重构了网络模块

我们游戏中断线和超时这一块出问题非常多。

这一次我专门抽出了一周时间,了解了下c#下的socket相关函数重写了网络模块。

首要目标是避免另开线程,使用nio。然后是内部处理超时,断线和其它IO异常,重连3次失败后,才提交给游戏自己处理。重连时,自动登陆并续发消息。

这里本来还需要添加一个补发消息的机制(断线后网络传输中导致丢失的消息),但是测试只有续发消息,已经解决掉了原来99%问题。所以补发消息先放一下,先再重构其它地方。

重构后测试的结果非常好,以前各种网络问题都没有复现,断线对玩家来说是不可见的,只要长时间断线(三次重连失败)才会给玩家一个提示框,让玩家手动点击去重连。

//----------------------------------------------
//           NET        : NetManager
//           AUTHOR: zhu kun qian
//----------------------------------------------

using System.IO;

public class NetMsg
{
    // 有必要用id吗?
    // 
    public cmd.CmdType cmdType;
    public uint loop;// 唯一的循环id
    public byte[] data;// protobuf二进制数据
    public byte[] totalData;//完整的消息二进制数据
    public MemoryStream netdata;

    // 这里能避免一次array copy不?
    // TODO:暂不考虑这避免arry copy的消耗,以后有时间时,可以考虑处理下。
}

 

//----------------------------------------------
//           NET        : NetManager
//           AUTHOR: zhu kun qian
//----------------------------------------------

using System.Net;
using System.Net.Sockets;
using System.Runtime.Remoting.Messaging;
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;

/*
 * 没有使用任何阻塞函数。
 * 
 * connect是block函数,所以改为使用ConnectAsync, beginConnect也是异步,也可以使用
 * 
 * 连接成功后,socket修改为non-blocking
*/
public class NetManagerSocket
{

    // 如果断开连接,直接new socket

    public string ip;
    public int port;

    private Socket socket;
    private byte[] readBuf = new byte[64 * 1024];// 最高缓存接收64k数据。

    private int readBufIndex = 0;//已经从readBuf中读取的字节数
    private int available = 0;// readbuf已经从socket中接收的字节数

    public SocketError socketError = SocketError.Success;// 这里应该是传递到上层,还是在这里处理?

    // ---------------------------------------
    // -- decoder相关
    private int length = 0;// protobuf消息体中的长度
    private int needRead = 0;// protobuf消息体中剩余需要读取的长度
    // ---------------------------------------

    public void connect()
    {
        socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
        socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, 5000);// 设置5秒发送超时
        socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, false);
        socket.NoDelay = true;
        // socket.Blocking = false;
        socket.Blocking = true;//设置为非阻塞

        socketError = SocketError.Success;

        Debug.Log("dontLinger:" + socket.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger));
        try
        {
            // 这里需要修改为异步connect
            // http://msdn.microsoft.com/en-us/library/d7ew360f%28v=vs.110%29.aspx
            // Connect method will block, unless you specifically set the Blocking property to false prior to calling Connect.
            // If you are using a connection-oriented protocol like TCP and you do disable blocking, Connect will throw a SocketException because it needs time to make the connection
            // 结论:很奇怪,并没有按文档上描述的执行,设置为blocking,并没有抛出异常。

            // socket.Connect(ip, port);

            // The asynchronous BeginConnect operation must be completed by calling the EndConnect method.
            // IAsyncResult result= socket.BeginConnect(ip, port, new AsyncCallback(connectCallback), socket);

            // Debug.Log("asyncResult is completed:"+result.IsCompleted);

            // 异步处理必须设置为blocking,why?
            //*
            SocketAsyncEventArgs eventArgs = new SocketAsyncEventArgs();
            eventArgs.RemoteEndPoint = new IPEndPoint(IPAddress.Parse(ip), port);
            eventArgs.Completed += new EventHandler<SocketAsyncEventArgs>(connectCallback2);
            eventArgs.UserToken = socket;

            socket.ConnectAsync(eventArgs);

            // */
            // 这里需要添加超时处理吗?
        }
        catch (Exception ex)
        {
            Debug.LogError("socket connection exception:" + ex);
            socketError = SocketError.ConnectionRefused;
        }
    }

    private void connectCallback2(object src, SocketAsyncEventArgs result)
    {
        Debug.Log("connectCallback2, isCompleted:" + result + " " + result.SocketError);
        if (result.SocketError != SocketError.Success)
        {
            socketError = result.SocketError;
        }
        else
        {
            Socket socket = (Socket)src;
            socket.Blocking = false;
        }
    }

    void connectCallback(IAsyncResult result)
    {
        Debug.Log("connectCallback22, isCompleted:" + result.IsCompleted);
        // connected = true;
        try
        {
            Socket socket = (Socket)result.AsyncState;
            socket.EndConnect(result);

            Debug.Log("socket is connected:" + socket.Connected);
            socket.Send(new byte[] { });
        }
        catch (Exception e)
        {
            Debug.LogError("error:" + e);
            socketError = SocketError.ConnectionRefused;
        }
    }

    public void disconnect()
    {
        if (socket != null)
        {
            socket.Close();
            socket = null;
        }
    }

    //

    public NetMsg read()
    {
        // 这里同步读也可以,但只读数据接收到足够一个消息的情况
        // 使用availed预判进行同步读,不会阻塞主线程
        // 解析消息放在这里。
        if (socket != null && socket.Available > 0)
        {
            if (available == 0)
            {
                if (socket.Available < 12)
                {
                    // 一个数据包,最小为12字节
                    return null;
                }
                // 开始从socket中读入一条数据
                // 如果足够,就直接解析为消息返回
                // 如果不够,就将数据放在cache中

                socketRead(2);
                if (socketError != SocketError.Success)
                {
                    return null;
                }

                length = readUshort();

                if (length == 0)
                {
                    socketRead(4);
                    if (socketError != SocketError.Success)
                    {
                        return null;
                    }
                    length = readInt();
                }
                int socketAvailable = socket.Available;
                needRead = length;
                if (socketAvailable < needRead)
                {
                    // 不足时,socket有多少缓存数据就读多少
                    socketRead(socketAvailable);
                    if (socketError != SocketError.Success)
                    {
                        return null;
                    }

                    needRead = needRead - socketAvailable;
                    return null;
                }
                else
                {
                    // 数据足够,解析数据
                    socketRead(needRead);
                    if (socketError != SocketError.Success)
                    {
                        return null;
                    }
                    return readMsg();
                }
            }
            else
            {
                // 继续从socket中接收数据
                int socketAvailable = socket.Available;
                if (socketAvailable < needRead)
                {
                    // 数据依旧不足
                    socketRead(socketAvailable);
                    if (socketError != SocketError.Success)
                    {
                        return null;
                    }

                    needRead = needRead - socketAvailable;
                    return null;
                }
                else
                {
                    // 数据足够,解析数据
                    socketRead(needRead);
                    if (socketError != SocketError.Success)
                    {
                        return null;
                    }

                    return readMsg();
                }
            }
        }
        return null;
    }

    private void readReset()
    {
        // 读入一个完整消息后,重置数据
        available = 0;
        readBufIndex = 0;
        length = 0;
        needRead = 0;
    }
    private NetMsg readMsg()
    {
        NetMsg netMsg = new NetMsg();

        UInt32 loop = readUInt();
        short cmdId = readShort();

        byte[] protoData = null;
        if (cmdId > 0)
        {
            protoData = new byte[length - 10];
            Array.Copy(readBuf, readBufIndex, protoData, 0, length - 10);
        }
        else
        {
            // 有压缩
            MemoryStream inms = new MemoryStream(readBuf, readBufIndex, length - 10);
            MemoryStream outms = new MemoryStream();
            SevenZipTool.Unzip(inms, outms);
            protoData = outms.ToArray();
            cmdId = (short)-cmdId;
        }
        

        netMsg.loop = loop;
        netMsg.cmdType = (cmd.CmdType)cmdId;
        netMsg.data = protoData;
        netMsg.netdata=new MemoryStream(protoData);
        readReset();
        return netMsg;
    }

    private short readShort()
    {
        short ret = BitConverter.ToInt16(readBuf, readBufIndex);
        readBufIndex += 2;
        return Endian.SwapInt16(ret);
    }
    private ushort readUshort()
    {
        ushort ret = BitConverter.ToUInt16(readBuf, readBufIndex);
        readBufIndex += 2;
        return Endian.SwapUInt16(ret);
    }

    private int readInt()
    {
        int ret = BitConverter.ToInt32(readBuf, readBufIndex);
        readBufIndex += 4;
        return Endian.SwapInt32(ret);
    }

    private uint readUInt()
    {
        uint ret = BitConverter.ToUInt32(readBuf, readBufIndex);
        readBufIndex += 4;
        return Endian.SwapUInt32(ret);

    }
    private void socketRead(int readLen)
    {    //从socket中读入数据入在readBuf中
        socket.Receive(readBuf, available, readLen, SocketFlags.None, out socketError);
        if (socketError != SocketError.Success)
        {
            Debug.LogError("socket Read error:" + socketError);
        }
        available += readLen;
    }

    public int send(NetMsg netMsg, int offset)
    {
        if (netMsg.totalData == null)
        {
            encode(netMsg);
        }

        int sendNum = socket.Send(netMsg.totalData, offset, netMsg.totalData.Length - offset, SocketFlags.None, out socketError);
        if (socketError != SocketError.Success)
        {
            Debug.LogError("socket send error:" + socketError);
            return 0;
        }

        if (sendNum + offset == netMsg.totalData.Length)
        {
            return -1;//标志,全部发送完成
        }
        return sendNum;
    }


    private void encode(NetMsg netMsg)
    {
        MemoryStream outstream = new MemoryStream();
        byte[] _t = null;

        int totalLength = netMsg.data.Length + 10;
        if (totalLength > 65535)
        {
            _t = BitConverter.GetBytes(Endian.SwapInt16((short)0));
            outstream.Write(_t, 0, _t.Length);
            _t = BitConverter.GetBytes(Endian.SwapInt32(totalLength));
            outstream.Write(_t, 0, _t.Length);
        }
        else
        {
            _t = BitConverter.GetBytes(Endian.SwapInt16((short)totalLength));
            outstream.Write(_t, 0, _t.Length);
        }

        _t = BitConverter.GetBytes(Endian.SwapUInt32(netMsg.loop));
        outstream.Write(_t, 0, _t.Length);
        _t = BitConverter.GetBytes(Endian.SwapInt16((short)netMsg.cmdType));
        outstream.Write(_t, 0, _t.Length);
        outstream.Write(netMsg.data, 0, netMsg.data.Length);
        _t = BitConverter.GetBytes(Endian.SwapInt32((int)0));
        outstream.Write(_t, 0, _t.Length);

        _t = outstream.ToArray();
        netMsg.totalData = _t;
    }

    public bool isConnected()
    {
        return socket != null && (socket.Connected);
    }
}
//----------------------------------------------
//           NET        : NetManager
//           AUTHOR: zhu kun qian
//----------------------------------------------

using System;
using System.Linq;
using System.Net.Sockets;

using UnityEngine;
using System.Collections.Generic;
using System.IO;


public class NetManager
{
    public uint loop;// 永不重复,一直加1

    public string ip = "";
    public int port = 0;

    private NetManagerSocket socket = null;

    private int maxQuerySize = 100;//接收和发送最多cache 100条消息
    private int maxReconnectTimes = 3;//重连最多重试3次

    // 发送相关
    public Queue<NetMsg> msgQuery = new Queue<NetMsg>(); // 发送消息队列
    public NetMsg reconnectTokenLogin = null;//重连发送的登陆消息
    public bool loginSuccess = false;

    private int sendBytes = 0;
    private float sendBytesTimeout = 0f; // 发送消息超时

    // 接收的消息队列
    private Queue<NetMsg> receviedMsgQueue = new Queue<NetMsg>();// 从服务器接收到的消息

    private int reconnectTryTimes = 0; // 重连次数
    private float connectTimeout = 0f; // 连接超时
    public EventDelegate.Callback reconnectErrorCallback; // 如果出现内部无法处理的得连(1、连试3次无法重连成功 2、累积的消息数量过量,需要重启游戏客户端)
    

    // 如果断线后,是使用原socket重连,还是使用新的socket?新new出来
    public static NetManager Instance = null;
    public NetManager()
    {
        Instance = this;
    }

    public bool isConnected()
    {
        return socket != null && socket.isConnected();
    }
    public void connect()
    {
        if (socket != null)
        {
            Debug.LogError("socket is not closed,try close");
            socket.disconnect();
        }

        Debug.Log("start connect ip:" + ip + " port:" + port);

        socket = new NetManagerSocket();
        socket.ip = ip;
        socket.port = port;
        socket.connect();

        sendBytes = 0;
        sendBytesTimeout = 0f;

        connectTimeout = 0f;
    }

    public void disconnect()
    {
        if (socket == null)
        {
            Debug.LogError("socket is null");
            return;
        }
        socket.disconnect();
        socket = null;
    }

    public void onUpdate()
    {
        // 每祯执行,没有阻塞
        if (socket != null)
        {
            // 改为在socket中处理呢
            if (socket.socketError != SocketError.Success)
            {
                // 如果遇到sokcet错误,不需要等等超时,立即重连
                Debug.LogError("socket error:" + socket.socketError);
                tryReconnect();
                return;
            }

            if (socket.isConnected())
            {
                NetMsg msg = socket.read();
                if (msg != null)
                {
                    receviedMsgQueue.Enqueue(msg);
                }
                NetMsg netMsg = null;
                if (reconnectTokenLogin != null)
                {
                    netMsg = reconnectTokenLogin;
                }
                else
                {
                    if (loginSuccess)
                    {
                        lock (msgQuery)
                        {
                            if (msgQuery.Count > 0)
                            {
                                netMsg = msgQuery.First();
                            }
                        }
                    }
                }
                if (netMsg != null)
                {
                    socketSend(netMsg);
                }
            }
            else
            {
                if (connectTimeout == 0f)
                {
                    connectTimeout = Time.realtimeSinceStartup;
                }
                else if (Time.realtimeSinceStartup - connectTimeout > 5)
                {
                    // 连接5秒超时,处理重连
                    tryReconnect();
                }
            }
        }
    }

    private void tryReconnect()
    {
        if (reconnectTryTimes >= 3)
        {
            // 跳出错误处理回调,交给外部处理
            if (reconnectErrorCallback != null)
            {
                reconnectErrorCallback();
            }
            disconnect();
            return;
        }
        Debug.LogError("socket connect timeout, try reconnect:" + reconnectTryTimes + " " + socket.socketError);
        reconnectTryTimes++;
        disconnect();
        connect();
        // 重试需要,重新发送登陆消息505
        LoginState.Instance.loginWithTokenSub(true);

    }

    public NetMsg readMsg()
    {
        if (receviedMsgQueue.Count == 0)
        {
            return null;
        }
        return receviedMsgQueue.Dequeue();
    }

    // true:超时
    private void socketSend(NetMsg netMsg)
    {
        // 发送数据
        bool newMsg = false;// 新发送的消息
        if (sendBytes == 0)
        {
            newMsg = true;

        }
        int num = socket.send(netMsg, sendBytes);
        if (num > 0)
        {
            sendBytes += num;
        }
        if (num == -1)
        {
            // 全部发送完成
            if (cmd.CmdType.tokenLogin == netMsg.cmdType)
            {
                reconnectTokenLogin = null;
            }
            else
            {
                lock (msgQuery)
                {
                    msgQuery.Dequeue();
                }
            }
            sendBytes = 0;
            sendBytesTimeout = 0f;
        }
        else
        {
            // 未发送完成,处理超时逻辑
            if (newMsg)
            {
                sendBytesTimeout = Time.realtimeSinceStartup;
            }
            else
            {
                // 检查时间是否超时
                if (Time.realtimeSinceStartup - sendBytesTimeout > 5)
                {
                    // 超过5秒
                    Debug.LogError("socket timeout.try reconnect");
                    // 重连重发
                    if (socket.socketError != SocketError.Success)
                    {
                        Debug.LogError("socket error:" + socket.socketError);
                    }
                    socket.socketError = SocketError.TimedOut;
                }
            }
        }
    }

    public void SendCmd<T>(cmd.CmdType cmdType, T protoObj)
    {
        send(cmdType,protoObj);
    }
    public void send<T>(cmd.CmdType cmdType, T protoObj)
    {
        NetMsg netMsg = new NetMsg();
        netMsg.loop = ++loop;
        netMsg.cmdType = cmdType;

        MemoryStream outms = new MemoryStream();
        ProtoBuf.Serializer.Serialize(outms, protoObj);
        netMsg.data = outms.ToArray();

        // todo:因为放在onupdate中,感觉这个lock也是可以避免掉的。暂时先加上,以后测试后再考虑去掉。
        // 只要能确认不会多线程操作,就可以去掉这个lock
        if (cmdType == cmd.CmdType.tokenLogin)
        {
            reconnectTokenLogin = netMsg;
            loginSuccess = false;
            return;
        }
        lock (msgQuery)
        {
            msgQuery.Enqueue(netMsg);
            // 在onupdate中发送,这样只差3ms,是可以接受的
        }

        if (msgQuery.Count > maxQuerySize)
        {
            Debug.LogError("msgQuery more than max size:" + msgQuery.Count);
        }
    }
}








2015.01.19记:我重构时,未添加断线补发未送达的消息,我们客户端写的逻辑不严谨,在登陆游戏按钮上连续多次点击,会导致卡住登陆。我这里只是使用禁止多次点击来避免这个问题,如果使用有断线补发未送达的消息也可以不会出现这个问题。总之,不要依赖客户端代码很强壮,最好还是添加上断线补发消息。

opengles中画四边型

opengles中仅支持以下几种模型:

/* BeginMode */
#define GL_POINTS                         0x0000
#define GL_LINES                          0x0001
#define GL_LINE_LOOP                      0x0002
#define GL_LINE_STRIP                     0x0003
#define GL_TRIANGLES                      0x0004
#define GL_TRIANGLE_STRIP                 0x0005
#define GL_TRIANGLE_FAN                   0x0006

所以如果需要画一个四边型,需要借助GL_TRIANGLES_STRIP

static const GLfloat vertices[] = { -1.0, 1.0, -0.0, 1.0, 1.0, -0.0, -1.0,
-1.0, -0.0, 1.0, -1.0, -0.0 };

static const GLfloat texCoords[] =
{ 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0 };

glBindTexture(GL_TEXTURE_2D, 1);
glVertexPointer(3, GL_FLOAT, 0, vertices);
glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

如此即可画出一个四边型

android&ios

curl库可以用在android ndk及ios上。

所以网络相关的功能,可以使用curl进行开发。

java.io.IOException: error=12, Cannot allocate memory 解决方案

java调用本地指令报以上错误。

执行:

echo 1 > /proc/sys/vm/overcommit_memory

后,错误消失。

js调用flash需要注意地方

获取flash对象的方法。

在网上查找的很多js获取flash对象的方法,不兼容IE9,这里提供一个能兼容IE9的方法。同时兼容其它浏览器。

function thisMovie(movieName) {
if (navigator.appName.indexOf(“Microsoft”) != -1) {
//alert(“IE”);
if (typeof (window[movieName].qqBuyUpdate) == ‘function’) {
// < IE9
return  window[movieName];
}
else if (typeof (document[movieName].qqBuyUpdate) == ‘function’) {
// >= IE9
return  document[movieName];
}
}
else {
// NON IE
return  document[movieName];
}
}

 

在js里面调用flash:

thisMovie(“client”).qqBuyUpdate();

mingw下cdt中console不输出

To add this flag in Eclipse, go to Project->Properties. Under “C++ Build”, select “Settings”. Under “MinGW C++ Linker”, select “Miscellaneous”. Add “-static-libgcc” to the “Linker flags” field. You may have to do this for your other build configurations and projects.

参考:

http://www.eclipse.org/forums/index.php/mv/tree/156519/

jsp中取客户端ip

一直以来取客户端的ip使用的是:

getRequest().getRemoteAddr();

在绝大多数情况下,这是可以正确取到客户端的ip的。

昨天服务器改为使用nginx转发后,无法获得到客户端的真实ip,只能取到nginx所以的服的ip。

在网上查询后了角到nginx要设置http header: X-Real-IP才可以取到真实IP。

proxy_set_header        X-Real-IP       $remote_addr;
proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;

设置之后,果断无效,查询对比多次,配置无误。

最后终于发现:这种情况下,应该改为先取header中的x-real-ip。如果为空的话,再去调用getRemoteAddr();

String realIP = getRequest().getHeader(“X-Real-IP”);
return realIP != null ? realIP : getRequest().getRemoteAddr();

这样似乎解决掉问题了。

但又遇到另一个棘手的事情了。

我们的程序要通过一个统一的nginx进行proxy转发至目标主机,然后在目标主机上再用proxy转发至目标主机tomcat所在端口8080。通过这两次转发后,x-real-ip取到的是第一次进行转发的主机ip,这仍旧不是我们所希望的。

我们所要求的是无论通过直接访问目标主机,还是通过proxy代理访问主机,都希望获得客户端请求的ip,最终解决方案如下:

在统一nginx转发的proxy上配置:

proxy_set_header        X-Real-IP       $remote_addr;
proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;

在目标主机的nginx上proxy配置:

proxy_set_header X-Forwarded-For  $http_x_forwarded_for;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

修改代码如下:

String address = getRequest().getHeader(“X-Forwarded-For”);
if (address != null && address.length() > 0
&& !”unknown”.equalsIgnoreCase(address)) {
return address;
}
String realIP = getRequest().getHeader(“X-Real-IP”);
return realIP != null ? realIP : getRequest().getRemoteAddr();

呜呼,终于能正确的取到客户端ip了。