因为最近要从公司离职,害怕用nio写的网络程序没有人能看懂(或许是因为写的不好吧),就调整成了mina(这样大家接触起来非常方便,即使没有socket基础,用起来也不难),所以之前基于nio写的网络程序就开放出来好了!

写的比较挫,大家见谅!

首先是PollServer类,主要处理select,做网络事件的监听和基于FutureTask的数据发送,代码如下:

  

package gs.gate;

import gs.gate.handle.ClientHandle;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.Vector;
import java.util.concurrent.FutureTask;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

import org.apache.log4j.Logger;

import door.HeartTimer;

public class PollServer implements Runnable
{
    private Logger log = Logger.getLogger(getClass());
    private Selector select = null;
    private ServerSocketChannel serverSocketChannel = null;
    private HeartTimer writerExpire = null;
    private volatile boolean run = true;

    private List<FutureTask<Integer>> writeTasks =
            new Vector<FutureTask<Integer>>();

    public PollServer(String host,int port) throws IOException
    {
        select = Selector.open();
        serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.socket().setReuseAddress(true);
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.socket().bind(new InetSocketAddress(host, port));
        serverSocketChannel.register(select, SelectionKey.OP_ACCEPT);
    }

    public Selector getSelector()
    {
        return this.select;
    }

    public void stop()
    {
        this.run = false;
    }

    public void run()
    {
        if(writerExpire == null)
        {
            writerExpire = new HeartTimer(50);
        }
        while(this.run)
        {

            try
            {
                this.listen();
            }
            catch (Exception e)
            {
                log.info("PollServer listen() err! " + e.toString());
            }

            try
            {
                List<FutureTask<Integer>> writeTasks_ = null;
                synchronized (writeTasks)
                {
                    writeTasks_ = new ArrayList<FutureTask<Integer>>(this.writeTasks);
                    this.writeTasks.clear();
                }

                for(FutureTask<Integer> task : writeTasks_)
                {
                    task.run();
                }
            }
            catch (Exception e)
            {
                log.error("PollServer processOutput() err" ,e);
            }
        }
    }

    public void listen() throws IOException
    {

        select.select(10);
        Set<SelectionKey> readyKeys = select.selectedKeys();
        Iterator<SelectionKey> itr = readyKeys.iterator();

        //处理接受
        while(itr.hasNext())
        {
            SelectionKey key = itr.next();
            itr.remove();

            if(key.isAcceptable())
            {
                SocketChannel newConnection = serverSocketChannel.accept();
                this.addClient(newConnection);
            }
            else if(key.isReadable())
            {
                ClientHandle handle = (ClientHandle)key.attachment();
                try
                {
                    if(handle.handleRead() <= 0)
                    {
                        log.info("if handleRead < 0");
                        this.removeClient(handle);
                    }
                }
                catch (Exception e)
                {
                    log.error("exception",e);
                    this.removeClient(handle);
                }
            }
            else if(key.isWritable())
            {
                ClientHandle handle = (ClientHandle)key.attachment();
                try
                {
                    handle.handleWrite();
                    if(handle.hasRemaining() == false)
                    {
                        key.cancel();
                    }
                }
                catch (Exception e)
                {
                    this.removeClient(handle);
                    log.error("if handleWrite error",e);
                }
            }

        }
    }

    public void addWriteTask(FutureTask<Integer> future)
    {
        this.writeTasks.add(future);
    }

    public void addClient(SocketChannel socket)
    {
        ClientHandle handle = new ClientHandle(socket,this);
        try
        {
            socket.socket().setTcpNoDelay(run);
            socket.configureBlocking(false);
            socket.register(select, SelectionKey.OP_READ,handle);
        }
        catch (Exception e)
        {
            try
            {
                log.error("create client err",e);
                socket.close();
            }
            catch (Exception  err)
            {}

        }
    }

    public void removeClient(ClientHandle handle)
    {
        if(handle == null)
        {
            return ;
        }

        log.info(" remove Client ");
        handle.handleDisConnected();
    }

}

主要函数: listen();作用:基于网络事件处理接受新链接和消息的接收!

主要函数: processOutput(); 作用: 做统一的发送处理,在这篇 浅谈游戏服务器的发送数据处理 中有讲解!每个连接在发送的时候,将数据和连接封装成FutureTask,然后投递到Pollserver中的安全队列中,在这里统一将安全队列中的任务执行完毕! 如果有数据没有发送完毕,就监听写时间,直到这个链接成为可写事件(即:写缓冲区中有空闲)。

下面是ClientHandle类的代码,做每个连接的处理,比如拆包分包,代码如下

package gs.gate.handle;

import gs.gate.PollServer;

import java.io.IOException;
import java.net.InetAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.concurrent.FutureTask;

import org.apache.log4j.Logger;

import dc.control.DCThread;
import dc.util.DcTask;
import door.IPlayer;
import senv.server.ServerKit;
import slib.net.ISession;
import slib.util.ByteBuffer;

public class ClientHandle implements ISession
{
    private Logger log = Logger.getLogger(getClass());
    public final static int RW_BUFFER_SIZE = 1024;
    private SocketChannel socket = null;

    private java.nio.ByteBuffer reader = java.nio.ByteBuffer.allocate(8*RW_BUFFER_SIZE);
    private java.nio.ByteBuffer writer = java.nio.ByteBuffer.allocate(10*RW_BUFFER_SIZE);

    private volatile IPlayer player = null;

    private PollServer poll = null;

    private boolean active = false;

    private boolean tgwProcessed = false;

    private String tgw = "tgw_l7_forward\r\nHost:" + ServerKit.ip+ ":" + ServerKit.port + "\r\n\r\n";

    public ClientHandle(SocketChannel socket,PollServer poll)
    {
        this.socket = socket;
        this.writer.limit(this.writer.capacity());
        this.active = true;
        this.poll = poll;

        try
        {
            this.socket.socket().setSendBufferSize(10*RW_BUFFER_SIZE);
            this.socket.socket().setReceiveBufferSize(8*RW_BUFFER_SIZE);
            this.socket.socket().setTcpNoDelay(true);
            this.socket.socket().setSoLinger(true, 3600);
        }
        catch (Exception e)
        {
            log.error("err",e);
        }

    }

    public SocketChannel getSocketChannel()
    {
        return this.socket;
    }

