C#通过fleck实现wss协议的WebSocket多人Web实时聊天(附源码)

article/2025/10/6 4:53:34

前言

最近想做一个Web版的即时聊天为后面开发的各项功能做辅助,就需要浏览器与服务器能够实时通讯。而WebSocket这种双向通信协议,就很合适用来实现这种需求。
本篇文章主要解决C#如何实现WebSocket服务端和Javascript客户端基于wss协议的安全通信问题。
本文代码已开源至Github:https://github.com/hxsfx/WebSocketServerTest

环境

  • 编程语言:C#
  • Websocket开源库:fleck
  • SSL域名证书:腾讯云IIS版本域名证书

最终效果

在这里插入图片描述

代码实现

前端

1、HTML

<!DOCTYPE html>
<html>
<head><meta charset="utf-8" /><title></title><link href="Content/index.css" rel="stylesheet" />
</head>
<body><div id="ChatContainer"><div class="tip "></div><div class="msgList"></div><div class="msgInput"><textarea id="SendMsgContent"></textarea><button id="SendMsgButton">发送</button></div></div><script src="Scripts/index.js"></script>
</body>
</html>

2、JavaScript

window.onload = function () {var TipElement = document.querySelector("#ChatContainer > div.tip");var MsgListElement = document.querySelector("#ChatContainer > div.msgList");var SendMsgContentElement = document.getElementById("SendMsgContent");var SendMsgButton = document.getElementById("SendMsgButton");window.wss = new WebSocket("wss://xxx.hxsfx.com:xxx");//监听消息状态wss.onmessage = function (e) {var dataJson = JSON.parse(e.data);loadData(dataJson.nickName, dataJson.msg, dataJson.date, dataJson.time, true);}//监听链接状态wss.onopen = function () {if (TipElement.className.indexOf("conn") < 0) {TipElement.className = TipElement.className + " conn";}if (TipElement.className.indexOf("disConn") >= 0) {TipElement.className = TipElement.className.replace("disConn", "");}}//监听关闭状态wss.onclose = function () {if (TipElement.className.indexOf("conn") >= 0) {TipElement.className = TipElement.className.replace("conn", "");}if (TipElement.className.indexOf("disConn") < 0) {TipElement.className = TipElement.className + " disConn";}}//监控输入框回车键(直接发送输入内容)SendMsgContentElement.onkeydown = function () {if (event.keyCode == 13 && SendMsgContentElement.value.trim() != "") {if (SendMsgContentElement.value.trim() != "") {SendMsgButton.click();event.returnValue = false;} else {SendMsgContentElement.value = "";}}}//发送按钮点击事件SendMsgButton.onclick = function () {var msgDataJson = {msg: SendMsgContentElement.value,};SendMsgContentElement.value = "";var today = new Date();var date = today.getFullYear() + "年" + (today.getMonth() + 1) + "月" + today.getDate() + "日";var time = today.getHours() + ":" + today.getMinutes() + ":" + today.getSeconds();loadData("自己", msgDataJson.msg, date, time, false);let msgDataJsonStr = JSON.stringify(msgDataJson);wss.send(msgDataJsonStr);}//把数据加载到对话框中function loadData(nickName, msg, date, time, isOther) {let msgItemElement = document.createElement('div');if (isOther) {msgItemElement.className = "msgItem other";} else {msgItemElement.className = "msgItem self";}let chatHeadElement = document.createElement('div');chatHeadElement.className = "chatHead";chatHeadElement.innerHTML = "<svg viewBox=\"0 0 1024 1024\"><path d=\"M956.696128 512.75827c0 245.270123-199.054545 444.137403-444.615287 444.137403-245.538229 0-444.522166-198.868303-444.522166-444.137403 0-188.264804 117.181863-349.108073 282.675034-413.747255 50.002834-20.171412 104.631012-31.311123 161.858388-31.311123 57.297984 0 111.87909 11.128455 161.928996 31.311123C839.504032 163.650197 956.696128 324.494489 956.696128 512.75827L956.696128 512.75827M341.214289 419.091984c0 74.846662 38.349423 139.64855 94.097098 171.367973 23.119557 13.155624 49.151443 20.742417 76.769454 20.742417 26.64894 0 51.773154-7.096628 74.286913-19.355837 57.06467-31.113625 96.650247-96.707552 96.650247-172.742273 0-105.867166-76.664054-192.039781-170.936137-192.039781C417.867086 227.053226 341.214289 313.226864 341.214289 419.091984L341.214289 419.091984M513.886977 928.114163c129.883139 0 245.746984-59.732429 321.688583-153.211451-8.971325-73.739445-80.824817-136.51314-182.517917-167.825286-38.407752 34.55091-87.478354 55.340399-140.989081 55.340399-54.698786 0-104.770182-21.907962-143.55144-57.96211-98.921987 28.234041-171.379229 85.823668-188.368158 154.831344C255.507278 861.657588 376.965537 928.114163 513.886977 928.114163L513.886977 928.114163M513.886977 928.114163 513.886977 928.114163z\"></path></svg>";let msgMainElement = document.createElement('div');msgMainElement.className = "msgMain";let nickNameElement = document.createElement('div');nickNameElement.className = "nickName";nickNameElement.innerText = nickName;let msgElement = document.createElement('div');msgElement.className = "msg";msgElement.innerText = msg;let timeElement = document.createElement('div');timeElement.className = "time";let time_date_Element = document.createElement('span');time_date_Element.innerText = date;let time_time_Element = document.createElement('span');time_time_Element.innerText = time;timeElement.append(time_date_Element);timeElement.append(time_time_Element);msgMainElement.append(nickNameElement);msgMainElement.append(msgElement);msgMainElement.append(timeElement);msgItemElement.append(chatHeadElement);msgItemElement.append(msgMainElement);MsgListElement.append(msgItemElement);MsgListElement.scrollTop = MsgListElement.scrollHeight - MsgListElement.clientHeight;}
} 

