這里 接著上次那個聊天室 說下改動吧 也沒改多少
就是客戶端會發心跳包過來 服務器刷新超時時間并且回應 給客戶端,如果都超時沒有接到回應給 就會主動回調 超時事件 這就是心跳包的過程
服務端
ChatServer全部代碼
using System;
using System.Collections.Generic;
using System.Linq;
using KGSocket;
using ChatNetData;
using KGSocket.Tool;
namespace ChatServer
{
//各個客戶端管理 業務邏輯都在這了
public class ChatServe : KGSocketServe<ChatSession, ChatDatas>
{
#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<ChatDatasPack> DataPackQue = new Queue<ChatDatasPack>();
#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<ChatSession, KGHeartBeat> 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<ChatSession, KGHeartBeat>().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);
};
}
/// <summary>
/// 處理客戶端發過來的消息
/// </summary>
/// <param name="pack"></param>
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;
}
}
/// <summary>
/// 這里是添加處理客戶端消息任務隊列
/// </summary>
/// <param name="session"></param>
/// <param name="chatData"></param>
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<ChatSession, KGHeartBeat> kGHeartBeatManage;
public override void StartCreate(string ip, int port)
{
base.StartCreate(ip, port);
kGHeartBeatManage = new KGHeartBeatManage<ChatSession, KGHeartBeat>().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);
};
}
/// <summary>
/// 處理客戶端發過來的消息
/// </summary>
/// <param name="pack"></param>
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
郵箱: 695907593@qq.com
日期:#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<ChatNetSession, ChatDatas> socketClient = new KGSocketClient<ChatNetSession, ChatDatas>();
public KGHeartBeatManage<ChatNetSession, KGHeartBeat> heartBeatManage;
public Queue<ChatDatas> ChatQue = new Queue<ChatDatas>();
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<ChatNetSession, KGHeartBeat>().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());
}
}
}
/// <summary>
/// 接收事件存入隊列
/// </summary>
/// <param name="info"></param>
public void AddNetData(ChatDatas info)
{
lock (obj)
{
ChatQue.Enqueue(info);
}
}
/// <summary>
/// 處理接收的數據
/// </summary>
/// <param name="info"></param>
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;
}
}
/// <summary>
/// 按鈕綁定事件
/// </summary>
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 工具
/// <summary>
/// 這個是打開選擇文件的
/// </summary>
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<Texture2D> action)
{
WWW www = new WWW(url);
yield return www;
action(www.texture);
}
/// <summary>
/// 提示錯誤信息文字
/// </summary>
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<ChatNetSession, KGHeartBeat>().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 歡迎進來灌水=。=