免费开源的Socket工具——SuperSocket的运用实例
写这篇文章的初衷,是要解决TCP通讯的粘包和分包问题。本人所做的项目,遇见粘包和分包的问题比较严重。粘包的解决办法很多,比如使用struck定长,增加byte长度信息进行解析等等。但是,一方面这些方法和需求有冲突,另一方面按照网络上的做法,最后都不尽如人意。所以决定用第三方的工具SuperSocket,这里总结一下这个工具的使用经验。
本次demo地址: https://gitee.com/chenheze90/L14_SuperSocket
首先创建两个工程:SuperSocket的服务和客户端
给服务客户端导入SuperSocket插件
SuperSocket.Engine
这里要注意,是SuperSocket.Engine而不是SuperSocket!!!
服务器项目SuperSocketServer的主界面MainWindow增加Loaded="Window_Loaded"事件,load事件代码如下
private void Window_Loaded(object sender, RoutedEventArgs e)
{
Task.Run(() =>
{
//实例化服务,AppServer是SuperSocket命名空间的tcp服务类
AppServer appServer = new AppServer();
appServer.NewSessionConnected += appServer_NewSessionConnected;
appServer.NewRequestReceived += appServer_NewRequestReceived;
appServer.SessionClosed += appServer_SessionClosed;
//配置:配置比较重要,特别是TextEncoding的设置,关系到是否能够传输汉字
ServerConfig serverConfig = new ServerConfig()
{
Ip = "127.0.0.1",
Port = 9514,
TextEncoding = "UTF-8",
Mode = SocketMode.Tcp,
};
//安全起见,对服务的设置和启动做了防呆,可以有效避免异常的情况
if (!appServer.Setup(serverConfig))
{
return;
}
if (!appServer.Start())
{
return;
}
});
}
信息接收事件,必须要用Application.Current.Dispatcher.Invoke,因为控件是前端线程生成,其他线程不能直接使用
private void appServer_NewRequestReceived(AppSession session, StringRequestInfo requestInfo)
{
try
{
Application.Current.Dispatcher.Invoke(() =>
{
txbMsg.Text += requestInfo.Key;
});
}
catch (Exception ex)
{
log4net.LogManager.GetLogger("TCP").Warn("接受数据失败", ex);
}
}
客户端连接事件和会话关闭事件:客户端连接事件在处理高并发和客户端管理的情况下比较有用,可以有选择地释放部分无用的客户端,节省内存和消耗。
会话关闭事件:supersocket有一个问题尚未解决,当发送的信息量超过1448byte的时候,会触发session关闭事件。因为tcp的数据上限是1448byte。
static void appServer_NewSessionConnected(AppSession session)
{
}
static void appServer_SessionClosed(AppSession session, CloseReason reason)
{
log4net.LogManager.GetLogger("TCP").Warn("TCP客户端断连,原因:" + reason.ToString());
}
客户端代码相对简单,就是将文本转化成byte[]就可以发送。
但是SuperSocket有个特殊的地方,一定要加上"\r\n"。如下代码所示,需要将"\r\n"转化成byte[]之后,加在原字符串的末尾才可以发送。
private async void Window_Loaded(object sender, RoutedEventArgs e)
{
TcpClient tcpClient = new TcpClient();
tcpClient.Connect("127.0.0.1", 9514);
NetworkStream netStream = tcpClient.GetStream();
while (true)
{
try
{
string tcpdata = "你好,世界!";
byte[] byteArray = Encoding.UTF8.GetBytes(tcpdata);
byte[] byteArrayLength = Encoding.UTF8.GetBytes("\r\n");//\r\n
byte[] combined = new byte[byteArray.Length + 2];
byteArray.CopyTo(combined, 0);
byteArrayLength.CopyTo(combined, byteArray.Length);
netStream.Write(combined, 0, combined.Length);
//rb.IsChecked = false;
}
catch (Exception ex)
{
netStream.Close();
tcpClient.Close();
MessageBox.Show(ex.ToString());
}
await Task.Delay(5000);
}
}