3、CSS

* {padding: 0;margin: 0;
}
html,
body {font-size: 14px;height: 100%;
}
body {padding: 2%;box-sizing: border-box;background-color: #a3aebc;
}
#ChatContainer {padding: 1% 25px 0 25px;width: 80%;max-width: 850px;height: 100%;background-color: #fefefe;border-radius: 10px;box-sizing: border-box;margin: auto;
}
#ChatContainer .tip {height: 30px;line-height: 30px;text-align: center;align-items: center;justify-content: center;color: #999999;
}
#ChatContainer .tip:before {content: "连接中";
}
#ChatContainer .tip.disConn {color: red;
}
#ChatContainer .tip.disConn:before {content: "× 连接已断开";
}
#ChatContainer .tip.conn {color: green;
}
#ChatContainer .tip.conn:before {content: "√ 已连接";
}
#ChatContainer .msgList {display: flex;flex-direction: column;overflow-x: hidden;overflow-y: auto;height: calc(100% - 100px);
}
#ChatContainer .msgList .msgItem {display: flex;margin: 5px;
}
#ChatContainer .msgList .msgItem .chatHead {height: 36px;width: 36px;background-color: #ffffff;border-radius: 100%;
}
#ChatContainer .msgList .msgItem .msgMain {margin: 0 5px;display: flex;flex-direction: column;
}
#ChatContainer .msgList .msgItem .msgMain .nickName {color: #666666;
}
#ChatContainer .msgList .msgItem .msgMain .msg {padding: 10px;line-height: 30px;color: #333333;
}
#ChatContainer .msgList .msgItem .msgMain .time {color: #999999;font-size: 9px;
}
#ChatContainer .msgList .msgItem .msgMain .time span:first-child {margin-right: 3px;
}
#ChatContainer .msgList .self {flex-direction: row-reverse;
}
#ChatContainer .msgList .self .nickName {text-align: right;
}
#ChatContainer .msgList .self .msg {border-radius: 10px 0 10px 10px;background-color: #d6e5f6;
}
#ChatContainer .msgList .self .time {text-align: right;
}
#ChatContainer .msgList .other .msg {border-radius: 0 10px 10px 10px;background-color: #e8eaed;
}
#ChatContainer .msgInput {margin: 15px 0;display: flex;
}
#ChatContainer .msgInput textarea {font-size: 16px;padding: 0 5px;width: 80%;box-sizing: border-box;height: 40px;line-height: 40px;overflow: hidden;color: #333333;border-radius: 10px 0 0 10px;border: none;outline: none;border: 1px solid #eee;resize: none;
}
#ChatContainer .msgInput button {width: 20%;text-align: center;height: 40px;line-height: 40px;color: #fefefe;background-color: #2a6bf2;border-radius: 0 10px 10px 0;border: 1px solid #2a6bf2;
}

后端

创建控制台程序(通过cmd命令调用,可修改源码为直接运行使用),然后进入Gnet安装fleck,其中的主要代码如下(完整源码移步github获取):

