Socket框架

来源:互联网 发布:嵌入式软件开发技术 编辑:程序博客网 时间:2024/06/05 23:51

1.构建消息包类 MessagePackage
<1> 定义包头
根据业务需求,这里有5种:

//  构造包头用的数据(包头10个字节里的内容)    public int bufferSize = 0;// 消息总长度    public int protocolId = 0;// 消息ID    // 网络标记包用来标记消息是否压缩、加密等信息 比如:0表示无压缩 无加密    public ushort nFlags = 0;    public ushort nModuleId = 0;// 发送的模块Id 客户端和服务器之间通讯设置为0    public uint bodyLength = 0;// 包体长度

<2> 接受数据缓冲区 和 当前接受包接受字节多少的索引

public byte[] buffer;public int bufferPoint;// 用来指示当前已经接收了多少字节

<3>自定义构造方法(用来初始化buffer区和重置bufferPoint)
上代码:

    public MessagePackage(int size) {        CreatBuffer(size);    }    public void CreatBuffer(int size) {        buffer = new byte[size];        bufferPoint = 0;    }

<4>将数据包头信息转换为实际信息

#region 将数据包头信息转换为实际信息    // 将字节数组(长度为2)转换成数字    private ushort BytesToInt(byte[] bytes) {        int value = 0;        value |= (bytes[1] & 0xFF) << 8;        value |= (bytes[0] & 0xFF);        return (ushort)value;    }    // 重置字节数组依次取出n个字节出来    private byte[] GetBytes(byte[] byteArr, int length, int offset) {        byte[] bytes = new byte[length];        for (int i = 0; i < length; i++)        {            bytes[i] = byteArr[offset + i];        }        return bytes;    }    // 读取包头信息    public void ReadPacketHead(MessagePackage msg) {        bodyLength = BytesToInt(GetBytes(msg.buffer, 2, 0));        bufferSize = BytesToInt(GetBytes(msg.buffer, 2, 2));        mouduleId = BytesToInt(GetBytes(msg.buffer, 2, 4));        flags = BytesToInt(GetBytes(msg.buffer, 2, 6));        protocolId = BytesToInt(GetBytes(msg.buffer, 2, 8));    }    #endregion

<5>将要发送的数据包转换为字节数组

#region 将要发送的数据包MessagePackage转换为字节数组    // 将数字转换成两个字节长度的数组    private byte[] IntToBytes(ushort value) {        byte[] bytes = new byte[2];        bytes[1] = (byte)((value >> 8) & 0xFF);        bytes[0] = (byte)(value & 0xFF);        return buffer;    }    // 将包头信息转换为字节数组之后放到buffer    public void PackPacket() {        List<byte> bytesList = new List<byte>();        bytesList.AddRange(IntToBytes((ushort)bodyLength));        bytesList.AddRange(IntToBytes((ushort)bufferSize));        bytesList.AddRange(IntToBytes(mouduleId));        bytesList.AddRange(IntToBytes(flags));        bytesList.AddRange(IntToBytes((ushort)protocolId));        // 此时buffer已经是我们通信的对象进行序列化(protobuf)的字节数组        bytesList.AddRange(buffer);        buffer = bytesList.ToArray();    }    #endregion

2.构建自己的Socket类M_Socket

<1>代理服务器 游戏服务器 地址和端口号
<2>包头长度 和 当前接受的长度
<3>构造链接
<4>开始链接 (区分代理服务器和游戏服务器)
<5>链接成功的回调(挂起异步链接)
<6>开始接收包头
<7>开始接受包体
<8>将数据添加到数据接收序列 (丢回主线程)
<9>发送数据
<10>关闭链接

上代码:

