Moba项目开发(一)打造网络通信系统-任务16:16.对接收到的消息做第一层处理

文章目录

    • 1.初始化的接口
    • 2.发送消息的接口
    • 3.发送ACK消息的接口
    • 4.接受消息的接口
    • 5.处理消息的接口
    • 6.关闭Socket的接口
    • 7.创建客户端报文数据建立虚拟连接的接口
    • 8.移除客户端的接口
    • 9.查询客户端的接口
    • 10.代码:

完善处理消息的接口后,完整体如下:

1.初始化的接口

public USocket(Action dispatchNetEvent)

2.发送消息的接口

public async void Send(byte[] data,IPEndPoint endPoint)

3.发送ACK消息的接口

public async void SendACK(BufferEntity ackPackage,IPEndPoint endPoint)

4.接受消息的接口

public async void Receive()

5.处理消息的接口

async Task Handle()

6.关闭Socket的接口

void Close()

7.创建客户端报文数据建立虚拟连接的接口

void CreateUClient(BufferEntity buffer)

8.移除客户端的接口

public void RemoveClient(int sessionid)

9.查询客户端的接口

public UClient GetClient(int sessionid)

10.代码:

using System;
using System.Collections.Concurrent;   //线程安全的队列
using System.Collections.Generic;
using System.Net;                      //导入命名空间
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using System.Text;

namespace MobaServer.Net
{
     
    class USocket
    {
     
        //还需要核心对象Udp
        UdpClient socket;//socket通信

        //客户端跟服务器通信,要直到他的端口
        //服务器IP为本地的IP,服务器现在是放在本地
        string ip = "192.168.6.23";
        //端口号
        int port = 8899;

        //把它缓存到本地上面,因为是一对多的服务器,所以再创建客户端时要将函数传递过去
        Action<BufferEntity> dispatchNetEvent;
        //初始化的接口
        public USocket(Action<BufferEntity> dispatchNetEvent)
        {
     
            this.dispatchNetEvent = dispatchNetEvent;
            //构造一下,传入端口号
            socket = new UdpClient(8899);

            //可以开始接受消息,开始的时候调用一下
            Receive();

            //现在就能随时查询任务的状态
            Task.Run(Handle, ct.Token);
        }

        //发送消息的接口
        public async void Send(byte[] data,IPEndPoint endPoint)
        {
     
            //内部使用异步发送的接口,不会阻塞主线程的运行
            if(socket!=null)
            {
     
                //用try-catch捕获异常,若出现异常,服务器要断开于客户端的连接
                try
                {
     
                    int length = await socket.SendAsync(data, data.Length, endPoint);
                    //若相等,说明数据是完整发送的
                    if (data.Length == length)
                    {
     

                    }
                }
                catch(Exception e)
                {
     
                    //关闭socket的接口
                    Close();
                }
            }
        }

        //发送ACK消息的接口
        public async void SendACK(BufferEntity ackPackage,IPEndPoint endPoint)
        {
     
            //调用Send接口将ACK报文发送出去
            //内部已经实现了await,不用加上await
            Send(ackPackage.buffer, endPoint);
        }

        //线程安全的队列存客户端发来的消息包
        ConcurrentQueue<UdpReceiveResult> awaitHandle = new System.Collections.Concurrent.ConcurrentQueue<UdpReceiveResult>();

        //接受消息的接口
        public async void Receive()
        {
     
            if (socket != null)
            {
     
                try
                {
     
                    //可以用来等待,因为是异步的接口
                    //当有客户端发送消息的时候,要缓存起来,再来进行处理
                    UdpReceiveResult result = await socket.ReceiveAsync();
                    //接受消息和处理消息不要写在一起,只需要缓存就可以了
                    //如果写在一起,这里用的try-catch,当接受消息时,会把要处理的消息throw掉并打印
                    awaitHandle.Enqueue(result);//压入队列
                    Receive();//继续接受下一条消息

                }
                catch(Exception e)
                {
     
                    //关闭socket的接口
                    Close();
                }
            }
        }

        //本地存储sessionID,默认从1000开始
        int sessionID = 1000;

        //多线程异步有讲过
        CancellationTokenSource ct = new CancellationTokenSource();
        //处理消息的接口
        //开启一个线程来处理
        async Task Handle()
        {
     
            //如果不是取消状态,就能够执行里面的代码
            while (!ct.IsCancellationRequested)
            {
     
                if (awaitHandle.Count > 0)
                {
     
                    UdpReceiveResult data;
                    //从队列中取出客户端发过来的消息包
                    if (awaitHandle.TryDequeue(out data))
                    {
     
                        BufferEntity bufferEntity = new BufferEntity(data.RemoteEndPoint, data.Buffer);
                        //如果数据报文包是完整的,那么开始取客户端数据包
                        if (bufferEntity.isFull)
                        {
     
                            //通过会话ID来进行查询,等于0表示这个客户端还没建立连接
                            //那就分配会话ID
                            if (bufferEntity.session == 0)
                            {
     
                                sessionID += 1;
                                //创建客户端 给客户端分配会话ID
                                bufferEntity.session = sessionID;
                                //创建客户端
                                //创建客户端报文数据建立虚拟连接的接口
                                CreateUClient(bufferEntity);
                            }

                            UClient targetClient;
                            //如果这个客户端已经建立连接,获取到客户端
                            //从队列中获取客户端的数据包
                            if (clients.TryGetValue(bufferEntity.session, out targetClient))
                            {
     
                                //如果获取到了,传递给Handle,按顺序处理消息
                                //生成一个Handle方法到UClient里面
                                targetClient.Handle(bufferEntity);
                            }
                        }
                    }
                }
            }
        }

        //关闭Socket的接口
        void Close()
        {
     
            //取消任务的信号
            ct.Cancel();

            if (socket != null)
            {
     
                socket.Close();
                socket = null;
            }
            //释放掉这个函数
            if(dispatchNetEvent != null)
            {
     
                dispatchNetEvent = null;
            }
            //所有客户端都要清理掉,避免下次运行的时候出现相关异常


        }

        //需要个管理客户端的字典,每个UClient代表不同的客户端,int是会话ID
        ConcurrentDictionary<int,UClient> clients = new ConcurrentDictionary<int, UClient>();

        //创建客户端报文数据建立虚拟连接的接口
        //根据BufferEntity里面的数据创建客户端
        void CreateUClient(BufferEntity buffer)
        {
     
            UClient client;
            //如果没有获取到字典中的客户端数据
            if(!clients.TryGetValue(buffer.session,out client))
            {
     
                //this表示讲本身的USocket传递给客户端
                //讲buffer.endPoint  远程端口传递给客户端
                //0,0表示发送和处理的序号
                //session 把会话ID传递给它
                //dispatchNetEvent  讲缓存的处理,分发消息的函数也传递给它
                //生成构造函数但是生成了UClient1.cs不是UClient.cs
                client = new UClient(this, buffer.endPoint, 0, 0, buffer.session,dispatchNetEvent);
                //int是会话ID,client是值
                clients.TryAdd(buffer.session, client);
            }
        }

        //移除客户端的接口
        public void RemoveClient(int sessionid)
        {
     
            UClient client;
            //如果能成功移除,那就设置为空
            if (clients.TryRemove(sessionid,out client))
            {
     
                client.Close();
                client = null;
            }
        }

        //查询客户端的接口
        public UClient GetClient(int sessionid)
        {
     
            UClient client;
            if(clients.TryGetValue(sessionid,out client))
            {
     
                return client;
            }
            return null;
        }
    }
}

你可能感兴趣的:(c#)