//组合监听地址
var loaction = webSocketProtocol + "://" + ListenIP + ":" + ListenPort;
var webSocketServer = new WebSocketServer(loaction);
if (loaction.StartsWith("wss://"))
{webSocketServer.Certificate = new X509Certificate2(pfxFilePath, pfxPassword, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);webSocketServer.EnabledSslProtocols = System.Security.Authentication.SslProtocols.Tls12;
}//当为安全链接时,将证书信息写入链接
//开始侦听
webSocketServer.Start(socket =>
{var socketConnectionInfo = socket.ConnectionInfo;var clientId = socketConnectionInfo.ClientIpAddress + ":" + socketConnectionInfo.ClientPort;socket.OnOpen = () =>{if (!ip_scoket_Dic.ContainsKey(clientId)){ip_scoket_Dic.Add(clientId, socket);}Console.WriteLine(CustomSend("服务端", $"[{clientId}]加入"));};socket.OnClose = () =>{if (ip_scoket_Dic.ContainsKey(clientId)){ip_scoket_Dic.Remove(clientId);}Console.WriteLine(CustomSend("服务端", $"[{clientId}]离开"));};socket.OnMessage = message =>{//将发送过来的json字符串进行解析var msgModel = JsonConvert.DeserializeObject<MsgModel>(message);Console.WriteLine(CustomSend(clientId, msgModel.msg, clientId));};
});
//出错后进行重启
webSocketServer.RestartAfterListenError = true;
Console.WriteLine("【开始监听】" + loaction);
//服务端发送消息给客户端
do
{Console.WriteLine(CustomSend("服务端", Console.ReadLine()));
} while (true);

问题及解决方法

问题:WebSocket connection to ‘wss://xxx.xxx.xxx.xxx:xxxx/’ failed:
解决方法:要建立WSS安全通道,必须要先申请域名SSL证书,同时在防火墙中开放指定端口,以及前端WSS请求域名要跟SSL证书域名相同。


http://chatgpt.dhexx.cn/article/mbiaH7ft.shtml

相关文章

Asp.Net Mvc基于Fleck开发的多人网页版即时聊天室

一、项目的核心说明 1、Fleck这个是实现websocket一个比较简单第三方组件&#xff0c;它不需要安装额外的容器。本身也就几个接口可供调用。 2、项目是基于.net framework 4.7.2 &#xff0c;在vs2019上开发的&#xff0c;没试过在低版本上运行。但是代码上没怎么用到新特性&…

winfrom+Fleck 上传文件

最近这个项目要做一个Winfrom上传Execl到服务器。 就想到之前用Fleck做的一个小功能&#xff0c;但是客户端Web端的。百度吧&#xff0c;看有没有办法&#xff0c;开始找到fw4 &#xff0c;但是要引用它的dll&#xff0c;是可以&#xff0c;继续找了会结果找到ClientWebSocket…

C#工作总结(一):Fleck的WebSocket使用

一.引子&#xff08;Foreword&#xff09; 最近公司里面要做窗体和网页交互的功能。网上找了一下资料&#xff0c;这里做一个简单的扩充和整理&#xff0c;部分内容可能是摘自其他博客&#xff0c;这里会注明出处和原文地址供大家和自己日后查阅。 二.基础知识&#xff08;Foun…

Fleck说明文档翻译

CSDN ZslLoveMiwa的博客 Fleck Fleck是一个在C#中的WebSocket服务器端的一个实现.。分支自Nugget项目, Fleck不需要任何的继承、容器亦或是需要增加的引用就可以使用。 实例 下面是一个把客户端网页发来的消息回发到客户端的服务器程序。 var server new WebSocketServer(&q…

fleck 客户端_C#中使用Fleck实现WebSocket通信简例

Fleck是一个开源的使用C#封装的WebSocket服务端工具库。 一、服务端搭建 这里选择在.net core 2.1框架下新建了一个控制台程序 在项目里使用NuGet引入Fleck包 NuGet引入Fleck包 其它方式也可以,最终引入项目里都是Fleck.dll Fleck.dll 二、服务端实例 Github上的简单例子: 简…

Fleck WebSocket使用

作为笔记存储. 最近公司有这方面的使用需求。在网上查了一些资料后。得到了想要的结果。以下记录摘抄至网上资料 1.首先,服务端。项目NuGet直接引用Fleck类库。 code: 1 //客户端url以及其对应的Socket对象字典2 IDictionary<string, IWebSocket…

车牌识别技术原理

车牌识别技术原理 车牌识别技术原理都是一样的&#xff0c;具体流程如下&#xff1a;图像捕捉与获取、车牌定位、字符分割字符识别、输出结果。不一样的是在上述环节中采用不同的技术&#xff0c;比如作为核心技术的车牌定位&#xff0c;就可能用到(1)自适应边界搜索法、(2)区…

python实现车牌识别

原始图象 代码 from hyperlpr import HyperLPR_plate_recognition import cv2 from PIL import ImageFont, ImageDraw, Image import numpy as npimage cv2.imread("car.jpg")#读入图片 resultHyperLPR_plate_recognition(image)#识别车牌 plate result[0][0] #…

基于形态学处理的车牌提取,字符分割和车牌识别算法matlab仿真

目录 1.算法仿真效果 2.MATLAB核心程序 3.算法涉及理论知识概要 4.完整MATLAB 1.算法仿真效果 matlab2022a仿真结果如下&#xff1a; 2.MATLAB核心程序 ........................................................................... figure(8);subplot(3,2,2),imshow(d…

车牌识别算法介绍与实践

汽车牌照自动识别整个处理过程分为预处理、边缘提取、车牌定位、字符分割、字符识别五大模块&#xff0c;其中字符识别过程主要由以下3个部分组成&#xff1a; ①正确地分割文字图像区域&#xff1b; ②正确的分离单个文字&#xff1b; ③正确识别单个字符。 用MATLAB软件编…

基于android的车牌识别程序,基于Android平台车牌识别算法,实现手机识别车牌

原标题:基于Android平台车牌识别算法,实现手机识别车牌 近年来,随着移动行业的爆发式发展,手机配置不断提高,基于手机平台的信息采集、图像处理、数据传输等方面的研究也成为了热点,这使得基于手机平台上的车牌识别成为可能。传统的车牌识别系统一般都基于固定的桌面平台…

【车牌识别】基于HOG特征提取和GRNN网络的车牌识别算法matlab仿真

1.软件版本 matlab2017b 2.系统原理 从图的算法流程图可知&#xff0c;基于车牌图像字符特征提取和神经网络的识别算法其首先将训练样本数据进行预处理&#xff0c;得到质量较高的样本数据&#xff0c;然后对这些样本数据进行HOG特征提取&#xff0c;再将特征数据通过神经网络…

【XLPNet车牌识别算法】第二章 检测部分-算法篇1

【XLPNet车牌识别算法】第二章 检测部分-算法篇1 1. XLPNet检测算法前言2. XLPNet检测算法详解2.1 起源2.2 算法细节2.1 算法命名2.2 数据集2.3 预备知识2.4 核心&#xff1a;旋转高斯分布&#xff08;Rotated Gaussian Distribution&#xff09;2.4.1 基于高斯策略的样本分配2…

车牌识别算法 基于yolov5的车牌检测+crnn中文车牌识别 支持12种中文车牌识别

yolov5 车牌识别算法&#xff0c;支持12种中文车牌类型 基于yolov5的车牌检测 车牌矫正以及 基于CRNN的车牌识别 1.单行蓝牌 2.单行黄牌 3.新能源车牌 4.白色警用车牌 5 教练车牌 6 武警车牌 7 双层黄牌 8 双层武警 9 使馆车牌 10 港澳牌车 11 双层农用车牌 12 民航车牌 效果如…

大陆车牌识别算法的背景与技术

我们写过一些关于车牌识别的相关算法发展&#xff0c;随着近两年车牌识别技术的发展、车牌类型的变化以及识别标准的规范化&#xff0c;我们重新梳理了关于大陆车牌识别的技术以及相关背景。文章主要分为三个部分&#xff0c;首先回顾一下目前大陆车牌的种类&#xff0c;第二部…

车牌识别调研总结

来自巴西阿雷格里港大学的学者发表于ECCV2018的论文 http://url.cn/5tnTl9p 《License Plate Detection and Recognition in Unconstrained Scenarios》&#xff0c;给出了一整套完整的车牌识别系统设计&#xff0c;着眼于解决在非限定场景有挑战的车牌识别应用&#xff0c;其性…

超高效车牌识别算法

核心参考文献 陈进 《车牌识别系统关键技术的研究》 文章相关下载资源 算法动态库 https://download.csdn.net/download/hobbitdream/10549960 演示工程 https://download.csdn.net/download/hobbitdream/10549965 算法基本实现原理 整个车牌识别算法包括两个部分&#x…

车牌识别算法实践(一):先验知识

一般来说&#xff0c;一个完整的车牌识别系统的处理流程包括图像输入、车牌提取、字符分割、字符识别和结果输出这五部分。其中车牌提取、字符分割和字符识别是较重要的部分&#xff0c;而车牌提取尤为关键&#xff0c;采用哪种算法或算法组合决定了整个系统的性能。 本人从多年…

基于matlab车牌识别算法

牌照自动识别是一项利用车辆的动态视频或静态图像进行牌照号码、牌照颜色自动识别的模式识别技术。其硬件基础一般包括触发设备、摄像设备、照明设备、图像采集设备、识别车牌号码的处理机等&#xff0c;其软件核心包括车牌定位算法、车牌字符分割算法和光学字符识别算法等。某…

【毕业设计】深度学习 YOLO 实现车牌识别算法

文章目录 0 前言1 课题介绍2 算法简介2.1网络架构 3 数据准备4 模型训练5 实现效果5.1 图片识别效果5.2视频识别效果 6 部分关键代码7 最后 0 前言 &#x1f525; 这两年开始毕业设计和毕业答辩的要求和难度不断提升&#xff0c;传统的毕设题目缺少创新和亮点&#xff0c;往往…