    public int handleWrite() throws IOException
    {
        if (!this.isActive())
        {
            return -1;
        }

        this.writer.flip();
        this.socket.write(writer);
        if(this.writer.hasRemaining())
        {
            this.writer.compact();
        }
        else
        {
            this.writer.clear();
        }

        return 0;
    }

    public boolean hasRemaining()
    {
        return this.writer.position() > 0;
    }

    public Integer writeData(ByteBuffer data) throws IOException
    {
        if (!this.isActive())
        {
            return -1;
        }
        if(data == null)
        {
            return 0;
        }

        this.writer.putInt(data.length());
        this.writer.put(data.toByteArray(), 0, data.length());
        this.writer.flip();

        int result = this.socket.write(this.writer);

        if(this.writer.hasRemaining())
        {
            this.writer.compact();
        }
        else
        {
            this.writer.clear();
        }
        return result;
    }

    @SuppressWarnings("deprecation")
    public int handleRead() throws IOException
    {
        if(socket == null)
        {
            return -1;
        }
        if(!this.isActive())
        {
            return -1;
        }

        int r = this.socket.read(this.reader);
        if(r <= 0)
        {
            return r;
        }

        if(this.tgwProcessed == false)
        {
            //腾讯平台 你mb
            this.reader.flip();
            byte bytes[] = new byte[tgw.length()];
            this.reader.get(bytes);

            String vali = new String(bytes,"UTF-8");
            if(vali.equals(tgw))
            {
                log.info("tgw 校验成功");
            }
            else
            {
                log.info("tgw 校验失败");
            }
            this.tgwProcessed = true;
            this.reader.compact();
        }

        while(true)
        {
            this.reader.flip();
            ByteBuffer data = this.createBuffer();
            if(data == null)
            {
                break;
            }

            this.reader.get(data.getByteArray(), data.top(), data.capacity());
            this.processData(data);

            if(this.reader.hasRemaining())
            {
                this.reader.compact();
            }
            else
            {
                this.reader.clear();
                break;
            }
        }
        return 1;
    }

    public void processData(ByteBuffer data)
    {
        if(player == null)
        {
            DcTask task = new DcTask();
            task.object = this;
            task.data = data;
            DCThread.getInstance().insertTask(task);
        }
        else
        {
            player.insertData(data);
        }
    }

    public void handleDisConnected()
    {
        if(!this.isActive())
        {
            return ;
        }
        if(player != null)
        {
            this.player.setSession(null);
            this.player.logOut();
        }
        this.close();
        this.player = null;
    }

    private ByteBuffer createBuffer()
    {
        if(reader.remaining() < 4)
        {
            return null;
        }

        int len = reader.getInt();
        if(len > reader.remaining())
        {
            this.reader.rewind();
            this.reader.compact();
            return null;
        }

        if (len > 0 && len <= 10 * 1024)
        {
            return new ByteBuffer(len);
        }

        return null;
    }

    @Override
    public void close()
    {
        setActive(false);
        try
        {
            log.error("socket close !");
            this.socket.close();
            this.socket.keyFor(this.poll.getSelector()).cancel();
        }
        catch (Exception e)
        {
            log.error("err" , e);
        }
    }

    @Override
    public long getActiveTime()
    {
        return 0;
    }

    @Override
    public InetAddress getAddress()
    {
        return null;
    }

    @Override
    public String getCode()
    {
        return null;
    }

    @Override
    public int getPing()
    {
        return 0;
    }

    @Override
    public long getPingTime()
    {
        return 0;
    }

    @Override
    public int getPort()
    {
        return 0;
    }

    @Override
    public int getServerId()
    {
        return 0;
    }

    @Override
    public int getSessionId()
    {
        return 0;
    }

    @Override
    public Object getSource()
    {
        return this.player;
    }

    @Override
    public int getTimeout()
    {
        return 0;
    }

    @Override
    public boolean isActive()
    {
        return this.active;
    }

    @Override
    public void send(ByteBuffer data)
    {
        WriteTask task = new WriteTask(this,data);
        FutureTask<Integer> future = new FutureTask<Integer>(task);
        poll.addWriteTask(future);
    }

    @Override
    public void send(ByteBuffer arg0, ByteBuffer arg1)
    {

    }

    @Override
    public void setCode(String arg0)
    {

    }

    @Override
    public void setPing(int arg0)
    {

    }

    @Override
    public void setPingTime(long arg0)
    {

    }

