public USocket(Action dispatchNetEvent)
public async void Send(byte[] data,IPEndPoint endPoint)
public async void SendACK(BufferEntity ackPackage,IPEndPoint endPoint)
public async void Receive()
async Task Handle()
void Close()
void CreateUClient(BufferEntity buffer)
public void RemoveClient(int sessionid)
public UClient GetClient(int sessionid)
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;
}
}
}