- 源碼簡(jiǎn)單:源碼就幾個(gè)類(lèi),每個(gè)類(lèi)也不大,在了解TcpClient用法的基礎(chǔ)上不需要注釋就可以看懂。
- 功能單一:不像其它庫(kù)一樣功能繁多,它只專(zhuān)注于實(shí)現(xiàn)簡(jiǎn)單的TCP客戶端、服務(wù)端,使用起來(lái)不用擔(dān)心增加系統(tǒng)的復(fù)雜性。
- 使用簡(jiǎn)單:后面的示例會(huì)具體講解,啟動(dòng)一個(gè)客戶端或服務(wù)端只需要幾行代碼,想擴(kuò)展功能也很簡(jiǎn)單。
說(shuō)一下它的不足之處(在我看來(lái)完全可以接受):
- 已停止更新:最后更新時(shí)間是2017年,不過(guò)庫(kù)的功能比較簡(jiǎn)單、單一,也沒(méi)那么多更新的內(nèi)容。
- 性能不是最優(yōu)的:底層是基于TcpClient的,性能注定不會(huì)太高,但能用TcpClient的地方都可以用它。
使用方法
在項(xiàng)目中直接引用SimpleTCP.dll即可, dll文件可以通過(guò)NuGet安裝或從github下載源碼編譯。SimpleTCP內(nèi)部有一個(gè)特殊字符分割字符串的協(xié)議可以直接使用,也可以在DataReceived事件處理程序中實(shí)現(xiàn)自己的協(xié)議。
實(shí)現(xiàn)客戶端
實(shí)現(xiàn)一個(gè)客戶端的代碼如下:
//初始化
var client = new SimpleTcpClient();
//設(shè)置編碼格式,默認(rèn)是UTF8
client.StringEncoder = System.Text.ASCIIEncoding.ASCII;
//設(shè)置分隔符,默認(rèn)是0x13
client.Delimiter = Encoding.ASCII.GetBytes("\r")[0];
//收到分割數(shù)據(jù)的事件,遇到分隔符就會(huì)觸發(fā)事件
client.DelimiterDataReceived += (sender, msg) =>
{
Console.WriteLine("DelimiterStr-"+DateTime.Now.ToString()+ msg.MessageString);
};
//收到數(shù)據(jù)的事件,可以在這里實(shí)現(xiàn)自己的協(xié)議
client.DataReceived += (sender, msg) =>
{
//字節(jié)數(shù)組
Console.WriteLine("Data:"+BitConverter.ToString(msg.Data));
//字符串消息
Console.WriteLine("ReceivedStr:" + msg.MessageString);
};
DelimiterDataReceived和DataReceived內(nèi)部使用了兩個(gè)不同的字節(jié)鏈表,解析起來(lái)互不影響。這兩個(gè)事件的處理程序中盡量不要做耗時(shí)的操作,否則會(huì)影響后續(xù)的數(shù)據(jù)接收。
SimpleTCP沒(méi)有心跳、重連功能,也沒(méi)找到反饋客戶端連接狀態(tài)的屬性(不建議使用內(nèi)部TcpClient的連接狀態(tài))。我們可以直接擴(kuò)展這些功能,代碼如下:
bool exit = false;
bool connected = false;
Task.Factory.StartNew(() =>
{
while (!exit)
{
try
{
if (connected)
{
//發(fā)送心跳
client.Write("");
Task.Delay(10000).Wait();
}
else
{
//斷線重連
client.Connect("127.0.0.1", 4196);
connected = true;
Task.Delay(1000).Wait();
}
}
catch (Exception)
{
connected = false;
client.Disconnect();
}
}
}, TaskCreationOptions.LongRunning);
把上面的代碼按順序復(fù)制到控制臺(tái)的Main函數(shù)中,然后加入下面的代碼就可以收發(fā)數(shù)據(jù)了:
while (true)
{
string strLine = Console.ReadLine();
if (strLine == "esc")
{
exit = true;
client.Disconnect();
return;
}
if (connected)
{
//獲取服務(wù)端回復(fù)的消息,最多等待3秒,收到消息時(shí)會(huì)提前返回
//也可以使用Write、WriteLine方法發(fā)送數(shù)據(jù),WriteLine會(huì)自動(dòng)在后面加上設(shè)置的分隔符
var replyMsg = client.WriteLineAndGetReply(strLine, TimeSpan.fromSeconds(3));
if (replyMsg != null)
{
Console.WriteLine(replyMsg);
}
}
}
注:WriteLineAndGetReply內(nèi)部使用的是DataReceived,不會(huì)自動(dòng)去除分隔符。
實(shí)現(xiàn)服務(wù)端
服務(wù)端功能比較簡(jiǎn)單,把收到分割數(shù)據(jù)加工后返回客戶端,代碼如下:
//初始化
var server = new SimpleTcpServer();
//設(shè)置編碼格式,默認(rèn)是UTF8
server.StringEncoder = System.Text.ASCIIEncoding.ASCII;
server.Delimiter = Encoding.ASCII.GetBytes("\r")[0];
//分割數(shù)據(jù)接收事件
server.DelimiterDataReceived += (sender, msg) =>
{
Console.WriteLine(msg.TcpClient.Client.RemoteEndPoint.ToString()+":" + msg.MessageString);
msg.ReplyLine("Reply-" + msg.MessageString);
};
//數(shù)據(jù)接收數(shù)據(jù)
server.DataReceived += (sender, msg) =>
{
Console.WriteLine(msg.TcpClient.Client.RemoteEndPoint.ToString() + ":" + msg.MessageString);
};
//客戶端連接事件
server.ClientConnected += (sender, msg) =>
{
Console.WriteLine("ClientConnected:" + msg.Client.RemoteEndPoint.ToString());
};
//客戶端斷開(kāi)事件
server.ClientDisconnected += (sender, msg) =>
{
Console.WriteLine("ClientDisconnected:" + msg.Client.RemoteEndPoint.ToString());
};
//開(kāi)始監(jiān)聽(tīng)
server.Start(4196);
//監(jiān)聽(tīng)的IP
var listeningIps = server.GetListeningIPs();
//監(jiān)聽(tīng)的V4Ip
var listeningV4Ips = server.GetListeningIPs().where(ip => ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork);
Task.Factory.StartNew(() =>
{
while (true)
{
//連接數(shù)監(jiān)控
int clientsConnected = server.ConnectedClientsCount;
Console.WriteLine("當(dāng)前連接的客戶端數(shù):" + clientsConnected);
Task.Delay(10000).Wait();
}
}, TaskCreationOptions.LongRunning);
Console.ReadLine();
//停止監(jiān)聽(tīng)
server.Stop();
Console.WriteLine("停止服務(wù)端!");
Console.ReadLine();
總結(jié)
上面的代碼主要為了展示庫(kù)的功能,實(shí)際使用時(shí)可能就幾行代碼,性能要求不高的項(xiàng)目都可以使用。
為了節(jié)省大家的時(shí)間,項(xiàng)目和庫(kù)的源碼都放到某度網(wǎng)盤(pán)了。給本公號(hào)發(fā)送【源碼】可以獲取鏈接。