// 登录服务器IP地址 端口    string loginServerIP = "";    int loginPort = 8000;    // 游戏服务器地址 端口    string gameServerIP = "";    int gamePort = 9000;    // 包头大小    public int headSize = 10;    int headPoint = 0;    public void BuildSocket() {        // 构造一个Socket 对象    长连接的方式 建立长连接socket        socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);      }    // 开始链接 public void StartConnect(bool isConnectionLoginServer = false) {        if (isConnectionLoginServer)        {            socket.BeginConnect(loginServerIP, loginPort, new AsyncCallback(ConnectCallBack), socket);        }        else {            socket.BeginConnect(gameServerIP, gamePort, new AsyncCallback(ConnectCallBack), socket);        }    }  // 链接完成的回调    public void ConnectCallBack(IAsyncResult ar) {        try        {            Socket tempSocket = (Socket)ar.AsyncState;            // 挂起异步socket链接            tempSocket.EndConnect(ar);        }        catch (Exception ec)        {            Debug.Log("连接出错了" + ec.ToString());            return;            // TODO 界面处理        //NetMgr中定义的链接成功的标志位        NetMgr.Inst.isConnectSuccess = true;        // 连接成功 开始监听是否有包头发送过来        ReceivePackageHead();    }     // 开始接收包头    public void ReceivePackageHead() {         MessagePackage msg = new  MessagePackage(headSize);        socket.BeginReceive(tmsg.buffer, 0, headSize, 0, new AsyncCallback(ReceivePackageHeadHandle), msg);    }  // 接收到包头的回调    public void ReceivePackageHeadHandle(IAsyncResult ar) {        // 获取本次接收数据的长度        int length = socket.EndReceive(ar);        if (length > 0)        {            MessagePackage tempTmsg = ( MessagePackage)ar.AsyncState;            headPoint += length;            if (headPoint < headSize)            {                // 没接收完  继续接收                socket.BeginReceive(tempTmsg.buffer, headPoint, headSize, 0, new AsyncCallback(ReceivePackageHeadHandle), tempTmsg);            }            else            {                // 接收完毕 开始接收包体                headPoint = 0;                // 开始接收包体 解析包头 得到bodyLength                tempTmsg.ReadPacketHead(tempTmsg);                // 根据bodyLength 重新开始初始化buffer区                tempTmsg.CreatBuffer(tempTmsg.bodyLength);                // 开始接收包体                ReceivePackageBody(tempTmsg);            }        }        else {            // TODO 界面处理 没收到            Debug.Log("服务器断开");        }    } public void ReceivePackageBody(MessagePackage msg) {        // 继续开始接收bodyLength长度的包体        socket.BeginReceive(msg.buffer, headPoint, msg.bodyLength, 0, new AsyncCallback(ReceivePackageBodyHandle), msg);    }public void ReceivePackageBodyHandle(IAsyncResult ar) {        int length = socket.EndReceive(ar);        if (length > 0)        {            // 收到包体            MessagePackage tempMsg = (MessagePackage)ar.AsyncState;            tempMsg.bufferPoint += length;            if (tempMsg.bufferPoint == length)            {                // 将数据添加到数据接收序列 (丢回主线程)                // 一会构建这个类                NetMgr.Inst.receiveQueue.Enqueue(tempMsg);                // 接收下一个数据包                ReceivePackageHead();            }            else {                // 没接收完 继续接收                socket.BeginReceive(tempMsg.buffer, tempMsg.bufferPoint, length - tempMsg.bufferPoint, 0, new AsyncCallback(ReceivePackageBodyHandle), tempMsg);            }        }        else {            // TODO 没有收到包体            Debug.Log("  ");        }    }    // 异步发送数据的方法 public void SendMsg(MessagePackage msg) {        socket.BeginSend(msg.buffer, 0, msg.buffer.Length, 0, new AsyncCallback(SendMsgHandle), socket);    }  // 发送数据包结束的回调    public void SendMsgHandle(IAsyncResult ar) {               try        {            Socket tempSocket = (Socket)ar.AsyncState;            tempSocket.EndSend(ar);        }        catch (Exception ec)        {            Debug.Log(ec.ToString());        }    // 关闭Socket连接    public void CloseConnection()    {        socket.Close();// 关闭连接        Debug.Log("客户端 关闭连接");    }

