最近在维护老项目,感觉内存一直都有问题,定位到问题是WebSocketServer的问题,了解了 Fleck、SuperSocket、TouchSocke 等开源项目,这里记录一下。

.net5、.net6、.net7、.net8 项目已经集成了WebSocket,只要  app.UseWebSockets() 代码就可以了, 详情见 WebSockets support in ASP.NET Core | Microsoft Learn

0. 控制台运行的代码

代码:https://gitee.com/Karl_Albright/csharp-web-socket-server

internal class Program
{
static void Main(string[] args)
{
WebSockSvr server = new WebSockSvr();
server.Start();
server.SendDatas();
Console.ReadLine();
}
}

1. Fleck

兼容 .NetFramework V4.0、.NetFramework V4.5、.NetCoreApp V2.0、.NetStandard V2.0

dotnet add package Fleck --version 1.2.0
using Fleck;
namespace FleckDemo
{public class FleckWebSockSvr
{
public List<IWebSocketConnection> ClinetList = new();
private WebSocketServer service; public FleckWebSockSvr()
{
service = new WebSocketServer("ws://0.0.0.0:4040");
} public void Start()
{
service.Start(socket =>
{
socket.OnOpen = () =>
{
Console.WriteLine("Open!");
ClinetList.Add(socket);
};
socket.OnClose = () =>
{
Console.WriteLine("Close!");
ClinetList.Remove(socket);
};
socket.OnMessage = message =>
{
Console.WriteLine(message);
ClinetList.ToList().ForEach(s => s.Send("Echo: " + message));
};
});
} public void SendDatas()
{
for (int i = 0; i < 200; i++)
{
Task.Run(async () =>
{
while (true)
{
try
{
for (int j = 0; j < ClinetList.Count; j++)
{
var sock = ClinetList[j];
if (sock.IsAvailable)
{
await sock.Send($"Dev[{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")}, 12.34, 34.56, 56.78, \"77705683\"]");
}
}
}
catch (Exception ex)
{
Console.WriteLine("出现异常:" + ex.Message + "\r\n" + ex.StackTrace);
}
finally
{
await Task.Delay(1000);
}
}
});
}
}
}
}

2. SuperSocket1.6

截止到现在 superSocket 2.0版本还没正式发布,有beta.26版本,和1.6相比改动挺大的。

兼容 .NetFramework V4.6.1、.NetFramework V4.6.2、.NetFramework V4.7、.NetFramework V4.7.1、.NetFramework V4.7.2、.NetFramework V4.8、.NetFramework V4.8.1

dotnet add package SuperSocket --version 1.6.6.1
dotnet add package SuperSocket.Engine --version 1.6.6.1
dotnet add package SuperSocket.WebSocket --version 1.6.6.1
using SuperSocket.SocketBase;
using SuperSocket.WebSocket;
using System;
using System.Collections.Generic;
using System.Threading.Tasks; namespace SuperSocketDemo
{
public class WebSockSvr
{
public List<WebSocketSession> ClinetList { get; set; } = new List<WebSocketSession>();
private WebSocketServer server;
public WebSockSvr()
{
server = new WebSocketServer();
server.NewMessageReceived += Ws_NewMessageReceived;//当有信息传入时
server.NewSessionConnected += Ws_NewSessionConnected;//当有用户连入时
server.SessionClosed += Ws_SessionClosed;//当有用户退出时
server.NewDataReceived += Ws_NewDataReceived;//当有数据传入时
} public void Start()
{
if (server.Setup(4040))//绑定端口
server.Start();//启动服务
} //public void SendDatas()
//{
// //对当前已连接的所有会话进行广播
// foreach (var session in server.GetAllSessions())
// {
// session.Send($"Dev[{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")}, 12.34, 34.56, 56.78, \"77705683\"]");
// Thread.Sleep(1000);
// }
//}
public void SendDatas()
{
for (int i = 0; i < 200; i++)
{
Task.Run(async () =>
{
while (true)
{
try
{
for (int j = 0; j < ClinetList.Count; j++)
{
var sock = ClinetList[j];
if (sock.Connected)
{
sock.TrySend($"Dev[{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")}, 12.34, 34.56, 56.78, \"77705683\"]");
}
}
}
catch (Exception ex)
{
Console.WriteLine("出现异常:" + ex.Message + "\r\n" + ex.StackTrace);
}
finally
{
await Task.Delay(10);
}
}
});
} } private void Ws_NewSessionConnected(WebSocketSession session)
{
ClinetList.Add(session);
} private void Ws_NewMessageReceived(WebSocketSession session, string value)
{ } private void Ws_SessionClosed(WebSocketSession session, CloseReason value)
{
ClinetList.Remove(session);
} private void Ws_NewDataReceived(WebSocketSession session, byte[] value)
{ }
}
}

