打开 JS 文件,查找:
o+=String.fromCharCode(t.charCodeAt(r)^i[(i[e]+i[s])%256]);
水印文字是从这个变量 o 输出来的,可以在这句的后面,“return o”的前面插入“console.log(o);”看到效果。
因为它是一行一行返回的,所以将
return o
改为下面的三目即可:
return o=='GoJS 3.0 evaluation'||o=='(c) 1998-2024 Northwoods Software'||o=='Not for distribution or production use'||o=='gojs.net'?'':o
修改方法来源于网络,仅供学习参考,请勿用于生产环境,请支持正版!
.NET SDK 的版本 8.0.303 至少需要 MSBuild 的 17.8.3 版本。当前可用的 MSBuild 版本为 16.11.2.50704。请将在 global.json 中指定的 .NET SDK 更改为需要当前可用的 MSBuild 版本的旧版本。
解决方法:
运行 Visual Studio Installer,
选择相应 VS 版本进行修改,
切到“单个组件”,
勾选“.NET SDK (out of support)”,
点击修改并继续。
POST
$.post('AjaxCases', {
following: true,
success: null,
page: 1,
}, function (response) {
console.log(response.data)
}, 'json').fail(function (xhr, status, error) {
console.log(xhr.responseText);
});
[HttpPost]
public IActionResult AjaxCases([FromForm] AjaxCasesRequestModel req)
{
bool? following = req.following;
bool? success = req.success;
int page = req.page;
}
提示:只能传输对象(Object),不能传输数组(Array)。
以以下版本为例:
PHP 7.3
sqlsrv 5.6.0
步骤:
安装 PHP 7.3
加入微软的源
curl https://packages.microsoft.com/config/rhel/7/prod.repo > /etc/yum.repos.d/mssqlrelease.repo
安装微软 ODBC 驱动、命令行工具及开发包
yum install msodbcsql mssql-tools unixODBC-devel
下载 sqlsrc 扩展
wget http://pecl.php.net/get/sqlsrv-5.6.0.tgz
解压(注意最好是 wget 直接下载解压的,如果是 SFTP 上传的,用户是 root 编译会出错)
tar -zxvf sqlsrv-5.6.0.tgz
进入
cd sqlsrv-5.6.0
/www/server/php/73/bin/phpize
./configure --with-php-config=/www/server/php/73/bin/php-config
make && make install
添加扩展
echo "extension = sqlsrv.so" >> /www/server/php/73/etc/php.ini
重新加载配置
/etc/init.d/php-fpm-73 reload
SignalR 是一个开源的实时通信库,用于构建实时 Web 应用程序。它提供了一个简单的 API,可以在客户端和服务器之间建立持久连接,以便实时地推送数据。
与传统的 WebSocket 相比,SignalR 提供了更高级的功能和更简单的开发体验。下面是一些主要区别:
支持多种传输方式:SignalR 可以使用多种传输方式,包括 WebSocket、Server-Sent Events(SSE)、长轮询和 Forever Frame。这使得 SignalR 在不同的环境中都能提供实时通信的能力,即使某些浏览器不支持 WebSocket,也可以使用其他传输方式。
自动处理连接管理:SignalR 管理连接的生命周期,包括连接的建立、断开和重新连接。它会自动处理连接的失败和重新连接的逻辑,简化了开发人员的工作。
服务器端推送:SignalR 允许服务器端主动推送消息给客户端,而不需要客户端发起请求。这使得实时通信变得更加高效和实时,适用于聊天应用、实时监控等场景。
跨平台支持:SignalR 可以在多个平台上使用,包括 .NET、Java、JavaScript 等。这使得开发人员可以使用自己熟悉的语言和框架来构建实时应用程序。
微软官方提供了针对 ASP.NET Core Web 应用(Razor 页面)的详细教程,这里给出 MVC 版本入门教程。
最终将创建一个正常运行的聊天应用:
创建 Web 应用项目
添加 SignalR 客户端库
在“解决方案资源管理器”>中,右键单击项目,然后选择“添加”“客户端库”。
在“添加客户端库”对话框中:
“提供程序”选择“unpkg”
“库”,请输入 @microsoft/signalr@latest。
选择“选择特定文件”,展开“dist/browser”文件夹,然后选择 signalr.js 和 signalr.min.js。
点击“安装” 。
创建 SignalR Hubs 类
using Microsoft.AspNetCore.SignalR;
/// <summary>
/// Hub 类管理连接、组和消息
/// </summary>
public class ChatHub : Hub
{
/// <summary>
/// 可通过已连接客户端调用 SendMessage,以向所有客户端发送消息
/// </summary>
public async Task SendMessage(string user, string message)
{
// Clients.All 向所有的客户端发送消息
// ReceiveMessage 是客户端监听的方法
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
}
其父类 Hub 可管理连接、组和消息。这里演示的是向所有客户端发送消息。
配置 SignalR
打开 Program.cs,添加注入:
builder.Services.AddSignalR();
添加路由:
app.MapHub<ChatHub>("/chatHub");
添加 SignalR 客户端代码
视图页面:
<div class="container">
<div class="row p-1">
<div class="col-1">用户</div>
<div class="col-5"><input type="text" id="userInput" /></div>
</div>
<div class="row p-1">
<div class="col-1">消息</div>
<div class="col-5"><input type="text" class="w-100" id="messageInput" /></div>
</div>
<div class="row p-1">
<div class="col-6 text-end">
<input type="button" id="sendButton" value="发送消息" />
</div>
</div>
<div class="row p-1">
<div class="col-6">
<hr />
</div>
</div>
<div class="row p-1">
<div class="col-6">
<ul id="messagesList"></ul>
</div>
</div>
</div>
<script src="~/lib/microsoft/signalr/dist/browser/signalr.min.js"></script>
<script src="~/js/chat.js"></script>
chat.js 文件:
"use strict";
var connection = new signalR.HubConnectionBuilder().withUrl("/chatHub").build();
// 在连接建立之前禁用发送按钮
document.getElementById("sendButton").disabled = true;
connection.on("ReceiveMessage", function (user, message) {
var li = document.createElement("li");
document.getElementById("messagesList").appendChild(li);
// 修改此处时应注意脚本注入问题
li.textContent = `${user} says ${message}`;
});
connection.start().then(function () {
document.getElementById("sendButton").disabled = false;
}).catch(function (err) {
return console.error(err.toString());
});
document.getElementById("sendButton").addEventListener("click", function (event) {
var user = document.getElementById("userInput").value;
var message = document.getElementById("messageInput").value;
connection.invoke("SendMessage", user, message).catch(function (err) {
return console.error(err.toString());
});
event.preventDefault();
});
完成。在线示例:https://xoyozo.net/Demo/SignalRDemo
推荐使用 SignalR 来平替 WebSocket,参考教程。
【服务端(.NET)】
一、创建 WebSocketHandler 类,用于处理客户端发送过来的消息,并分发到其它客户端
public class WebSocketHandler
{
private static List<WebSocket> connectedClients = [];
public static async Task Handle(WebSocket webSocket)
{
connectedClients.Add(webSocket);
byte[] buffer = new byte[1024];
WebSocketReceiveResult result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
while (!result.CloseStatus.HasValue)
{
string message = Encoding.UTF8.GetString(buffer, 0, result.Count);
Console.WriteLine($"Received message: {message}");
// 处理接收到的消息,例如广播给其他客户端
await BroadcastMessage(message, webSocket);
result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
}
connectedClients.Remove(webSocket);
await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
}
private static async Task BroadcastMessage(string message, WebSocket sender)
{
foreach (var client in connectedClients)
{
if (client != sender && client.State == WebSocketState.Open)
{
await client.SendAsync(Encoding.UTF8.GetBytes(message), WebSocketMessageType.Text, true, CancellationToken.None);
}
}
}
}
二、在 Program.cs 或 Startup.cs 中插入:
// 添加 WebSocket 路由
app.UseWebSockets();
app.Use(async (context, next) =>
{
if (context.Request.Path == "/chat")
{
if (context.WebSockets.IsWebSocketRequest)
{
WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync();
await WebSocketHandler.Handle(webSocket);
}
else
{
context.Response.StatusCode = 400;
}
}
else
{
await next();
}
});
这里的路由路径可以更改,此处将会创建一个 WebSocket 连接,并将其传递给 WebSocketHandler.Handle 方法进行处理。
也可以用控制器来接收 WebSocket 消息。
【客户端(JS)】
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0" />
<title>WebSocket 示例(JS + ASP.NET)</title>
<style>
#list { width: 100%; max-width: 500px; }
.tip { color: red; }
</style>
</head>
<body>
<h2>WebSocket 示例(JS + ASP.NET)</h2>
<textarea id="list" readonly rows="20"></textarea>
<div>
<input type="text" id="msg" />
<button onclick="fn_send()">发送</button>
<button onclick="fn_disconnect()">断开</button>
</div>
<p id="tip_required">请输入要发送的内容</p>
<p id="tip_closed">WebSocket 连接未建立</p>
<script>
var wsUrl = "wss://" + window.location.hostname + ":" + window.location.port + "/chat";
var webSocket;
var domList = document.getElementById('list');
// 初始化 WebSocket 并连接
function fn_ConnectWebSocket() {
webSocket = new WebSocket(wsUrl);
// 向服务端发送连接请求
webSocket.onopen = function (event) {
var content = domList.value;
content += "[ WebSocket 连接已建立 ]" + '\r\n';
domList.innerHTML = content;
document.getElementById('tip_closed').style.display = 'none';
webSocket.send(JSON.stringify({
msg: 'Hello'
}));
};
// 接收服务端发送的消息
webSocket.onmessage = function (event) {
if (event.data) {
var content = domList.value;
content += event.data + '\r\n';
domList.innerHTML = content;
domList.scrollTop = domList.scrollHeight;
}
};
// 各种情况导致的连接关闭或失败
webSocket.onclose = function (event) {
var content = domList.value;
content += "[ WebSocket 连接已关闭,3 秒后自动重连 ]" + '\r\n';
domList.innerHTML = content;
document.getElementById('tip_closed').style.display = 'block';
setTimeout(function () {
var content = domList.value;
content += "[ 正在重连... ]" + '\r\n';
domList.innerHTML = content;
fn_ConnectWebSocket();
}, 3000); // 3 秒后重连
};
}
// 检查连接状态
function fn_IsWebSocketConnected() {
if (webSocket.readyState === WebSocket.OPEN) {
document.getElementById('tip_closed').style.display = 'none';
return true;
} else {
document.getElementById('tip_closed').style.display = 'block';
return false;
}
}
// 发送内容
function fn_send() {
if (fn_IsWebSocketConnected()) {
var message = document.getElementById('msg').value;
document.getElementById('tip_required').style.display = message ? 'none' : 'block';
if (message) {
webSocket.send(JSON.stringify({
msg: message
}));
}
}
}
// 断开连接
function fn_disconnect() {
if (fn_IsWebSocketConnected()) {
// 部分浏览器调用 close() 方法关闭 WebSocket 时不支持传参
// webSocket.close(001, "Reason");
webSocket.close();
}
}
// 执行连接
fn_ConnectWebSocket();
</script>
</body>
</html>
【在线示例】
https://xoyozo.net/Demo/JsNetWebSocket
【其它】
-
服务端以 IIS 作为 Web 服务器,那么需要安装 WebSocket 协议
-
一个连接对应一个客户端(即 JS 中的 WebSocket 对象),注意与会话 Session 的区别
-
在实际项目中使用应考虑兼容性问题
-
程序设计应避免 XSS、CSRF 等安全隐患
-
参考文档:https://learn.microsoft.com/zh-cn/aspnet/core/fundamentals/websockets
未能找到路径“\roslyn\csc.exe”的一部分。
解决方法:
在 nuget 中直接卸载 Microsoft.CodeDom.Providers.DotNetCompilerPlatform
如果 web.config 报错,删除与 Microsoft.CodeDom.Providers.DotNetCompilerPlatform 相关的项,如:
<system.codedom>
<compilers>
<compiler language="c#;cs;csharp" extension=".cs" warningLevel="4" compilerOptions="/langversion:default /nowarn:1659;1699;1701;612;618" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
<compiler language="vb;vbs;visualbasic;vbscript" extension=".vb" warningLevel="4" compilerOptions="/langversion:default /nowarn:41008,40000,40008 /define:_MYTYPE=\"Web\" /optionInfer+" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.VBCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</compilers>
</system.codedom>
然后遇到错误:
功能 空传播运算符 在 C# 5 中不可用。请使用 6 或更高的语言版本
所以还是少不了 Microsoft.CodeDom.Providers.DotNetCompilerPlatform
又把这货装上了,竟然不报错了。
这期间我更改过在 Release 模式下调试,将 .NET Framework 4.8 升级到 .NET Framework 4.8.1 等,然后又退回到原来的状态和版本,竟然都正常了。
就这样吧!
本文由 ChatGTP 3.5 提供
在 .NET C# 桌面应用程序中,你可以使用委托和事件来实现在窗体页面处理事务过程中实时更新界面。下面是一个示例,展示了在不同情况下如何更新主界面:
首先,在窗体页面中定义一个委托和一个事件,用于更新界面:
public delegate void UpdateUIHandler(string msg);
public event UpdateUIHandler UpdateUIEvent;
然后,在窗体的构造函数中订阅事件,并在事件处理程序中更新界面:
public Form1()
{
InitializeComponent();
UpdateUIEvent += UpdateUI;
}
private void UpdateUI(string msg)
{
if (InvokeRequired)
{
Invoke(new UpdateUIHandler(UpdateUI), msg);
}
else
{
// 在这里更新界面
label1.Text = msg;
}
}
现在,你可以在窗体页面的任何地方调用 UpdateUIEvent
来触发更新界面的事件。例如,在按钮点击事件中:
private void button1_Click(object sender, EventArgs e)
{
// 处理事务过程中实时更新界面
string msg = "处理事务中...";
UpdateUIEvent?.Invoke(msg);
// 异步调用一个类方法中的方法,并在方法内部实时更新主界面
Task.Run(() =>
{
MyClass myClass = new MyClass();
myClass.LongRunningMethod(UpdateUIEvent);
});
}
在上面的示例中,MyClass
是一个自定义的类,其中的 LongRunningMethod
是一个耗时的方法。在这个方法内部,你可以通过传递 UpdateUIEvent
来实时更新主界面:
public class MyClass
{
public void LongRunningMethod(UpdateUIHandler updateUI)
{
// 处理事务过程中实时更新界面
string msg = "处理事务中...";
updateUI?.Invoke(msg);
// 其他耗时操作...
}
}
通过以上示例,你可以在窗体页面处理事务过程中实时更新界面,并且在异步调用的方法内部也能实时更新主界面。记得在更新界面时使用 Invoke
方法,以确保在主线程上更新界面,避免跨线程访问 UI 的问题。
客户端:Failed to connect to host.
服务端:无任何日志
原因:可能是域名、IP 或端口有误,或端口未开放。
客户端/服务端:530 Login incorrect.
原因:用户名或密码错误。
客户端/服务端:503 Use AUTH first.
原因:服务端开启了 FTPS 协议(FTP over TLS),且禁止了 FTP 协议(plain FTP)。
解决方法:不建议服务端恢复不安全的 FTP 协议,客户端应配置 client.Config 的 EncryptionMode、DataConnectionEncryption、SslProtocols、ValidateCertificate 等参数。
客户端/服务端:530 This server does not allow plain FTP. You have to use FTP over TLS.
原因:服务端启用了 FTPS。
解决方法:给 client.Config.EncryptionMode 正确的赋值(FtpEncryptionMode.Explicit 或 FtpEncryptionMode.Auto)。
服务端:[Error] TLS session of data connection not resumed.
原因:证书版本有误。
解决方法:给 client.Config.SslProtocols 赋值正确的 TLS 版本。
服务端:521 PROT P required
原因:服务端开启了“Require TLS session resumption on data connection when using PROT P”
解决方法:client.Config.DataConnectionEncryption = true;
客户端/服务端:450 TLS session of data connection has not resumed or the session does not match the control connection
原因:TLS 与控制连接不匹配,即服务端开启了“Require TLS session resumption on data connection when using PROT P”
解决方法:我暂时还不知道原因,临时关闭了“Require TLS session resumption on data connection when using PROT P”(不建议)。该问题出现在 FileZilla Server 0.x 版本,在 1.x 版本中没有该配置选项。
有一个项目,在 VS2022 中正常打开,在 VS2019 中无法加载项目,项目目标框架是 .NET Standard 2.0。报错内容:
error: 无法打开项目文件。 .NET SDK 的版本 7.0.306 至少需要 MSBuild 的 17.4.0 版本。当前可用的 MSBuild 版本为 16.11.2.50704。请将在 global.json 中指定的 .NET SDK 更改为需要当前可用的 MSBuild 版本的旧版本。
解决方法:
运行 Visual Studio Installer,选择 Visual Studio 2019 进行修改,切换到“单个组件”,勾选“.NET SDK (out of support)”,会联动勾选“.NET 5.0 Runtime (Out of support)”与“.NET Core 3.1 Runtime (Out of support)”。