一,定义玩家的信息协议
public class PlayerMessage
{
/// <summary>
/// 玩家id
/// </summary>
public int id;
/// <summary>
/// 玩家昵称
/// </summary>
public string nickName;
/// <summary>
/// 玩家状态(0=未准备,1=准备,2=出牌,3=pass)
/// </summary>
public int state;
/// <summary>
/// 玩家手里的牌
/// </summary>
public string cardsIds;
/// <summary>
/// 0==农民,1==地主
/// </summary>
public int scale=0;
}
二,定义消息传输类型协议
/// <summary>
/// 消息体
/// </summary>
public class MessageData
{
/// <summary>
/// 消息类型
/// </summary>
public MessageType msgType;
/// <summary>
/// 消息内容
/// </summary>
public string msg;
}
/// <summary>
/// 定义简单的协议类型
/// </summary>
public enum MessageType
{
/// <summary>
/// 申请加入
/// </summary>
REQUEST_JOIN = 0,
/// <summary>
/// 同意加入
/// </summary>
AGREE_JOIN=1,
/// <summary>
/// 准备开始游戏
/// </summary>
READY_BEGIN=2,
/// <summary>
/// 开始游戏
/// </summary>
BEGIN_PLAY=3,
/// <summary>
/// 发牌
/// </summary>
DEAL_CARDS=4,
/// <summary>
/// 洗牌
/// </summary>
REFRESH_CARDS=5,
/// <summary>
/// 出牌
/// </summary>
SEND_CARDS=6,
/// <summary>
/// 所有玩家信息
/// </summary>
ALLPLAYER_MESSAGE = 7,
/// <summary>
/// 我的信息
/// </summary>
MY_MESSAGE = 8,
/// <summary>
/// 地主牌
/// </summary>
LANLORD_CARDS = 9,
/// <summary>
/// 桌面的牌
/// </summary>
DESKTOP_CARDS = 10,
/// <summary>
/// 当前出牌玩家
/// </summary>
CURRENT_SEND=11,
/// <summary>
/// 游戏结束
/// </summary>
GAME_OVER = 12,
}
三,客户端的框架搭建(收发消息队列)
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System;
using LitJson;
using System.IO;
/// <summary>
/// 声明一个委托对象
/// </summary>
/// <param name="data">接收到的数据对象</param>
public delegate void ReceiveMessageData(MessageData data);
/// <summary>
/// 当连接状态发生改变
/// </summary>
public delegate void OnConnectChange();
public class ClientSocketController : MonoBehaviour
{
Socket clientSocket;
/// <summary>
/// 数据缓冲池
/// </summary>
public byte[] buffer = new byte[512];
/// <summary>
/// 数据记忆流
/// </summary>
public MemoryStream bufferMs = new MemoryStream();
/// <summary>
/// 委托变量
/// </summary>
public ReceiveMessageData receiveMessageData;
/// <summary>
/// 连接成功
/// </summary>
public OnConnectChange OnConnectSuccess;
/// <summary>
/// 连接异常
/// </summary>
public OnConnectChange OnConnectExcept;
/// <summary>
/// 发送队列
/// </summary>
public Queue<MessageData> sendQueue = new Queue<MessageData>();
/// <summary>
/// 判断是否正在发送
/// </summary>
public bool IsSend = false;
/// <summary>
/// 主动连接次数
/// </summary>
public int ConnectedCount = 0;
/// <summary>
/// 最大主动连接次数
/// </summary>
public int MaxConnectNumber = 5;
/// <summary>
///尝试连接服务器
/// </summary>
public bool tryConnectToServer = false;
/// <summary>
/// 等待时间
/// </summary>
public float holdTime = 1;
// Use this for initialization
void Start()
{
ConnectedCount = 0;
//创建socket对象
clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//开始链接服务器
ConnectedToSercver();
}
/// <summary>
/// 连接服务器
/// </summary>
public void ConnectedToSercver()
{
IPEndPoint ipendPoint = new IPEndPoint(IPAddress.Parse("192.168.213.54"), 10004);
Debug.Log("开始连接服务器.......");
//请求连接
clientSocket.BeginConnect(ipendPoint, ConnectCallback, "");
}
/// <summary>
/// 连接服务器的回调
/// </summary>
/// <param name="ar"></param>
public void ConnectCallback(IAsyncResult ar)
{
try
{
clientSocket.EndConnect(ar);
}
catch (Exception e)
{
Debug.Log(e.ToString());
}
//判断到底连接成功了还是没有?
Debug.Log("连接回调!!!!!");
if (clientSocket.Connected == true)
{
OnConnectSuccess();
Debug.Log("连接成功!!!!!");
ReceiveMessageFormServer();
}
else
{
Debug.Log("连接失败");
tryConnectToServer = true;
holdTime = 1;
//Invoke("ConnectedToSercver", 0.5f);
}
}
/// <summary>
/// 把发送的信息对象放入队列中
/// </summary>
public void PutMessageToQueue(MessageData data)
{
//byte[] msgBytes = ToolsUtil.StringToByteArry(JsonMapper.ToJson(data));
//SendMessageToServer(msgBytes,0,msgBytes.Length);
//把要发送的消息放入队列
sendQueue.Enqueue(data);
//看一下有没有内容发送!如果正在发,不用管,如果没有发,启动发送
DeSendQueueToServer();
}
/// <summary>
/// 出队列,发送给服务器
/// </summary>
public void DeSendQueueToServer()
{
if (IsSend == false)
{
//启动发送
//出队列,对象转json,json字符串转byte[] 然后发送
//确保队列中有元素
if (sendQueue.Count > 0)
{
MessageData data = sendQueue.Dequeue();
byte[] sendBytes = ToolsUtil.ObjectToJsonBytes(data);
Debug.Log(System.Text.Encoding.UTF8.GetString(sendBytes));
SendBytesMessageToServer(sendBytes, 0, sendBytes.Length);
}
}
}
/// <summary>
/// 发送一条文字信息给服务器
/// </summary>
/// <param name="msg"></param>
public void SendMessageToServer(string msg)
{
//发送类型
MessageData data = new MessageData();
data.msgType = MessageType.SEND_CARDS;
data.msg = msg;
PutMessageToQueue(data);
}
/// <summary>
/// 发送消息的方法
/// </summary>
/// <param name="sendMsgConent">消息内容</param>
/// <param name="offest">从消息内容第几个开始发送</param>
/// <param name="size">发送的长度</param>
public void SendBytesMessageToServer(byte[] sendMsgConent, int offest, int size)
{
if (clientSocket.Connected && IsSend == false)
{
IsSend = true;
//需要粘包
//这个信息的总长度+信息
//int 转成byte数组
byte[] lengthBytes = BitConverter.GetBytes(sendMsgConent.Length + 4);
//创建一个内存流
MemoryStream ms = new MemoryStream();
//把头文件写进去,整个信息的长度
ms.Write(lengthBytes, 0, lengthBytes.Length);
//再把信息写进去
ms.Write(sendMsgConent, 0, sendMsgConent.Length);
//把内存流文件转成byte数组
byte[] allMsgBytes = ms.ToArray();
//用完之后,清理一下内存
ms.Dispose();//释放
ms.Close();//关闭
Debug.Log("开始发送!!准备发送长度=" + allMsgBytes.Length);
clientSocket.BeginSend(allMsgBytes, offest, allMsgBytes.Length, SocketFlags.None, SendMessageCallback, "");
}
}
/// <summary>
/// 发送信息的回调
/// </summary>
/// <param name="ar"></param>
public void SendMessageCallback(IAsyncResult ar)
{
//发送结束
IsSend = false;
Debug.Log("发送结束!!!");
//停止发送
int length = clientSocket.EndSend(ar);
Debug.Log("发送的长度:" + length);
//开启下一次发送
DeSendQueueToServer();
}
/// <summary>
/// 从服务器接收数据
/// </summary>
public void ReceiveMessageFormServer()
{
Debug.Log("开始接收数据");
clientSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, ReceiveMessageCallback, "");
}
/// <summary>
/// 接收信息的回调
/// </summary>
/// <param name="ar"></param>
public void ReceiveMessageCallback(IAsyncResult ar)
{
Debug.Log("接收结束!!!!!");
//结束接收
int length = clientSocket.EndReceive(ar);
Debug.Log("接收的数据长度=" + length);
//把数据缓冲放进记忆流
bufferMs.Write(buffer, 0, length);
//拆包
bool isDisOver = false;
//当前拆到第几字节
int currentIndex = 0;
//把流转换为数组
byte[] receiveMsgBytes = bufferMs.ToArray();
while (isDisOver == false)
{
//开始拆包
if (receiveMsgBytes.Length <= 4 + currentIndex)//如果信息包体长度小于4加当前读到得信息长度,说明记忆流最后的包体数据不完整
{
//如果下一条信息的长度不够了,存起来
bufferMs.Dispose();
bufferMs.Close();
bufferMs = new MemoryStream();
bufferMs.Write(receiveMsgBytes, currentIndex, receiveMsgBytes.Length - currentIndex);
//结束循环
isDisOver = true;
}
else
{
//判断记忆流开头数据长度
int currentMsgLength = BitConverter.ToInt32(receiveMsgBytes, currentIndex);
if (currentMsgLength > receiveMsgBytes.Length - currentIndex)//当前包的信息长度大于剩余信息长度
{//后面数据包的信息不完整
//信息不完整,存起来
bufferMs.Dispose();
bufferMs.Close();
bufferMs = new MemoryStream();
bufferMs.Write(receiveMsgBytes, currentIndex, receiveMsgBytes.Length - currentIndex);
//结束循环
isDisOver = true;
}
else//信息是完整的
{
//读取这条信息的内容
//读取信息是从包头后面开始开始,长度是总长度减去包头长度
string msg = ToolsUtil.ByteArrayToString(receiveMsgBytes, currentIndex + 4, currentMsgLength - 4);
//拆完之后把读取文件的下标往后移
Debug.Log("服务器发来消息=" + msg);
//把信息转换为byte数组
//byte[] resByte = ToolsUtil.StringToByteArry(msg);
//把接收到的数据转换为信息对象
try
{
MessageData reveiveMsgData = LitJson.JsonMapper.ToObject<MessageData>(msg.Trim());
if (receiveMessageData != null)
{
//接收消息委托
receiveMessageData(reveiveMsgData);
}
ReceiveMessageFormServer();
currentIndex += currentMsgLength;
}
catch (Exception e)
{
Debug.Log(e.ToString());
throw;
}
}
}
}
}
// Update is called once per frame
void Update()
{
holdTime -= Time.deltaTime;
if (tryConnectToServer == true && holdTime < 0 && ConnectedCount <= MaxConnectNumber)
{
tryConnectToServer = false;
ConnectedCount += 1;
ConnectedToSercver();
}
}
}
四,单个服务器的消息处理框架
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace LandlordServerDemo
{
class ClientController
{
/// <summary>
/// 用户链接的通道
/// </summary>
private Socket clientSocket;
public int id;//16
/// <summary>
/// 昵称
/// </summary>
public string nickName;
/// <summary>
/// 玩家id
/// </summary>
public int playerId = 1;
/// <summary>
/// 所有玩家列表
/// </summary>
public static List<PlayerMessage> allPlayerList = new List<PlayerMessage>();
/// <summary>
/// 准备游戏的玩家数量
/// </summary>
public int IsReadyPlayers=0;
Thread receiveThread;
/// <summary>
/// 发送队列
/// </summary>
public Queue<MessageData> sendQueue = new Queue<MessageData>();
/// <summary>
/// 接收队列
/// </summary>
public Queue<MessageData> receiveQueue = new Queue<MessageData>();
/// <summary>
/// 发送线程
/// </summary>
private Thread sendThread;
/// <summary>
/// 处理接收的队列
/// </summary>
private Thread handlerReceiveThread;
/// <summary>
/// 缓冲内存流
/// </summary>
private MemoryStream bufferMS = new MemoryStream();
public ClientController(Socket socket)
{
clientSocket = socket;
//启动接收的方法
//开始收的线程
receiveThread = new Thread(ReceiveFromClient);
//启动收的线程
receiveThread.Start();
//发送的无限死循环的线程开启一下
sendThread = new Thread(SendToClient);
//启动线程
sendThread.Start();
handlerReceiveThread = new Thread(HandlerReceiveQueue);
//启动线程
handlerReceiveThread.Start();
}
//接收的线程方法
byte[] buffer = new byte[512];//到底用多少???
void ReceiveFromClient()
{
try
{
//Socket clientSocket = (Socket)socket;
while (clientSocket.Connected)
{
//if(clientSocket.Connected)
{
//定义一个缓冲区域
//这儿会有阻塞
int length = clientSocket.Receive(buffer, buffer.Length, SocketFlags.None);
if (length > 0)
{
//首先把接收到的字节放入缓冲流
bufferMS.Write(buffer, 0, length);
//拆包
bool isDisOver = false;
//当前拆到第几个字节了
int currentIndex = 0;
//把流转换为数组
byte[] receiveMsgBytes = bufferMS.ToArray();
Console.WriteLine("开始拆包,receiveMsgBytes.Length=" + receiveMsgBytes.Length);
while (isDisOver == false)
{
Console.WriteLine("currentIndex=" + currentIndex);
//开始拆包
if (receiveMsgBytes.Length <= 4 + currentIndex)
{
//如果下一条信息的长度不够了,存起来
bufferMS.Dispose();
bufferMS.Close();
bufferMS = new MemoryStream();
bufferMS.Write(receiveMsgBytes, currentIndex, receiveMsgBytes.Length - currentIndex);
//结束循环
isDisOver = true;
}
else
{
//超过4个
//判断一下头内容中有多长
int currentMsgLength = BitConverter.ToInt32(receiveMsgBytes, currentIndex);
Console.WriteLine("当前信息的长度是:" + currentMsgLength);
if (currentMsgLength > receiveMsgBytes.Length - currentIndex)
{
//信息不完整,存起来
//如果下一条信息的长度不够了,存起来
bufferMS.Dispose();
bufferMS.Close();
bufferMS = new MemoryStream();
bufferMS.Write(receiveMsgBytes, currentIndex, receiveMsgBytes.Length - currentIndex);
//结束循环
isDisOver = true;
}
else
{
//信息是完整
//读取这条信息内容,读取完之后把指针往后拨
string json = System.Text.Encoding.UTF8.GetString(receiveMsgBytes, currentIndex + 4, currentMsgLength - 4);
Console.WriteLine("接收到数据长度是:" + length + ",开始解析接收的内容。。。。");
//接收的内容打印出来
//服务器收到应该全部是json字符串,如果不是,说明出错了!
Console.WriteLine("客户端发过来信息说:" + json);
//处理接受信息
//将json字符串转换为数据对象
//异常捕获
try
{
MessageData data = LitJson.JsonMapper.ToObject<MessageData>(json.Trim());
//放入到接收队列中
receiveQueue.Enqueue(data);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
// Program.RemoveOutLineClient(this);
}
//指针往后拨
currentIndex += currentMsgLength;
}
}
}
}
}
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
Program.RemoveOutLineClient(this);
Console.WriteLine("当前还有" + Program.clientControllerList.Count + "个用户在线");
}
}
/// <summary>
/// 处理消息队列里的消息
/// </summary>
public void HandlerReceiveQueue()
{
while (true)
{
if (receiveQueue.Count > 0)
{
//出队列
MessageData data = receiveQueue.Dequeue();
Console.WriteLine(data.msgType);
//根据收到的消息类型做出反应
switch (data.msgType)
{
case MessageType.REQUEST_JOIN://请求加入
break;
case MessageType.AGREE_JOIN:
break;
case MessageType.READY_BEGIN:
break;
case MessageType.BEGIN_PLAY:
break;
case MessageType.DEAL_CARDS:
break;
case MessageType.REFRESH_CARDS:
break;
case MessageType.SEND_CARDS:
break;
case MessageType.MY_MESSAGE:
break;
case MessageType.DESKTOP_CARDS:
break;
default:
break;
}
}
Thread.Sleep(100);
}
}
/// <summary>
/// 把玩家的信息广播给所有人
/// </summary>
public void SendAllPlayerMessageToAllClient()
{
//广播给所有人
MessageData allPlayerData = new MessageData();
allPlayerData.msgType = MessageType.ALLPLAYER_MESSAGE;
allPlayerData.msg = LitJson.JsonMapper.ToJson(allPlayerList);
SendMessageDataToAllClient(allPlayerData);
}
/// <summary>
/// 广播信息,告诉所有用户有什么信息过来了!
/// </summary>
/// <param name="data"></param>
void SendMessageDataToAllClient(MessageData data)
{
for (int i = 0; i < Program.clientControllerList.Count; i++)
{
try
{
Program.clientControllerList[i].SendToClient(data);
}
catch (Exception)
{
i--;
}
}
}
/// <summary>
/// 广播消息,排除掉自己
/// </summary>
/// <param name="data"></param>
void SendMessageDataToAllClientWithOutSelf(MessageData data)
{
//5 i=3
for (int i = 0; i < Program.clientControllerList.Count; i++)
{
if (Program.clientControllerList[i] != this)
{
Program.clientControllerList[i].SendToClient(data);
}
}
}
/// <summary>
/// 把要发送给服务器的消息放入消息队列(告诉自己)
/// </summary>
/// <param name="data"></param>
void SendToClient(MessageData data)
{
//放入发送队列
sendQueue.Enqueue(data);
}
/// <summary>
/// 发消息给客户端
/// </summary>
/// <param name="msgByte">需要发送的内容</param>
void SendToClient()
{
while (true)
{
//发送
try
{
if (sendQueue.Count > 0)
{
//byte[] sendMsg = System.Text.Encoding.UTF8.GetBytes(JsonMapper.ToJson(data));
//出队列
MessageData data = sendQueue.Dequeue();
//把对象转换为json字符串
string msg = LitJson.JsonMapper.ToJson(data);
Console.WriteLine("服务器发送消息:" + msg);
//把json字符串转换byte数组
byte[] msgBytes = System.Text.Encoding.UTF8.GetBytes(msg);
Console.WriteLine("服务器开始发送信息长度:" + msgBytes.Length);
//粘包
MemoryStream ms = new MemoryStream();
//包头(这条信息的长度)
byte[] lengthBytes = BitConverter.GetBytes(msgBytes.Length + 4);
//拼接包头
ms.Write(lengthBytes, 0, lengthBytes.Length);
//拼接包体信息
ms.Write(msgBytes, 0, msgBytes.Length);
//将拼接好的信息转换为byte数组
byte[] allMsgBytes = ms.ToArray();
//资源释放
ms.Dispose();
ms.Close();
//确保发送的是拼接后的信息
int sendLength = clientSocket.Send(allMsgBytes);
Console.WriteLine("服务器发送信息结束,成功发送:" + sendLength);
Thread.Sleep(50);
}
else
{
Thread.Sleep(500);
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
Program.RemoveOutLineClient(this);
Console.WriteLine("当前还有" + Program.clientControllerList.Count + "个用户在线");
//抛出异常,可以进行捕获
throw new Exception();
}
}
}
/// <summary>
/// 销毁自己,也就是释放资源
/// </summary>
public void DestroySelf()
{
//异常 空针针,null 数组越界 特殊情况引起的(1/0),可以进行捕获,不会影响整个系统的运行
//错误 无法捕获,系统内部已经没有办法自动消化这个错误。
try
{
receiveThread.Abort();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
try
{
sendThread.Abort();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
try
{
handlerReceiveThread.Abort();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
}
}