3.构造NetMgr
<1>单例类

<2>2个队列 接收包队列和发送包队列

<3>代理服务器和游戏服务器的socket

<4>定义连接状态的标志位

<5>定义代理服务器链接的标志位

<6>允许重连次数和等待时间

<7>序列化反序列化

<8>重连方法实现

上代码:

    private static NetMgr inst;    public static NetMgr Inst { get { return inst; } }    private void Awake()    {        inst = this;    }    // 是否链接成功    public bool isConnectSuccess = false;    public bool isLoginServer = true;    TaoSocket loginSocket;    TaoSocket gameSocket;    public Queue<MessagePackaget> receiveQueue = new Queue<MessagePackaget>();    public Queue<MessagePackaget> sendQueue = new Queue<MessagePackaget>();    //  链接登陆服务器    public void StartConnectLoginServer() {        isConnectSuccess = false;// 断线重新置位(保障)        isLoginServer = true;        loginSocket = new M_Socke();        loginSocket.BuildSocket();        loginSocket.StartConnect(isLoginServer);    }    // 链接游戏服务器    public void StartConnectGameServer() {        isConnectSuccess = false;        isLoginServer = false;// 链接登陆服务器         gameSocket = new M_Socke();        gameSocket.BuildSocket();        gameSocket.StartConnect(isLoginServer);    }   // 发送数据    private void SendMsg(MessagePacket msg) {        if (isConnectSuccess) {            if (isLoginServer)            {                loginSocket.SendMsg(msg);            }            else {                gameSocket.SendMsg(msg);            }        }    }    private void Update()    {        if (sendQueue.Count > 0) {            SendMsg(sendQueue.Dequeue());        }        if (receiveQueue.Count > 0) {            MsgProcessing(receiveQueue.Dequeue());        }    }    private void MsgProcessing(MessagePacket msg)    {        // 开始解析数据        switch (msg.protocolId)        {            // 根据消息ID 反序列化字节数组            case (int)MySpace.Package.PacketTypeS2C.S2C_GetSelfItem_Response:                var pack = Serialize_Deserialize.Deserialize<GetSelfItem_Response>(msg.buffer);                break;                // 等等..        }    }// 处理重连    float waitTime = 12f;    int waitCount = 0;    float timer = 0;    public IEnumerator WaitSocketBack() {        while (!isConnectSuccess) {            yield return 0;            timer += Time.deltaTime;            if (timer > waitTime) {                break;            }        }        if (isConnectSuccess)        {            // TODO 连接成功            Debug.Log("Connect is ok!");            waitCount = 0;// 重置重连次数        }        else {            // 超时            Debug.Log("Connect Failed! 超时!");            // 链接失败            if (waitCount < 3)            {                waitCount++;                ContinueConnect();            }            else            {                // 弹出tips 告知玩家 网络状况较差 请检查网络连接后重试                if (isLoginServer)                {                    loginSocket.CloseConnection();                }                else                {                    gameSocket.CloseConnection();                }                StopCoroutine("WaitSocketBack");// 停止协程            }        }    }    // 重连    private void ContinueConnect()    {        if (isLoginServer)        {            // 重连代理服务器            loginSocket.CloseConnection();// 关闭之前连接            loginSocket.BuildSocket();            loginSocket.StartConnect();            // 调用协程            StartCoroutine("WaitSocketBack");        }        else        {            // 重连游戏服务器            gameSocket.CloseConnection();// 关闭之前连接            gameSocket.BuildSocket();            gameSocket.StartConnect();            // 调用协程            StartCoroutine("WaitSocketBack");        }    }

好了,大致就是这样,有时间再补充。

原创粉丝点击