这里 接着上次那个聊天室 说下改动吧 也没改多少
就是客户端会发心跳包过来 服务器刷新超时时间并且回应 给客户端,如果都超时没有接到回应给 就会主动回调 超时事件 这就是心跳包的过程
服务端
ChatServer全部代码
using System;
using System.Collections.Generic;
using System.Linq;
using KGSocket;
using ChatNetData;
using KGSocket.Tool;
namespace ChatServer
{
//各个客户端管理 业务逻辑都在这了
public class ChatServe : KGSocketServe
{
#region 这里是单例
private static ChatServe instance;
public static ChatServe Instance
{
get
{
if (instance == null)
{
instance = new ChatServe();
}
return instance;
}
set => instance = value;
}
#endregion
public static readonly string obj = "lock";
public Queue DataPackQue = new Queue();
#region 缓存
private int SessionID = 0;
public int GetSessionID()
{
if (SessionID == int.MaxValue)
{
SessionID = 0;
}
return SessionID += 1;
}
public bool IsUserOnLine(string name)
{
return SessionList.Select(v => v.PlayerName).ToList().Contains(name);
}
#endregion
public KGHeartBeatManage kGHeartBeatManage;
public void Update()
{
if (DataPackQue.Count > 0)
{
//多线程调用就要加个线程锁了避免同时调用
lock (obj)
{
DisposePack(DataPackQue.Dequeue());
}
}
}
public override void StartCreate(string ip, int port)
{
base.StartCreate(ip, port);
kGHeartBeatManage = new KGHeartBeatManage().InitTimerEvent(null, lost =>
{
if (lost != null && lost.mSocket != null && lost.mSocket.Connected)
{
(lost.SessionID + "ID心跳包超时 准备断开连接").KLog(LogLevel.Err);
lost.Clear();
}
}).StartTimer();
//添加新的会话事件
AddSessionEvent += v =>
{
kGHeartBeatManage.AddConnectDic(v);
};
//删除会话事件
RemoveSessionEvent += v =>
{
kGHeartBeatManage.RemoveConnectDic(v);
};
}
///
/// 处理客户端发过来的消息
///
///
public void DisposePack(ChatDatasPack pack)
{
switch ((CMD)pack.chatDatas.Cmd)
{
//心跳包指令
case CMD.HeartBeat:
//更新心跳包
kGHeartBeatManage.UpdateOneHeat(pack.chatSession);
pack.chatSession.SendData(pack.chatDatas);
break;
//登录指令
case CMD.ReqLogin:
//判断在线的名字是否重复
if (IsUserOnLine(pack.chatDatas.PlayerName))
{
pack.chatDatas.Err = (int)ErrorInfo.NameRepeatsErr;
}
else
{
//返回指令
pack.chatDatas.Cmd = (int)CMD.RspLogin;
//存储角色的名字和头像图片数据
pack.chatSession.PlayerName = pack.chatDatas.PlayerName;
pack.chatSession.HeadData = pack.chatDatas.HeadData;
}
//返回消息给客户端
pack.chatSession.SendData(pack.chatDatas);
break;
//聊天指令
case CMD.ReqChatInfo:
//返回指令
pack.chatDatas.Cmd = (int)CMD.RspChatInfo;
//赋值角色的名字和头像图片数据
pack.chatDatas.PlayerName = pack.chatSession.PlayerName;
pack.chatDatas.HeadData = pack.chatSession.HeadData;
//发回给自身聊天
pack.chatSession.SendData(pack.chatDatas);
//改成对方
pack.chatDatas.Chatdata.Islocal = 1;
//分发到各个客户端 聊天消息
SessionList.Where(v => v != pack.chatSession).ToList().ForEach(v =>
{
(v.mSocket.Connected).ToString().KLog();
v.SendData(pack.chatDatas);
});
break;
}
}
///
/// 这里是添加处理客户端消息任务队列
///
///
///
public void AddDataPackQue(ChatSession session, ChatDatas chatData)
{
lock (obj)
{
DataPackQue.Enqueue(new ChatDatasPack { chatSession = session, chatDatas = chatData });
}
}
}
}
增加的部分
1.指令 添加多了一个HeartBeat的指令
namespace ChatNetData
{
public enum CMD
{
None,
HeartBeat,
ReqLogin,
ReqChatInfo,
RspLogin,
RspChatInfo,
}
}
2. 在ChatServe里声明持有一个KGHeartBeatManage进行初始化 在回应 添加多了一个HeartBeat的回应
public KGHeartBeatManage kGHeartBeatManage;
public override void StartCreate(string ip, int port)
{
base.StartCreate(ip, port);
kGHeartBeatManage = new KGHeartBeatManage().InitTimerEvent(null, lost =>
{
if (lost != null && lost.mSocket != null && lost.mSocket.Connected)
{
(lost.SessionID + "ID心跳包超时 准备断开连接").KLog(LogLevel.Err);
lost.Clear();
}
}).StartTimer();
//添加新的会话事件
AddSessionEvent += v =>
{
kGHeartBeatManage.AddConnectDic(v);
};
//删除会话事件
RemoveSessionEvent += v =>
{
kGHeartBeatManage.RemoveConnectDic(v);
};
}
///
/// 处理客户端发过来的消息
///
///
public void DisposePack(ChatDatasPack pack)
{
switch ((CMD)pack.chatDatas.Cmd)
{
//心跳包指令
case CMD.HeartBeat:
//更新心跳包
kGHeartBeatManage.UpdateOneHeat(pack.chatSession);
pack.chatSession.SendData(pack.chatDatas);
break;
}
}
貌似就差不多了AddSessionEvent/RemoveSessionEvent是 添加客户端 删除客户端的事件回调 这里回调之后可以直接用AddConnectDic/RemoveConnectDic 进行增删心跳包数据
InitTimerEvent 进行初始化 然后进行开启.StartTimer
客户端
客户端这边增加的也挺少 先把Tool dll拖进来工程 然后增加部分全在GameRoot
GameRoot全部代码
/****************************************************
文件:GameRoot.cs
作者:KG
邮箱: [email protected]
日期:#CreateTime#
功能:Nothing
*****************************************************/
using System.Collections;
using System.Collections.Generic;
using System.Windows.Forms;
using UnityEngine;
using KGSocket;
using ChatNetData;
using UnityEngine.UI;
using System.IO;
using System;
using KGSocket.Tool;
public class GameRoot : MonoBehaviour
{
public KGSocketClient socketClient = new KGSocketClient();
public KGHeartBeatManage heartBeatManage;
public Queue ChatQue = new Queue();
private static readonly string obj = "lock";
public LoginPanel loginPanel;
public ChatPanel chatPanel;
public Text ErrorTxt;
public byte[] HeadData;
private void Awake()
{
loginPanel.gameObject.SetActive(true);
chatPanel.gameObject.SetActive(false);
}
void Start()
{
socketClient.StartCreate("127.0.0.1", 8895);
socketClient.Client.OnStartReciveEvent += () =>
{
heartBeatManage = new KGHeartBeatManage().InitTimerEvent(send =>
{
socketClient.Client.SendData(new ChatDatas {Cmd=(int)CMD.HeartBeat });
}, obj =>
{
Debug.Log("心跳包超时准备断开连接");
if (obj!=null)
{
Debug.Log(heartBeatManage.ConnectDic[obj].Lostcount);
obj.Clear();
}
}).StartTimer();
heartBeatManage.AddConnectDic(socketClient.Client,null,5,5);
};
InItClickEvent();
}
// Update is called once per frame
void Update()
{
if (ChatQue.Count > 0)
{
Debug.Log(ChatQue.Count);
lock (obj)
{
//出列 处理
ProcessNetData(ChatQue.Dequeue());
}
}
}
///
/// 接收事件存入队列
///
///
public void AddNetData(ChatDatas info)
{
lock (obj)
{
ChatQue.Enqueue(info);
}
}
///
/// 处理接收的数据
///
///
public void ProcessNetData(ChatDatas info)
{
Debug.Log((CMD)(info.Cmd));
//先判断是否有错误
if ((ErrorInfo)(info.Err) != ErrorInfo.None)
{
switch ((ErrorInfo)(info.Err))
{
case ErrorInfo.NameRepeatsErr:
StartDelayTxt("用户名重复请更换");
loginPanel.LoginBtn.interactable = true;
break;
}
return;
}
//然后判断收到的指令 做处理
switch ((CMD)(info.Cmd))
{
case CMD.HeartBeat:
//接收到心跳包 刷新
if(heartBeatManage!=null)
heartBeatManage.UpdateOneHeat(socketClient.Client);
break;
case CMD.RspLogin:
loginPanel.gameObject.SetActive(false);
chatPanel.gameObject.SetActive(true);
chatPanel.HeadImg.LoadImage(info.HeadData);
chatPanel.PlayerNameTxt.text = info.PlayerName;
// StartCoroutine(DownloadImage(dialog.FileName, v => loginPanel.PlayerHead.texture = v));
break;
case CMD.RspChatInfo:
Debug.Log(info.PlayerName);
chatPanel.SpawnDialogUI(info.PlayerName, info.Chatdata.chat, info.HeadData.ByteToTexture(), info.Chatdata.Islocal);
break;
}
}
///
/// 按钮绑定事件
///
public void InItClickEvent()
{
//接收事件
socketClient.Client.OnReciveDataEvent += AddNetData;
//登录按钮
loginPanel.LoginBtn.onClick.AddListener(() =>
{
if (loginPanel.PlayerHead.texture.name== "unselected"|| loginPanel.NameInput.text=="")
{
StartDelayTxt("未选择头像或者名字为空 不能登录");
}
else
{
loginPanel.LoginBtn.interactable = false;
socketClient.Client.SendData(new ChatDatas
{
Cmd = (int)(CMD.ReqLogin),
PlayerName = loginPanel.NameInput.text,
HeadData = this.HeadData
});
}
});
loginPanel.SelectHeadBtn.onClick.AddListener(() =>
{
OpenFile();
});
// 发送消息按钮
chatPanel.SendChatBtn.onClick.AddListener(() =>
{
if (chatPanel.chatInput.text == "")
{
StartDelayTxt("发送的内容不能为空");
}
else
{
ChatDatas chat = new ChatDatas
{
Cmd = (int)(CMD.ReqChatInfo),
Chatdata = new SendChat() { chat = chatPanel.chatInput.text },
};
socketClient.Client.SendData(chat);
chatPanel.chatInput.text = "";
}
});
socketClient.SetLog((v, l) =>
{
Debug.Log(v + "Level:" + l);
});
}
private void OnDestroy()
{
heartBeatManage.Dispose();
socketClient.Client.Clear();
}
#region 工具
///
/// 这个是打开选择文件的
///
public void OpenFile()
{
OpenFileDialog dialog = new OpenFileDialog();
dialog.Filter = "PNG图片|*.png|JPG图片|*.jpg"; //过滤文件类型
dialog.InitialDirectory = UnityEngine.Application.dataPath; //定义打开的默认文件夹位置,可以在显示对话框之前设置好各种属性
if (dialog.ShowDialog() == DialogResult.OK)
{
HeadData = dialog.FileName.GetPictureBytes();
StartCoroutine(DownloadImage(dialog.FileName, v => loginPanel.PlayerHead.texture = v));
}
}
//加载路径图片
IEnumerator DownloadImage(string url, Action action)
{
WWW www = new WWW(url);
yield return www;
action(www.texture);
}
///
/// 提示错误信息文字
///
public void StartDelayTxt(string info)
{
ErrorTxt.text = info;
StopAllCoroutines();
StartCoroutine(DelayTxt());
}
IEnumerator DelayTxt()
{
ErrorTxt.gameObject.SetActive(true);
yield return new WaitForSeconds(2);
ErrorTxt.gameObject.SetActive(false);
}
#endregion
}
增加的部分
1.初始化
socketClient.Client.OnStartReciveEvent += () =>
{
heartBeatManage = new KGHeartBeatManage().InitTimerEvent(send =>
{
socketClient.Client.SendData(new ChatDatas {Cmd=(int)CMD.HeartBeat });
}, obj =>
{
Debug.Log("心跳包超时准备断开连接");
if (obj!=null)
{
Debug.Log(heartBeatManage.ConnectDic[obj].Lostcount);
obj.Clear();
}
}).StartTimer();
heartBeatManage.AddConnectDic(socketClient.Client,null,5,5);
};
在连接完成的事件回调中 初始化KGHeartBeatManage 并启动就可以了
2.回应心跳包指令
//然后判断收到的指令 做处理
switch ((CMD)(info.Cmd))
{
case CMD.HeartBeat:
//接收到心跳包 刷新
if(heartBeatManage!=null)
heartBeatManage.UpdateOneHeat(socketClient.Client);
break;
这里接收到了 就是进行了刷新UpdateOneHeat心跳包的超时时间和次数
好了 差不多这样
源码在下面
https://github.com/LKaiGuo/KGScriptGenerator 喜欢的给我点个星星啊
u3d萌新QQ群844087555 欢迎进来灌水=。=