C#心跳機(jī)制服務(wù)器的示例代碼
什么是心跳機(jī)制?
心跳機(jī)制出現(xiàn)在tcp長連接中,客戶端和服務(wù)器之見定時發(fā)送一種特殊的數(shù)據(jù)包通知對方還在線,以確保tcp鏈接地可靠性,有可能tcp鏈接由于某些原因(列入網(wǎng)線被拔了,突然斷電)導(dǎo)致客戶端斷了,但是服務(wù)器不知道客戶端斷了,服務(wù)器還保持與客戶端連接的狀態(tài),所以為了不浪費(fèi)資源,需要知道客戶端非正常中斷,服務(wù)器把斷開客戶端斷開鏈接,需要加入心跳包機(jī)制
控制臺應(yīng)用項(xiàng)目
Program.cs
internal class Program
{
static Server server;
static void Main(string[] args)
{
Server server = new Server(IPAddress.Any,3333);
server.Start();// 除了服務(wù)器監(jiān)聽方法,監(jiān)聽客戶連接的方法,掃描客戶端是否在線的方法
//如果監(jiān)聽到有客戶端連接的時候,打印哪個終端連入到服務(wù)器了 使用時間封裝
server.有客戶端連入的事件 += 有客戶端連入服務(wù)器方法;// 綁定事件
server.客戶端斷開事件 += f2;
server.接受到消息的事件 += f3;
Console.ReadKey();
}
// 相當(dāng)于點(diǎn)擊之后的毀掉方法,在客戶端連接成功之后調(diào)用這個方法
public static void 有客戶端連入服務(wù)器方法(object obj)
{
TcpClient t1 = obj as TcpClient;
Console.WriteLine(t1.Client.RemoteEndPoint+"連接到服務(wù)器");
}
public static void f2(object obj)
{
Console.WriteLine(obj.ToString()+"斷開連接");
}
public static void f3(TcpClient t1, byte[] b1)
{
t1.GetStream().Write(b1, 0, b1.Length);
}
}Server.cs
internal class Server
{
TcpListener listen;
// 1 通過構(gòu)造函數(shù)創(chuàng)建服務(wù)器對象
public Server(IPAddress ip,int port)
{
listen = new TcpListener(ip, port);
}
// 2 封裝開啟監(jiān)聽的方法
public void Start()
{
listen.Start(100);// 開啟監(jiān)聽
// 接受客戶端的連接
StartConnect();
// 掃描心跳方法
SaoMiao();
}
// 3 接受客戶端的連接 封裝一監(jiān)聽客戶端連接的方法
// 保存所有的客戶端字典,鍵是ip 值是客戶端
Dictionary<string, TcpClient> clientDic = new Dictionary<string, TcpClient>();
// 字段保存客戶端和當(dāng)前連接服務(wù)器時間點(diǎn)
Dictionary<string, DateTime> heartDic = new Dictionary<string, DateTime>();
public event Action<TcpClient> 有客戶端連入的事件;
void StartConnect()
{
Task.Run(() =>
{
while (true)// 接入多個客戶端
{
TcpClient client = listen.AcceptTcpClient();
string ip = client.Client.RemoteEndPoint.ToString();// 獲取遠(yuǎn)程ip
// 保存當(dāng)前客戶端
clientDic.Add(ip, client);
// 記錄當(dāng)前客戶端心跳 鏈接成功的時候記錄當(dāng)前客戶端時間點(diǎn)
heartDic.Add(ip, DateTime.Now);
// 調(diào)用事件函數(shù) 觸發(fā)事件
有客戶端連入的事件?.Invoke(client);
// 4接受客戶端發(fā)來的消息
ReceivMsg(client);
}
});
}
// 4 接受客戶端發(fā)來的消息
// 封裝接受的消息
public event Action<string> 客戶端斷開事件;// 當(dāng)客戶端斷開時候調(diào)用
public event Action<TcpClient, byte[]> 接受到消息的事件;// 接收到消息調(diào)用
void ReceivMsg(TcpClient t1)
{
NetworkStream stream = t1.GetStream();
string ip = t1.Client.RemoteEndPoint.ToString();
byte[] bs = new byte[1024];
Task.Run(() =>
{
try
{
while (true)
{
int count = stream.Read(bs, 0, bs.Length);
// 必須判斷是否是心跳包 事先約定好
if (count == 0)
{
// 客戶端斷開
throw new Exception("客戶端斷開連接");
}
// 如果接收數(shù)據(jù)長度不為0
// 必須判斷是否心跳包 事先約定好:如果數(shù)據(jù)第一位是0的時候,當(dāng)成普通包
// 如果數(shù)據(jù)第一位是1說明是心跳包
switch (bs[0]) // 判斷第一位數(shù)據(jù)是不是0
{
case 0: // 普通數(shù)據(jù) 取出來的時候不需要顯示第一位標(biāo)識符
// skip 從第一位開始截取
// take 到指定位置的元素為止
// 第一位0、1代表是否心跳包標(biāo)識符
//
byte[] body = bs.Skip(1).Take(count-1).ToArray();
// 要么群發(fā) 要么單發(fā)
接受到消息的事件?.Invoke(t1,body);
break;
case 1: // 發(fā)的是心跳包
// 修改是心跳包發(fā)的時間點(diǎn)
heartDic[ip] = DateTime.Now;
break;
}
}
}
catch (Exception e)
{
// 從字典里把客戶端清除掉
clientDic.Remove(ip);
// 如果客戶端打開了,打印客戶
客戶端斷開事件?.Invoke(ip);
//刪除心跳記錄
heartDic.Remove(ip);
}
});
}
// 遍歷所有客戶端 掃描是否在未超時時間內(nèi)
void SaoMiao()
{
Task.Run(() =>
{
while (true)
{
Thread.Sleep(4000);// 線程休眠4s
DateTime now1 = DateTime.Now;
foreach (var item in heartDic) // 遍歷所以的心跳記錄
{
// now1 當(dāng)前時間點(diǎn)
// item.Value 服務(wù)器接受客戶端發(fā)來的心跳包時間
if (now1-item.Value>new TimeSpan(0,0,4))
{
Console.WriteLine(item.Key+"掉線了");
}
else
{
Console.WriteLine(item.Key+"在線");
}
}
}
});
}
// 無參數(shù)的構(gòu)造函數(shù)
public Server()
{
}
// 群發(fā)方法
public void Send()
{
}
// 指定給誰發(fā)
public void send指定()
{
}
//指定給哪些客戶端發(fā)
}到此這篇關(guān)于C#心跳機(jī)制服務(wù)器的示例代碼的文章就介紹到這了,更多相關(guān)C#心跳機(jī)制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Winform?控件優(yōu)化LayeredWindow無鋸齒圓角窗體
這篇文章主要為大家介紹了Winform?控件優(yōu)化LayeredWindow實(shí)現(xiàn)無鋸齒圓角窗體示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09
c#打印預(yù)覽控件中實(shí)現(xiàn)用鼠標(biāo)移動頁面功能代碼分享
項(xiàng)目中需要實(shí)現(xiàn)以下功能:打印預(yù)覽控件中,可以用鼠標(biāo)拖動頁面,以查看超出顯示范圍之外的部分內(nèi)容,下面就是實(shí)現(xiàn)代碼2013-12-12
C#中new和override的區(qū)別個人總結(jié)
這篇文章主要介紹了C#中new和override的區(qū)別個人總結(jié),本文以問答的方式講解了new和override的區(qū)別,需要的朋友可以參考下2015-06-06
C#十六進(jìn)制字符串轉(zhuǎn)十進(jìn)制int的方法
這篇文章主要介紹了C#十六進(jìn)制字符串轉(zhuǎn)十進(jìn)制int的方法,涉及C#操作數(shù)制轉(zhuǎn)換的技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-03-03
C#實(shí)現(xiàn)PDF文檔自動化生成的開發(fā)實(shí)戰(zhàn)
在現(xiàn)代軟件應(yīng)用中,PDF文檔因其跨平臺、內(nèi)容固定性強(qiáng)以及易于分享的特性,扮演著不可或缺的角色,本文將深入探討如何利用C#強(qiáng)大的能力,結(jié)合一款功能豐富的PDF處理庫,實(shí)現(xiàn)PDF文檔的自動化生成,需要的朋友可以參考下2026-01-01
C#實(shí)現(xiàn)窗體中動態(tài)按鈕的設(shè)計(jì)方法
在窗體界面中,通常以按鈕來代替菜單欄的功能,這種形式雖然給用戶一種直觀、界面風(fēng)格各異的感覺,但通常按鈕都是以靜止的形式顯示,所以本文給大家介紹了C#實(shí)現(xiàn)窗體中動態(tài)按鈕的設(shè)計(jì)方法,感興趣的朋友可以參考下2024-04-04