3. SuperSocket2.0.0-beta.26

兼容 .NetStandard V2.1、.Net5、.Net6、.Net7、.Net8

dotnet add package SuperSocket.WebSocket.Server --version 2.0.0-beta.26
using Microsoft.Extensions.Hosting;
using SuperSocket.Server;
using SuperSocket.Server.Abstractions;
using SuperSocket.Server.Host;
using SuperSocket.WebSocket.Server; namespace SuperSocket2Demo
{public class WebSockSvr
{
public List<WebSocketSession> ClinetList { get; set; } = new();
private IServer server;
public WebSockSvr()
{
server = WebSocketHostBuilder.Create()
.ConfigureSuperSocket(opts =>
{
opts.AddListener(new ListenOptions
{
Ip = "127.0.0.1",
Port = 4040
});
})
.UseSessionHandler((session) =>
{
var sess = (WebSocketSession)session;
ClinetList.Add(sess);
return ValueTask.CompletedTask;
}, (session, reason) =>
{
var sess = (WebSocketSession)session;
ClinetList.Remove(sess);
return ValueTask.CompletedTask;
})
.UseWebSocketMessageHandler(async (session, message) =>
{
})
.BuildAsServer();
}
public Task Start()
{
return server.StartAsync();
}
public void SendDatas()
{
for (int i = 0; i < 200; i++)
{
Task.Run(async () =>
{
while (true)
{
try
{
for (int j = 0; j < ClinetList.Count; j++)
{
var sock = ClinetList[j];
if (sock.State == SessionState.Connected)
{
await sock.SendAsync($"Dev[{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")}, 12.34, 34.56, 56.78, \"77705683\"]");
}
}
}
catch (Exception ex)
{
Console.WriteLine("出现异常:" + ex.Message + "\r\n" + ex.StackTrace);
}
finally
{
await Task.Delay(10);
}
}
});
}
}
}
}

4. TouchSocket

目前兼容 .NetFramework V4.5、.NetFramework V4.6.2、.NetFramework V4.7.2、.NetFramework V4.8.1、.NetStandard V2.0、.NetStandard V2.1、.Net6、.Net7、.Net8