    public void enableWriteEvent()
    {
        try
        {
            this.socket.register(this.poll.getSelector(), SelectionKey.OP_WRITE, this);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    public void shutdownWriteEvent()
    {

    }

    @Override
    public void setSource(Object arg0)
    {
        this.player = (IPlayer) arg0;
    }

    protected void setActive(boolean b)
    {
        this.active = b;
    }

}

重要的几个函数:  send(ByteBuffer data) 将发送处理包装成FutureTask,投递到PollServer中进行处理,就是PollServer::processOutput中处理

handleRead() 这里处理接受数据事件,做了拆包,将二进制数据,按照 长度-内容 的格式进行解析,拆分成一个个ByteBuffer(定义见下文)包,然后进行处理。

ClientHandle继承自ISession接口,其实这个无所谓,大家可以自己定义。我这里因为要和之前的系统兼容,所以才继承了这个。这里一不小心居然用到了适配器模式,我以为这辈子只会用到创建者模式呢? 个人还是觉得,这些设计模式还是为了解决问题用到的,而不是为了多变的需求而想太多用到的;设计模式用得多用的频繁,反而增加代码的可读性!

最后看下WriteTask的封装

package gs.gate.handle;

import java.util.concurrent.Callable;
import slib.util.ByteBuffer;

public class WriteTask implements Callable<Integer>
{

    private ClientHandle client = null;

    private ByteBuffer data = null;

    public WriteTask(ClientHandle handle,ByteBuffer data)
    {
        client = handle;
        this.data = data;
    }

    @Override
    public Integer call() throws Exception
    {
        return this.client.writeData(data);
    }

}

这个就不多解析了!

缺陷1: 没有做空闲连接的处理,后来的mina库,提供了这个功能!有兴趣的同学自己写个吧!

缺陷2: 自定义的消息包,用了ByteBuffer类,和nio提供的ByteBuffer 重复!

给出自定义的ByteBuffer的处理:

package slib.util;

/**
 * 类说明:字节缓存类,字节操作高位在前,低位在后
 *
 * @version 1.0
 * @author fxxxysh <hanshuang@linekong.com>
 */

public class ByteBuffer
{

    /* static fields */
    /** 默认的初始容量大小 */
    public static final int CAPACITY = 32;

    /** 默认的动态数据或文字的最大长度,400k */
    public static final int MAX_DATA_LENGTH = 400 * 1024;

    /* fields */
    /** 字节数组 */
    byte[] bytes;

    /** 字节缓存的长度 */
    int top;

    /** 字节缓存的偏移量 */
    int offset;

    /* constructors */
    /** 按默认的大小构造一个字节缓存对象 */
    public ByteBuffer()
    {
        this(CAPACITY);
    }

    /** 按指定的大小构造一个字节缓存对象 */
    public ByteBuffer(int capacity)
    {
        if (capacity < 1)
            throw new IllegalArgumentException(getClass().getName()
                    + " <init>, invalid capatity:" + capacity);
        bytes = new byte[capacity];
        top = 0;
        offset = 0;
    }

    /** 按指定的字节数组构造一个字节缓存对象 */
    public ByteBuffer(byte[] data)
    {
        if (data == null)
            throw new IllegalArgumentException(getClass().getName()
                    + " <init>, null data");
        bytes = data;
        top = data.length;
        offset = 0;
    }

    /** 按指定的字节数组构造一个字节缓存对象 */
    public ByteBuffer(byte[] data, int index, int length)
    {
        if (data == null)
            throw new IllegalArgumentException(getClass().getName()
                    + " <init>, null data");
        if (index < 0 || index > data.length)
            throw new IllegalArgumentException(getClass().getName()
                    + " <init>, invalid index:" + index);
        if (length < 0 || data.length < index + length)
            throw new IllegalArgumentException(getClass().getName()
                    + " <init>, invalid length:" + length);
        bytes = data;
        top = index + length;
        offset = index;
    }

    /* properties */
    /** 得到字节缓存的容积 */
    public int capacity()
    {
        return bytes.length;
    }

    /** 设置字节缓存的容积,只能扩大容积 */
    public void setCapacity(int len)
    {
        int c = bytes.length;
        if (len <= c)
            return;
        for (; c < len; c = (c << 1) + 1)
            ;
        byte[] temp = new byte[c];
        System.arraycopy(bytes, 0, temp, 0, top);
        bytes = temp;
    }

    /** 得到字节缓存的长度 */
    public int top()
    {
        return top;
    }

    /** 设置字节缓存的长度 */
    public void setTop(int top)
    {
        if (top < offset)
            throw new IllegalArgumentException(this + " setTop, invalid top:"
                    + top);
        if (top > bytes.length)
            setCapacity(top);
        this.top = top;
    }

    /** 得到字节缓存的偏移量 */
    public int offset()
    {
        return offset;
    }

    /** 设置字节缓存的偏移量 */
    public void setOffset(int offset)
    {
        if (offset < 0 || offset > top)
            throw new IllegalArgumentException(this
                    + " setOffset, invalid offset:" + offset);
        this.offset = offset;
    }

    /** 得到字节缓存的使用长度 */
    public int length()
    {
        return top - offset;
    }

    /** 得到字节缓存的字节数组,一般使用toArray()方法 */
    public byte[] getByteArray()
    {
        return bytes;
    }

    /* methods */
    /* byte methods */
    /** 得到指定偏移位置的字节 */
    public byte read(int pos)
    {
        return bytes[pos];
    }

    /** 设置指定偏移位置的字节 */
    public void write(int b, int pos)
    {
        bytes[pos] = (byte) b;
    }

    /* read methods */
    /**
     * 按当前偏移位置读入指定的字节数组
     *
     * @param data
     *            指定的字节数组
     * @param pos
     *            指定的字节数组的起始位置
     * @param len
     *            读入的长度
     */
    public void read(byte[] data, int pos, int len)
    {
        System.arraycopy(bytes, offset, data, pos, len);
        offset += len;
    }

    /** 读出一个布尔值 */
    public boolean readBoolean()
    {
        return (bytes[offset++] != 0);
    }

    /** 读出一个字节 */
    public byte readByte()
    {
        return bytes[offset++];
    }

    /** 读出一个无符号字节 */
    public int readUnsignedByte()
    {
        return bytes[offset++] & 0xff;
    }

    /** 读出一个字符 */
    public char readChar()
    {
        return (char) readUnsignedShort();
    }

    /** 读出一个短整型数值 */
    public short readShort()
    {
        return (short) readUnsignedShort();
    }

    /** 读出一个无符号的短整型数值 */
    public int readUnsignedShort()
    {
        int pos = offset;
        offset += 2;
        return (bytes[pos + 1] & 0xff) + ((bytes[pos] & 0xff) << 8);
    }

    /** 读出一个整型数值 */
    public int readInt()
    {
        int pos = offset;
        offset += 4;
        return (bytes[pos + 3] & 0xff) + ((bytes[pos + 2] & 0xff) << 8)
                + ((bytes[pos + 1] & 0xff) << 16) + ((bytes[pos] & 0xff) << 24);
    }

    /** 读出一个浮点数值 */
    public float readFloat()
    {
        return Float.intBitsToFloat(readInt());
    }

    /** 读出一个长整型数值 */
    public long readLong()
    {
        int pos = offset;
        offset += 8;
        return (bytes[pos + 7] & 0xffL) + ((bytes[pos + 6] & 0xffL) << 8)
                + ((bytes[pos + 5] & 0xffL) << 16)
                + ((bytes[pos + 4] & 0xffL) << 24)
                + ((bytes[pos + 3] & 0xffL) << 32)
                + ((bytes[pos + 2] & 0xffL) << 40)
                + ((bytes[pos + 1] & 0xffL) << 48)
                + ((bytes[pos] & 0xffL) << 56);
    }

    /** 读出一个双浮点数值 */
    public double readDouble()
    {
        return Double.longBitsToDouble(readLong());
    }

    /**
     * 读出动态长度, 数据大小采用动态长度,整数类型下,最大为512M 1xxx,xxxx表示(0~0x80) 0~128B
     * 01xx,xxxx,xxxx,xxxx表示(0~0x4000) 0~16K
     * 001x,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx表示(0~0x20000000) 0~512M
     */
    public int readLength()
    {
        int n = bytes[offset] & 0xff;
        if (n >= 0x80)
        {
            offset++;
            return n - 0x80;
        }
        else if (n >= 0x40)
            return readUnsignedShort() - 0x4000;
        else if (n >= 0x20)
            return readInt() - 0x20000000;
        else
            throw new IllegalArgumentException(this
                    + " readLength, invalid number:" + n);
    }

    /** 读出一个指定长度的字节数组,可以为null */
    public byte[] readData()
    {
        int len = readLength() - 1;
        if (len < 0)
            return null;
        if (len > MAX_DATA_LENGTH)
            throw new IllegalArgumentException(this
                    + " readData, data overflow:" + len);
        byte[] data = new byte[len];
        read(data, 0, len);
        return data;
    }

    /** 读出一个短字节数组,长度不超过254 */
    public byte[] readShortData()
    {
        int len = readUnsignedByte();
        if (len == 255)
            return null;
        byte[] data = new byte[len];
        if (len != 0)
            read(data, 0, len);
        return data;
    }

    /** 读出一个指定长度的字符串 */
    public String readString(int len)
    {
        byte[] data = new byte[len];
        if (len == 0)
            return "";
        read(data, 0, len);
        return new String(data);
    }

    /** 读出一个短字符串,长度不超过254 */
    public String readShortString()
    {
        int len = readUnsignedByte();
        if (len == 255)
            return null;
        return readString(len);
    }

    /** 读出一个字符串,长度不超过65534 */
    public String readString()
    {
        int len = readUnsignedShort();
        if (len == 65535)
            return null;
        return readString(len);
    }

    /** 读出一个指定长度和编码类型的字符串 */
    public String readUTF(String charsetName)
    {
        int len = readLength() - 1;
        if (len < 0)
            return null;
        if (len > MAX_DATA_LENGTH)
            throw new IllegalArgumentException(this
                    + " readUTF, data overflow:" + len);
        byte[] data = new byte[len];
        read(data, 0, len);
        if (charsetName == null)
            return new String(data);
        try
        {
            return new String(data, charsetName);
        }
        catch (Exception e)
        {
            throw new IllegalArgumentException(this
                    + " readUTF, invalid charsetName:" + charsetName);
        }
    }

    /** 读出一个指定长度的utf字符串 */
    public String readUTF()
    {
        int len = readLength() - 1;
        if (len < 0)
            return null;
        if (len == 0)
            return "";
        if (len > MAX_DATA_LENGTH)
            throw new IllegalArgumentException(this
                    + " readUTF, data overflow:" + len);
        StringBuffer sb = new StringBuffer(len);
        int pos = ByteKit.readUTF(bytes, offset, len, sb);
        if (pos > 0)
            throw new IllegalArgumentException(this
                    + " readUTF, format err, len=" + len + ", pos:" + pos);
        offset += len;
        return sb.toString();
    }

    /* write methods */
    /**
     * 写入指定字节数组
     *
     * @param data
     *            指定的字节数组
     * @param pos
     *            指定的字节数组的起始位置
     * @param len
     *            写入的长度
     */
    public void write(byte[] data, int pos, int len)
    {
        if (bytes.length < top + len)
            setCapacity(top + len);
        System.arraycopy(data, pos, bytes, top, len);
        top += len;
    }

    /** 写入一个布尔值 */
    public void writeBoolean(boolean b)
    {
        if (bytes.length < top + 1)
            setCapacity(top + CAPACITY);
        bytes[top++] = (byte) (b ? 1 : 0);
    }

    /** 写入一个字节 */
    public void writeByte(int b)
    {
        if (bytes.length < top + 1)
            setCapacity(top + CAPACITY);
        bytes[top++] = (byte) b;
    }

    /** 写入一个字符 */
    public void writeChar(int c)
    {
        writeShort(c);
    }

    /** 写入一个短整型数值 */
    public void writeShort(int s)
    {
        int pos = top;
        if (bytes.length < pos + 2)
            setCapacity(pos + CAPACITY);
        bytes[pos] = (byte) (s >>> 8);
        bytes[pos + 1] = (byte) s;
        top += 2;
    }

    /** 在指定位置写入一个短整型数值,length不变 */
    public void writeShort(int s, int pos)
    {
        if (bytes.length < pos + 2)
            setCapacity(pos + CAPACITY);
        bytes[pos] = (byte) (s >>> 8);
        bytes[pos + 1] = (byte) s;
    }

    /** 写入一个整型数值 */
    public void writeInt(int i)
    {
        int pos = top;
        if (bytes.length < pos + 4)
            setCapacity(pos + CAPACITY);
        bytes[pos] = (byte) (i >>> 24);
        bytes[pos + 1] = (byte) (i >>> 16);
        bytes[pos + 2] = (byte) (i >>> 8);
        bytes[pos + 3] = (byte) i;
        top += 4;
    }

    /** 在指定位置写入一个整型数值,length不变 */
    public void writeInt(int i, int pos)
    {
        if (bytes.length < pos + 4)
            setCapacity(pos + CAPACITY);
        bytes[pos] = (byte) (i >>> 24);
        bytes[pos + 1] = (byte) (i >>> 16);
        bytes[pos + 2] = (byte) (i >>> 8);
        bytes[pos + 3] = (byte) i;
    }

    /** 写入一个浮点数值 */
    public void writeFloat(float f)
    {
        writeInt(Float.floatToIntBits(f));
    }

    /** 写入一个长整型数值 */
    public void writeLong(long l)
    {
        int pos = top;
        if (bytes.length < pos + 8)
            setCapacity(pos + CAPACITY);
        bytes[pos] = (byte) (l >>> 56);
        bytes[pos + 1] = (byte) (l >>> 48);
        bytes[pos + 2] = (byte) (l >>> 40);
        bytes[pos + 3] = (byte) (l >>> 32);
        bytes[pos + 4] = (byte) (l >>> 24);
        bytes[pos + 5] = (byte) (l >>> 16);
        bytes[pos + 6] = (byte) (l >>> 8);
        bytes[pos + 7] = (byte) l;
        top += 8;
    }

    /** 写入一个双浮点数值 */
    public void writeDouble(double d)
    {
        writeLong(Double.doubleToLongBits(d));
    }

    /** 写入动态长度 */
    public void writeLength(int len)
    {
        if (len >= 0x20000000 || len < 0)
            throw new IllegalArgumentException(this
                    + " writeLength, invalid len:" + len);
        if (len >= 0x4000)
            writeInt(len + 0x20000000);
        else if (len >= 0x80)
            writeShort(len + 0x4000);
        else
            writeByte(len + 0x80);
    }

    /** 写入一个字节数组,可以为null */
    public void writeData(byte[] data)
    {
        writeData(data, 0, (data != null) ? data.length : 0);
    }

    /** 写入一个字节数组,可以为null */
    public void writeData(byte[] data, int pos, int len)
    {
        if (data == null)
        {
            writeLength(0);
            return;
        }
        writeLength(len + 1);
        write(data, pos, len);
    }

    /** 写入一个字符串,可以为null */
    public void writeString(String s)
    {
        if (s != null)
        {
            byte[] temp = s.getBytes();
            if (temp.length > 65534)
                throw new IllegalArgumentException(getClass().getName()
                        + " writeString, invalid s:" + s);
            writeShort(temp.length);
            if (temp.length != 0)
                write(temp, 0, temp.length);
        }
        else
            writeShort(65535);
    }

    /** 写入一个字符串,以指定的字符进行编码 */
    public void writeUTF(String str, String charsetName)
    {
        if (str == null)
        {
            writeLength(0);
            return;
        }
        byte[] data;
        if (charsetName != null)
        {
            try
            {
                data = str.getBytes(charsetName);
            }
            catch (Exception e)
            {
                throw new IllegalArgumentException(this
                        + " writeUTF, invalid charsetName:" + charsetName);
            }
        }
        else
            data = str.getBytes();
        writeLength(data.length + 1);
        write(data, 0, data.length);
    }

    /** 写入一个utf字符串,可以为null */
    public void writeUTF(String str)
    {
        writeUTF(str, 0, (str != null) ? str.length() : 0);
    }

    /** 写入一个utf字符串中指定的部分,可以为null */
    public void writeUTF(String str, int index, int length)
    {
        if (str == null)
        {
            writeLength(0);
            return;
        }
        int len = ByteKit.getUTFLength(str, index, length);
        writeLength(len + 1);
        int pos = top;
        if (bytes.length < pos + len)
            setCapacity(pos + len);
        ByteKit.writeUTF(str, index, length, bytes, pos);
        top += len;
    }

    /** 检查是否为相同类型的实例 */
    public boolean checkClass(Object obj)
    {
        return (obj instanceof ByteBuffer);
    }

    /** 在指定位置写入一个字节,length不变 */
    public void writeByte(int b, int pos)
    {
        if (bytes.length < pos + 1)
            setCapacity(pos + CAPACITY);
        bytes[pos] = (byte) b;
    }

    /** 得到字节缓存当前长度的字节数组 */
    public byte[] toByteArray()
    {
        byte[] data = new byte[top - offset];
        System.arraycopy(bytes, offset, data, 0, data.length);
        return data;
    }

    /** 清除字节缓存对象 */
    public void clear()
    {
        top = 0;
        offset = 0;
    }

    /* common methods */
    public int hashCode()
    {
        int hash = 17;
        for (int i = top - 1; i >= 0; i--)
            hash = 65537 * hash + bytes[i];
        return hash;
    }

    public boolean equals(Object obj)
    {
        if (this == obj)
            return true;
        if (!checkClass(obj))
            return false;
        ByteBuffer bb = (ByteBuffer) obj;
        if (bb.top != top)
            return false;
        if (bb.offset != offset)
            return false;
        for (int i = top - 1; i >= 0; i--)
        {
            if (bb.bytes[i] != bytes[i])
                return false;
        }
        return true;
    }

    public String toString()
    {
        return super.toString() + "[" + top + "," + offset + "," + bytes.length
                + "] ";
    }

}

相应的ByteKit类代码:

/**
 * Copyright 2001 by seasky <www.seasky.cn>.
 */

package slib.util;

/**
 * 类说明: 字节及字节数组的方法操作库
 *
 * @version 1.0
 * @author zminleo <zmin@seasky.cn>
 */

public final class ByteKit
{

    /* static fields */
    /** 库信息 */
    public static final String toString=ByteKit.class.getName();

    /* static methods */
    /** 在字节数组中指定位置读出一个布尔值 */
    public static boolean readBoolean(byte[] bytes,int pos)
    {
        return bytes[pos]!=0;
    }
    /** 在字节数组中指定位置读出一个字节 */
    public static byte readByte(byte[] bytes,int pos)
    {
        return bytes[pos];
    }
    /** 在字节数组中指定位置读出一个无符号字节 */
    public static int readUnsignedByte(byte[] bytes,int pos)
    {
        return bytes[pos]&0xff;
    }
    /** 在字节数组中指定位置读出一个字符 */
    public static char readChar(byte[] bytes,int pos)
    {
        return (char)readUnsignedShort(bytes,pos);
    }
    /** 在字节数组中指定位置读出一个字符,低位在前,高位在后 */
    public static char readChar_(byte[] bytes,int pos)
    {
        return (char)readUnsignedShort_(bytes,pos);
    }
    /** 在字节数组中指定位置读出一个短整型数值 */
    public static short readShort(byte[] bytes,int pos)
    {
        return (short)readUnsignedShort(bytes,pos);
    }
    /** 在字节数组中指定位置读出一个短整型数值,低位在前,高位在后 */
    public static short readShort_(byte[] bytes,int pos)
    {
        return (short)readUnsignedShort_(bytes,pos);
    }
    /** 在字节数组中指定位置读出一个无符号短整型数值 */
    public static int readUnsignedShort(byte[] bytes,int pos)
    {
        return (bytes[pos+1]&0xff)+((bytes[pos]&0xff)<<8);
    }
    /** 在字节数组中指定位置读出一个无符号短整型数值,低位在前,高位在后 */
    public static int readUnsignedShort_(byte[] bytes,int pos)
    {
        return ((bytes[pos+1]&0xff)<<8)+(bytes[pos]&0xff);
    }
    /** 在字节数组中指定位置读出一个整型数值 */
    public static int readInt(byte[] bytes,int pos)
    {
        return ((bytes[pos+3]&0xff))+((bytes[pos+2]&0xff)<<8)
            +((bytes[pos+1]&0xff)<<16)+((bytes[pos]&0xff)<<24);
    }
    /** 在字节数组中指定位置读出一个整型数值,低位在前,高位在后 */
    public static int readInt_(byte[] bytes,int pos)
    {
        return ((bytes[pos+3]&0xff)<<24)+((bytes[pos+2]&0xff)<<16)
            +((bytes[pos+1]&0xff)<<8)+((bytes[pos]&0xff));
    }
    /** 在字节数组中指定位置读出一个浮点数值 */
    public static float readFloat(byte[] bytes,int pos)
    {
        return Float.intBitsToFloat(readInt(bytes,pos));
    }
    /** 在字节数组中指定位置读出一个浮点数值,低位在前,高位在后 */
    public static float readFloat_(byte[] bytes,int pos)
    {
        return Float.intBitsToFloat(readInt_(bytes,pos));
    }
    /** 在字节数组中指定位置读出一个长整型数值 */
    public static long readLong(byte[] bytes,int pos)
    {
        return (bytes[pos+7]&0xffL)+((bytes[pos+6]&0xffL)<<8)
            +((bytes[pos+5]&0xffL)<<16)+((bytes[pos+4]&0xffL)<<24)
            +((bytes[pos+3]&0xffL)<<32)+((bytes[pos+2]&0xffL)<<40)
            +((bytes[pos+1]&0xffL)<<48)+((bytes[pos]&0xffL)<<56);
    }
    /** 在字节数组中指定位置读出一个长整型数值,低位在前,高位在后 */
    public static long readLong_(byte[] bytes,int pos)
    {
        return ((bytes[pos+7]&0xffL)<<56)+((bytes[pos+6]&0xffL)<<48)
            +((bytes[pos+5]&0xffL)<<40)+((bytes[pos+4]&0xffL)<<32)
            +((bytes[pos+3]&0xffL)<<24)+((bytes[pos+2]&0xffL)<<16)
            +((bytes[pos+1]&0xffL)<<8)+(bytes[pos]&0xffL);
    }
    /** 在字节数组中指定位置读出一个双浮点数值 */
    public static double readDouble(byte[] bytes,int pos)
    {
        return Double.longBitsToDouble(readLong(bytes,pos));
    }
    /** 在字节数组中指定位置读出一个双浮点数值,低位在前,高位在后 */
    public static double readDouble_(byte[] bytes,int pos)
    {
        return Double.longBitsToDouble(readLong_(bytes,pos));
    }
    /** 写入一个布尔值在字节数组中指定位置 */
    public static void writeBoolean(boolean b,byte[] bytes,int pos)
    {
        bytes[pos]=(byte)(b?1:0);
    }
    /** 写入一个字节在字节数组中指定位置 */
    public static void writeByte(int b,byte[] bytes,int pos)
    {
        bytes[pos]=(byte)b;
    }
    /** 在字节数组中指定位置写入一个字符 */
    public static void writeChar(int c,byte[] bytes,int pos)
    {
        writeShort(c,bytes,pos);
    }
    /** 写入一个字符在字节数组中指定位置,低位在前,高位在后 */
    public static void writeChar_(int c,byte[] bytes,int pos)
    {
        writeShort_(c,bytes,pos);
    }
    /** 写入一个短整型数值在字节数组中指定位置 */
    public static void writeShort(int s,byte[] bytes,int pos)
    {
        bytes[pos]=(byte)(s>>>8);
        bytes[pos+1]=(byte)s;
    }
    /** 写入一个短整型数值在字节数组中指定位置,低位在前,高位在后 */
    public static void writeShort_(int s,byte[] bytes,int pos)
    {
        bytes[pos]=(byte)s;
        bytes[pos+1]=(byte)(s>>>8);
    }
    /** 写入一个整型数值在字节数组中指定位置 */
    public static void writeInt(int i,byte[] bytes,int pos)
    {
        bytes[pos]=(byte)(i>>>24);
        bytes[pos+1]=(byte)(i>>>16);
        bytes[pos+2]=(byte)(i>>>8);
        bytes[pos+3]=(byte)i;
    }
    /** 在字节数组中指定位置写入一个整型数值,低位在前,高位在后 */
    public static void writeInt_(int i,byte[] bytes,int pos)
    {
        bytes[pos]=(byte)i;
        bytes[pos+1]=(byte)(i>>>8);
        bytes[pos+2]=(byte)(i>>>16);
        bytes[pos+3]=(byte)(i>>>24);
    }
    /** 写入一个浮点数值在字节数组中指定位置 */
    public static void writeFloat(float f,byte[] bytes,int pos)
    {
        writeInt(Float.floatToIntBits(f),bytes,pos);
    }
    /** 写入一个浮点数值在字节数组中指定位置,低位在前,高位在后 */
    public static void writeFloat_(float f,byte[] bytes,int pos)
    {
        writeInt_(Float.floatToIntBits(f),bytes,pos);
    }
    /** 写入一个长整型数值在字节数组中指定位置 */
    public static void writeLong(long l,byte[] bytes,int pos)
    {
        bytes[pos]=(byte)(l>>>56);
        bytes[pos+1]=(byte)(l>>>48);
        bytes[pos+2]=(byte)(l>>>40);
        bytes[pos+3]=(byte)(l>>>32);
        bytes[pos+4]=(byte)(l>>>24);
        bytes[pos+5]=(byte)(l>>>16);
        bytes[pos+6]=(byte)(l>>>8);
        bytes[pos+7]=(byte)l;
    }
    /** 写入一个长整型数值在字节数组中指定位置,低位在前,高位在后 */
    public static void writeLong_(long l,byte[] bytes,int pos)
    {
        bytes[pos]=(byte)l;
        bytes[pos+1]=(byte)(l>>>8);
        bytes[pos+2]=(byte)(l>>>16);
        bytes[pos+3]=(byte)(l>>>24);
        bytes[pos+4]=(byte)(l>>>32);
        bytes[pos+5]=(byte)(l>>>40);
        bytes[pos+6]=(byte)(l>>>48);
        bytes[pos+7]=(byte)(l>>>56);
    }
    /** 写入一个双浮点数值在字节数组中指定位置 */
    public static void writeDouble(double d,byte[] bytes,int pos)
    {
        writeLong(Double.doubleToLongBits(d),bytes,pos);
    }
    /** 写入一个双浮点数值在字节数组中指定位置,低位在前,高位在后 */
    public static void writeDouble_(double d,byte[] bytes,int pos)
    {
        writeLong_(Double.doubleToLongBits(d),bytes,pos);
    }
    /** 将指定的字节数据转换为ISO-8859-1格式的字符串 */
    public static String readISO8859_1(byte[] data)
    {
        return readISO8859_1(data,0,data.length);
    }
    /** 将指定的字节数据转换为ISO-8859-1格式的字符串 */
    public static String readISO8859_1(byte[] data,int pos,int len)
    {
        char[] array=new char[len];
        for(int i=pos+len-1,j=array.length-1;i>=pos;i--,j--)
            array[j]=(char)data[i];
        return new String(array);
    }
    /** 将指定的字符串转换为ISO-8859-1格式的字节数据 */
    public static byte[] writeISO8859_1(String str)
    {
        return writeISO8859_1(str,0,str.length());
    }
    /** 将指定的字符串转换为ISO-8859-1格式的字节数据 */
    public static byte[] writeISO8859_1(String str,int index,int len)
    {
        byte[] data=new byte[len];
        writeISO8859_1(str,index,len,data,0);
        return data;
    }
    /** 将指定的字符串转换为ISO-8859-1格式的字节数据 */
    public static void writeISO8859_1(String str,int index,int len,
        byte[] data,int pos)
    {
        int c;
        for(int i=index+len-1,j=pos+len-1;i>=index;i--,j--)
        {
            c=str.charAt(i);
            data[j]=(c>256)?63:(byte)c;
        }
    }
    /** 将指定的字符数组转换为ISO-8859-1格式的字节数据 */
    public static void writeISO8859_1(char[] chars,int index,int len,
        byte[] data,int pos)
    {
        int c;
        for(int i=index+len-1,j=pos+len-1;i>=index;i--,j--)
        {
            c=chars[i];
            data[j]=(c>256)?63:(byte)c;
        }
    }
    /** 将指定的UTF8格式的字节数据转换为字符串,返回null表示失败 */
    public static String readUTF(byte[] data)
    {
        StringBuffer sb=new StringBuffer(data.length);
        int pos=readUTF(data,0,data.length,sb);
        return (pos==0)?sb.toString():null;
    }
    /**
     * 将指定的UTF8格式的字节数据转换为字符串, 返回0表示成功,否则表示失败位置
     */
    public static int readUTF(byte[] data,StringBuffer sb)
    {
        return readUTF(data,0,data.length,sb);
    }
    /**
     * 将指定的UTF8格式的字节数据转换为字符串, 返回0表示成功,否则表示失败位置
     */
    public static int readUTF(byte[] data,int pos,int len,StringBuffer sb)
    {
        int i,c,cc,ccc;
        int end=pos+len;
        while(pos<end)
        {
            c=data[pos]&0xff;
            i=c>>4;
            if(i<8)
            {
                // 0xxx xxxx
                pos++;
                sb.append((char)c);
            }
            else if(i==12||i==13)
            {
                // 110x xxxx 10xx xxxx
                pos+=2;
                if(pos>end) return pos;
                cc=data[pos-1];
                if((cc&0xC0)!=0x80) return pos;
                sb.append((char)(((c&0x1f)<<6)|(cc&0x3f)));
            }
            else if(i==14)
            {
                // 1110 xxxx 10xx xxxx 10xx
                // xxxx
                pos+=3;
                if(pos>end) return pos;
                cc=data[pos-2];
                ccc=data[pos-1];
                if(((cc&0xC0)!=0x80)||((ccc&0xC0)!=0x80)) return pos;
                sb.append((char)(((c&0x0f)<<12)|((cc&0x3f)<<6)|(ccc&0x3f)));
            }
            else
                // 10xx xxxx 1111 xxxx
                return pos;
        }
        return 0;
    }
    /** 获得指定的字符串转换为UTF8格式的字节数据的长度 */
    public static int getUTFLength(String str,int index,int len)
    {
        int utfLen=0;
        int c;
        for(int i=index;i<len;i++)
        {
            c=str.charAt(i);
            if((c>=0x0001)&&(c<=0x007f))
                utfLen++;
            else if(c>0x07ff)
                utfLen+=3;
            else
                utfLen+=2;
        }
        return utfLen;
    }
    /** 在字节数组中指定位置写入一个短整型数值 */
    public static void writeShort(byte[] bytes,int pos,int s)
    {
        bytes[pos]=(byte)(s>>>8);
        bytes[pos+1]=(byte)s;
    }
    /** 在字节数组中指定位置写入一个字节 */
    public static void writeByte(byte[] bytes,int pos,int b)
    {
        bytes[pos]=(byte)b;
    }
    /** 获得指定的字符数组转换为UTF8格式的字节数据的长度 */
    public static int getUTFLength(char[] chars,int index,int len)
    {
        int utfLen=0;
        int c;
        for(int i=index;i<len;i++)
        {
            c=chars[i];
            if((c>=0x0001)&&(c<=0x007f))
                utfLen++;
            else if(c>0x07ff)
                utfLen+=3;
            else
                utfLen+=2;
        }
        return utfLen;
    }
    /** 将指定的字符串转换为UTF8格式的字节数据 */
    public static byte[] writeUTF(String str)
    {
        return writeUTF(str,0,str.length());
    }
    /** 将指定的字符串转换为UTF8格式的字节数据 */
    public static byte[] writeUTF(String str,int index,int len)
    {
        byte[] data=new byte[getUTFLength(str,index,len)];
        writeUTF(str,index,len,data,0);
        return data;
    }
    /** 将指定的字符串转换为UTF8格式的字节数据 */
    public static void writeUTF(String str,int index,int len,byte[] data,
        int pos)
    {
        int c;
        for(int i=index;i<len;i++)
        {
            c=str.charAt(i);
            if((c>=0x0001)&&(c<=0x007f))
            {
                data[pos++]=(byte)c;
            }
            else if(c>0x07ff)
            {
                data[pos++]=(byte)(0xe0|((c>>12)&0x0f));
                data[pos++]=(byte)(0x80|((c>>6)&0x3f));
                data[pos++]=(byte)(0x80|(c&0x3f));
            }
            else
            {
                data[pos++]=(byte)(0xc0|((c>>6)&0x1f));
                data[pos++]=(byte)(0x80|(c&0x3f));
            }
        }
    }
    /** 将指定的字符数组转换为UTF8格式的字节数据 */
    public static void writeUTF(char[] chars,int index,int len,byte[] data,
        int pos)
    {
        int c;
        for(int i=index;i<len;i++)
        {
            c=chars[i];
            if((c>=0x0001)&&(c<=0x007f))
            {
                data[pos++]=(byte)c;
            }
            else if(c>0x07ff)
            {
                data[pos++]=(byte)(0xe0|((c>>12)&0x0f));
                data[pos++]=(byte)(0x80|((c>>6)&0x3f));
                data[pos++]=(byte)(0x80|(c&0x3f));
            }
            else
            {
                data[pos++]=(byte)(0xc0|((c>>6)&0x1f));
                data[pos++]=(byte)(0x80|(c&0x3f));
            }
        }
    }

    /* constructors */
    private ByteKit()
    {
    }

}

使用方法:

for(Map.Entry<String, Map<String, String>> entry : gateConfig.entrySet())
{
    String host = entry.getValue().get("host");
    String port = entry.getValue().get("port");

    PollServer poll = new PollServer(host,Integer.parseInt(port));
    Thread gateThread = new Thread(poll);
    gateThread.setName(entry.getKey());
    gateThread.start();
}

可能调整成mina库,还有其他的一个原因,就是在服务器端会无辜收到一个rst标识导致服务器断开。起初以为是代码问题,后来经过很长时间的排查和咨询,发现服务器用的是南方电信的网络,而一些北方网通的客户端在访问的时候,就会随机出现rst 连接复位现象!查询了好久,最后还是运维的大哥给的思维!当然在起初解决这个问题的时候,我还是本着代码的问题;顺便还去专门研究了Tcp/ip协议详解,翻出了大学里面学的计算机网络这本书。无论如何解决了就好!欢迎拍砖!

开源一个基于nio的java网络程序的更多相关文章

  1. 分享一个基于 netty 的 java 开源项目

    1.简介 中微子代理(neutrino-proxy)是一个基于 netty 的.开源的 java 内网穿透项目.遵循 MIT 许可,因此您可以对它进行复制.修改.传播并用于任何个人或商业行为. 2.项 ...

  2. 基于连接的Java网络编程

    实现了基于TCP的Java Socket编程,功能很简单:客户端向服务器端输出一名话"connect",服务器端接收输出到控制台并向客户端输出一名话"Hello" ...

  3. [安卓] 12、开源一个基于SurfaceView的飞行射击类小游戏

    前言  这款安卓小游戏是基于SurfaceView的飞行射击类游戏,采用Java来写,没有采用游戏引擎,注释详细,条理比较清晰,适合初学者了解游戏状态转化自动机和一些继承与封装的技巧. 效果展示    ...

  4. 开源一个基于天天团购的团购app

    可能大家都知道天天团购开源系统,一个做团购的开源项目很赞,前些日子做了基于天天团购系统做的团购客户端和移动端服务器!源代码放出,有了解的可以看看,希望收益! 先说服务器:app的服务器,基于天天团购的 ...

  5. 基于NIO的Netty网络框架

    Netty是一个高性能.异步事件驱动的NIO框架,它提供了对TCP.UDP和文件传输的支持,Netty的所有IO操作都是异步非阻塞的,通过Future-Listener机制,用户可以方便的主动获取或者 ...

  6. C# 开源一个基于 yarp 的 API 网关 Demo,支持绑定 Kubernetes Service

    关于 Neting 刚开始的时候是打算使用微软官方的 Yarp 库,实现一个 API 网关,后面发现坑比较多,弄起来比较麻烦,就放弃了.目前写完了查看 Kubernetes Service 信息.创建 ...

  7. 【MVVMLight小记】一.快速搭建一个基于MVVMLight的silverlight小程序

    写了篇MVVM小记http://www.cnblogs.com/whosedream/p/mvvmnote1.html,说好要写点MVVMLight的东西,所以接着写,以便和大家共勉. 我假设你已经有 ...

  8. 开源一个基于dotnet standard的轻量级的ORM框架-Light.Data

    还在dotnet framework 2.0的时代,当时还没有EF,而NHibernate之类的又太复杂,并且自己也有一些特殊需求,如查询结果直接入表.水平分表和新增数据默认值等,就试着折腾个轻量点O ...

  9. 菜渣开源一个基于 EMIT 的 AOP 库(.NET Core)

    目录 1,快速入门 1.1 继承 ActionAttribute 特性 1.2 标记代理类型 2,如何创建代理类型 2.1 通过API直接创建 2,创建代理类型 通过API 通过 Microsoft. ...

随机推荐

  1. Hermes实时检索分析平台

    一.序言 随着TDW的发展,公司在大数据离线分析方面已经具备了行业领先的能力.但是,很多应用场景往往要求在数秒内完成对几亿.几十亿甚至几百上千亿的数据分析,从而达到不影响用户体验的目的.如何能够及时有 ...

  2. 免安装版MySQL安装步骤

    http://downloads.mysql.com/archives/community/ 1:添加环境变量把MySQL解压后的bin目录添加到path环境变量中 2:修改或者添加my-defaul ...

  3. Oracle 11g导入导出命令

    首先需要进入系统的cmd: 执行导出命令,效果如下 expdp hisjk/hisjk@orcl  directory=DATA_PUMP_DIR dumpfile=hisjk.dmp SCHEMAS ...

  4. Scala 深入浅出实战经典 第67讲:Scala并发编程匿名Actor、消息传递、偏函数解析

    王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-87讲)完整视频.PPT.代码下载:百度云盘:http://pan.baidu.com/s/1c0noOt6 ...