dotnet add package TouchSocket --version 2.1.5
dotnet add package TouchSocket.Http --version 2.1.5
dotnet add package TouchSocket.WebApi --version 2.1.5
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using TouchSocket.Core;
using TouchSocket.Http;
using TouchSocket.Http.WebSockets;
using TouchSocket.Rpc;
using TouchSocket.Sockets;
using TouchSocket.WebApi; namespace TouchSocketDemo.Core
{
public class WebSockSvr
{
public List<IHttpSession> ClinetList { get; set; } = new List<IHttpSession>();
private HttpService service;
public WebSockSvr()
{
service = new HttpService();
}
public async Task Start()
{
await service.SetupAsync(new TouchSocketConfig()//加载配置
.SetListenIPHosts(4040)
.ConfigureContainer(a =>
{
a.AddConsoleLogger();
})
.ConfigurePlugins(a =>
{
a.UseWebSocket().SetWSUrl(null).UseAutoPong();
//a.Add<MyWebSocketPlugin>();
a.Add(typeof(IWebSocketHandshakedPlugin), async (IWebSocket client, HttpContextEventArgs e) =>
{
ClinetList.Add(client.Client);
await e.InvokeNext();
});
a.Add(typeof(IWebSocketClosingPlugin), async (IWebSocket client, ClosedEventArgs e) =>
{
ClinetList.Remove(client.Client);
await e.InvokeNext();
}); a.Add(typeof(IWebSocketReceivedPlugin), async (IWebSocket client, WSDataFrameEventArgs e) =>
{
switch (e.DataFrame.Opcode)
{
case WSDataType.Close:
{
await client.CloseAsync("断开");
}
return;
case WSDataType.Ping:
await client.PongAsync();//收到ping时,一般需要响应pong
break;
case WSDataType.Pong:
break;
default:
break;
} await e.InvokeNext();
}); a.UseWebSocketReconnection();//a.Add<MyWebSocketPlugin>();
}));
await service.StartAsync();
} public void SendDatas()
{
for (int i = 0; i < 200; i++)
{
Task.Run(async () =>
{
while (true)
{
try
{
var clientList = ClinetList.ToList();
for (int j = 0; j < clientList.Count; j++)
{
var sock = (HttpSessionClient)clientList[j];
if (sock.Online)
{
await sock.WebSocket.SendAsync($"Dev[{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")}, 12.34, 34.56, 56.78, \"77705683\"]");
}
}
}
catch (Exception ex)
{
Console.WriteLine("出现异常:" + ex.Message + "\r\n" + ex.StackTrace);
}
finally
{
await Task.Delay(10);
}
}
});
}
}
public class MyWebSocketPlugin : PluginBase, IWebSocketHandshakingPlugin, IWebSocketHandshakedPlugin, IWebSocketReceivedPlugin
{
public MyWebSocketPlugin(ILog logger)
{
this.m_logger = logger;
} public async Task OnWebSocketHandshaking(IWebSocket client, HttpContextEventArgs e)
{
if (client.Client is IHttpSessionClient socketClient)
{
//服务端
var id = socketClient.Id;
}
else if (client.Client is IHttpClient httpClient)
{
//客户端
}
this.m_logger.Info("WebSocket正在连接");
await e.InvokeNext();
} public async Task OnWebSocketHandshaked(IWebSocket client, HttpContextEventArgs e)
{
this.m_logger.Info("WebSocket成功连接");
await e.InvokeNext();
} private readonly ILog m_logger; public async Task OnWebSocketReceived(IWebSocket client, WSDataFrameEventArgs e)
{
switch (e.DataFrame.Opcode)
{
case WSDataType.Close:
{await client.CloseAsync("断开");
}
return;
case WSDataType.Ping:await client.PongAsync();//收到ping时,一般需要响应pong
break;
case WSDataType.Pong:
this.m_logger.Info("Pong");
break;
default:
{
//其他报文,需要考虑中继包的情况。所以需要手动合并 WSDataType.Cont类型的包。
//或者使用消息合并器
//获取消息组合器
var messageCombinator = client.GetMessageCombinator();
try
{
//尝试组合
if (messageCombinator.TryCombine(e.DataFrame, out var webSocketMessage))
{
//组合成功,必须using释放模式
using (webSocketMessage)
{
//合并后的消息
var dataType = webSocketMessage.Opcode; //合并后的完整消息
var data = webSocketMessage.PayloadData; if (dataType == WSDataType.Text)
{
//按文本处理
}
else if (dataType == WSDataType.Binary)
{
//按字节处理
}
else
{
//可能是其他自定义协议
}
}
}
}
catch (Exception ex)
{
this.m_logger.Exception(ex);
messageCombinator.Clear();//当组合发生异常时,应该清空组合器数据
}
}
break;
}
await e.InvokeNext();
}
}
}
}

C# WebSocket Servers -- Fleck、SuperSocket、TouchSocke的更多相关文章

  1. Web端即时通讯技术盘点:短轮询、Comet、Websocket、SSE

    1. 前言 Web端即时通讯技术因受限于浏览器的设计限制,一直以来实现起来并不容易,主流的Web端即时通讯方案大致有4种:传统Ajax短轮询.Comet技术.WebSocket技术.SSE(Serve ...

  2. 基于Tomcat7、Java、WebSocket的服务器推送聊天室

    http://blog.csdn.net/leecho571/article/details/9707497 http://blog.fens.me/java-websocket-intro/ jav ...

  3. python全栈开发day115、116-websocket、websocket原理、websocket加解密、简单问答机器人实现

    1.websocket 1.websocket 与轮询 轮询: 不断向服务器发起询问,服务器还不断的回复 浪费带宽,浪费前后端资源 保证数据的实时性 长轮询: 1.客户端向服务器发起消息,服务端轮询, ...

  4. 006-优化web请求二-应用缓存、异步调用【Future、ListenableFuture、CompletableFuture】、ETag、WebSocket【SockJS、Stomp】

    四.应用缓存 使用spring应用缓存.使用方式:使用@EnableCache注解激活Spring的缓存功能,需要创建一个CacheManager来处理缓存.如使用一个内存缓存示例 package c ...

  5. fiddler 手机 https 抓包 以及一些fiddler无法解决的https问题http2、tcp、udp、websocket证书写死在app中无法抓包

    原文: https://blog.csdn.net/wangjun5159/article/details/52202059 fiddler手机抓包原理 fiddler手机抓包的原理与抓pc上的web ...

  6. Tomcat学习总结(4)——基于Tomcat7、Java、WebSocket的服务器推送聊天室

    前言           HTML5 WebSocket实现了服务器与浏览器的双向通讯,双向通讯使服务器消息推送开发更加简单,最常见的就是即时通讯和对信息实时性要求比较高的应用.以前的服务器消息推送大 ...

  7. Web通信协议:OSI、TCP、UDP、Socket、HTTP、HTTPS、TLS、SSL、WebSocket、Stomp

    1      各层的位置 1.1      OSI七层模型全景图 OSI是Open System Interconnect的缩写,意为开放式系统互联. 1.2      五层网络协议 在七层的基础上, ...

  8. Websocket - Websocket原理(握手、解密、加密)、基于Python实现简单示例

    一.Websocket原理(握手.解密.加密) WebSocket协议是基于TCP的一种新的协议.WebSocket最初在HTML5规范中被引用为TCP连接,作为基于TCP的套接字API的占位符.它实 ...

  9. python中socket、socketio、flask-socketio、WebSocket的区别与联系

    socket.socketio.flask-socketio.WebSocket的区别与联系 socket 是通信的基础,并不是一个协议,Socket是应用层与TCP/IP协议族通信的中间软件抽象层, ...

  10. 基于supersocket、C#对JT808协议进行解析构建gps监控平台服务端

    GPS监控平台.车联网.物联网系统中GPRS网络数据的并发通讯和处理解析,主要功能有socket的UDP和TCP链路建立和维持,网络数据协议包接收与解析,分发上传到其他业务规则服务器,在物联网以及位置 ...

随机推荐

  1. C# 开发技巧 轻松监控方法执行耗时

    前言 MethodTimer.Fody 是一个功能强大的库,可以用于测量 .NET 应用程序中的方法的执行时间.允许你在不修改代码的情况下,自动地测量和记录方法的执行时间. 这个工具是基于.NET的 ...

  2. 空间反演对称性 (Spatial Inversion Symmetry) 和非线性响应 (Non-linear Response)

    我们定义一次宇称变换 (parity transformation) 为反转所有坐标: \[\mathcal{P}: \begin{pmatrix} x \\ y \\ z \end{pmatrix} ...

  3. .NET 高性能缓冲队列实现 BufferQueue

    目录 前言 适用场景 功能说明 使用示例 BufferQueue 内部设计概述 Topic 的隔离 Partition 的设计 对并发的支持 Partition 的动态扩容 Segment 的回收机制 ...

  4. Bulk RNA-seq 基本分析流程

    目的: 对illumina数据进行处理,利用 RNA-Seq 发现新的 RNA 变体和剪接位点,或量化 mRNA 以进行基因表达分析等.对两组或多组样本的转录组数据,通过差异表达分析和对所发现的差异表 ...

  5. 【SpringMVC】09 对JSON的应用

    前面JavaWeb的JSON回顾: https://www.cnblogs.com/mindzone/p/12820877.html 上面的这个帖子我都还没有实际写进Servlet使用,要Mark一下 ...

  6. 【PC-Game】世嘉拉力:进化

    SegaRally:Revo游戏本体资源: 游侠网115盘 + 详细介绍 https://game.ali213.net/forum.php?mod=viewthread&tid=409661 ...

  7. 国产操作系统 “银河麒麟操作系统V10” 试用失败历程

    面对外国的科技封锁,具有自主产权的国产软件已经变得迫在眉睫了,几天前在新闻上看到国产的操作"银河麒麟操作系统V10"已经发布,于是抱着尝鲜的心态想着去试着用用.虽然都是基于linu ...

  8. 新兴互联网银行搭档Apache SeaTunnel构建数据流通管道!

    当新兴互联网银行乘着数字化改革的风潮搭档数据集成平台Apache SeaTunnel,成千万上亿的数据就有了快速流通的管道.6月26日14:00,Apache SeaTunnel社区将带上企业最佳实践 ...

  9. java关于二维数组的操作

    代码: ''' package tests; public class Yanghui { public static void main(String[] args) { //声明二维数组的三种方式 ...

  10. 痞子衡嵌入式:探析i.MXRT1050在GPIO上增加RC延时电路后导致边沿中断误触发问题(上篇)

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是i.MXRT1050在GPIO上增加RC延时电路后导致边沿中断误触发问题探析. 前段时间有一个 RT1052 客户反馈了一个有趣的问题, ...