  5. Console中加入招聘等个性化信息

    try { if (window.console && window.console.log) { console.log("%c XX息科技 ", "f ...

  6. Jenkins 安装或更新插件失败

    试试这个插件网址是否可以在网页中打开 http://mirror.xmission.com/jenkins/updates/current/update-center.json   如可以,把这个网址 ...

  7. 【cocos2d-x 手游研发小技巧(8)通讯的数据压缩与解压 】

    今天说一下手机游戏通讯协议中的数据问题,大量的数据将给服务器端和客户端带来很大的压力,一般来说. 转载请注明出处:http://www.cnblogs.com/zisou/p/cocos2dxJQ-8 ...

  8. ASP.NET MVC 自定义路由中几个需要注意的小细节

    本文主要记录在ASP.NET MVC自定义路由时,一个需要注意的参数设置小细节. 举例来说,就是在访问 http://localhost/Home/About/arg1/arg2/arg3 这样的自定 ...

  9. Spark源码系列(三)作业运行过程

    作业执行 上一章讲了RDD的转换,但是没讲作业的运行,它和Driver Program的关系是啥,和RDD的关系是啥? 官方给的例子里面,一执行collect方法就能出结果,那我们就从collect开 ...

  10. Java Web 工作技巧总结 16.10

    摘要: 原创出处:www.bysocket.com 泥瓦匠BYSocket 希望转载,保留摘要,谢谢! 在你成为领导者以前,成功只同自己的成长有关.当你成为领导者以后,成功都同别人的成长有关. 1.聊 ...