Ai
350 Star 2.8K Fork 844

GVP若汝棋茗/TouchSocket

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
Program.cs 26.13 KB
一键复制 编辑 原始数据 按行查看 历史
若汝棋茗 提交于 2025-11-23 12:16 +08:00 . Squashed commit of the following:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797
//------------------------------------------------------------------------------
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
// CSDN博客:https://blog.csdn.net/qq_40374647
// 哔哩哔哩视频:https://space.bilibili.com/94253567
// Gitee源代码仓库:https://gitee.com/RRQM_Home
// Github源代码仓库:https://github.com/RRQM
// API首页:https://touchsocket.net/
// 交流QQ群:234762506
// 感谢您的下载和使用
//------------------------------------------------------------------------------
using System;
using System.IO;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using TouchSocket.Core;
using TouchSocket.Http;
using TouchSocket.Http.WebSockets;
using TouchSocket.Rpc;
using TouchSocket.Sockets;
using TouchSocket.WebApi;
namespace WebSocketConsoleApp;
internal class Program
{
private static async Task Main(string[] args)
{
var consoleAction = new ConsoleAction();
consoleAction.OnException += ConsoleAction_OnException;
consoleAction.Add("1", "使用/ws直接连接", ConnectWith_ws);
consoleAction.Add("2", "使用/ws和query参数连接", ConnectWith_wsquery);
consoleAction.Add("3", "使用/ws和header参数连接", ConnectWith_wsheader);
consoleAction.Add("4", "使用post方式连接", ConnectWith_Post_ws);
consoleAction.Add("5", "发送字符串", SendText);
consoleAction.Add("6", "发送部分字符串", SendSubstringText);
consoleAction.Add("7", "调用Add", SendAdd);
consoleAction.Add("8", "发送二进制", SendBinary);
consoleAction.Add("9", "发送自定义消息", SendCustomMessage);
consoleAction.Add("10", "发送Ping消息", SendPingMessage);
consoleAction.Add("11", "发送分包消息", SendContMessage);
consoleAction.ShowAll();
var service = await CreateHttpService();
await consoleAction.RunCommandLineAsync();
}
private static async Task SendContMessage()
{
#region WebSocket直接连接服务器
using var webSocket = new WebSocketClient();
await webSocket.SetupAsync(new TouchSocketConfig()
.ConfigureContainer(a =>
{
a.AddConsoleLogger();
})
.SetRemoteIPHost("ws://127.0.0.1:7789/ws"));
await webSocket.ConnectAsync();
#endregion
#region WebSocket发送分包数据
for (int i = 0; i < 10; i++)
{
//发送文本分包消息
//最后一个参数表示是否为最后一个包
await webSocket.SendAsync($"hello{i}", i == 9);
}
for (int i = 0; i < 10; i++)
{
//发送二进制分包消息
//最后一个参数表示是否为最后一个包
await webSocket.SendAsync(new byte[] { 0, 1, 2, 3, 4 }, i == 9);
}
#endregion
#region WebSocket关闭连接
await webSocket.CloseAsync("正常关闭");
//或者使用关闭状态码
await webSocket.CloseAsync( WebSocketCloseStatus.NormalClosure,"正常关闭");
#endregion
}
private static async Task SendPingMessage()
{
using var webSocket = new WebSocketClient();
await webSocket.SetupAsync(new TouchSocketConfig()
.ConfigureContainer(a =>
{
a.AddConsoleLogger();
})
.SetRemoteIPHost("ws://127.0.0.1:7789/ws"));
await webSocket.ConnectAsync();
for (int i = 0; i < 10; i++)
{
#region WebSocket发送Ping或者Pong消息
await webSocket.PingAsync();
//如果收到了Ping消息,则需要回应Pong。
//await webSocket.PongAsync();
#endregion
}
}
private static async Task SendCustomMessage()
{
using var webSocket = new WebSocketClient();
await webSocket.SetupAsync(new TouchSocketConfig()
.ConfigureContainer(a =>
{
a.AddConsoleLogger();
})
.SetRemoteIPHost("ws://127.0.0.1:7789/ws"));
await webSocket.ConnectAsync();
#region WebSocket发送自定义消息
var frame = new WSDataFrame(Encoding.UTF8.GetBytes("Hello"));
frame.Opcode = WSDataType.Text;
frame.FIN = true;
//设置RSV位,一般情况下,RSV位不允许随意设置,除非你非常清楚你在做什么。
frame.RSV1 = true;
frame.RSV2 = true;
frame.RSV3 = true;
await webSocket.SendAsync(frame);
#endregion
}
private static async Task SendBinary()
{
using var webSocket = new WebSocketClient();
await webSocket.SetupAsync(new TouchSocketConfig()
.ConfigureContainer(a =>
{
a.AddConsoleLogger();
})
.SetRemoteIPHost("ws://127.0.0.1:7789/ws"));
await webSocket.ConnectAsync();
#region WebSocket发送二进制
await webSocket.SendAsync(new byte[] { 1, 2, 3, 4, 5 });
#endregion
}
private static async Task SendAdd()
{
#region WebSocket命令行插件客户端调用
var client = new WebSocketClient();
await client.SetupAsync(new TouchSocketConfig()
.ConfigureContainer(a =>
{
a.AddConsoleLogger();
})
.ConfigurePlugins(a =>
{
a.UseReconnection<WebSocketClient>();
a.Add(typeof(IWebSocketReceivedPlugin), async (IHttpSession c, WSDataFrameEventArgs e) =>
{
client.Logger.Info($"收到Add的计算结果:{e.DataFrame.ToText()}");
await e.InvokeNext();
});
})
.SetRemoteIPHost("ws://127.0.0.1:7789/ws"));
await client.ConnectAsync();
await client.SendAsync("Add 10 20");
#endregion
await Task.Delay(1000);
await client.CloseAsync("我想关就关");
//或者使用关闭状态码
//await client.CloseAsync( WebSocketCloseStatus.InternalServerError,"我不想关,但还得关");
}
private static async Task SendSubstringText()
{
var client = new WebSocketClient();
await client.SetupAsync(new TouchSocketConfig()
.ConfigureContainer(a =>
{
a.AddConsoleLogger();
})
.SetRemoteIPHost("ws://127.0.0.1:7789/ws"));
await client.ConnectAsync();
for (var i = 0; i < 10; i++)
{
var msg = Encoding.UTF8.GetBytes("hello");
await client.SendAsync("Hello", i == 9);
}
}
private static async Task SendText()
{
using var webSocket = new WebSocketClient();
await webSocket.SetupAsync(new TouchSocketConfig()
.ConfigureContainer(a =>
{
a.AddConsoleLogger();
})
.SetRemoteIPHost("ws://127.0.0.1:7789/ws"));
await webSocket.ConnectAsync();
#region WebSocket发送文本
await webSocket.SendAsync("hello");
#endregion
}
private static void ConsoleAction_OnException(Exception obj)
{
Console.WriteLine(obj.Message);
}
/// <summary>
/// 通过/ws直接连接
/// </summary>
private static async Task ConnectWith_ws()
{
#region 简单创建WebSocket客户端
using var client = new WebSocketClient();
client.Connected=(c,e)=>
{
Console.WriteLine("Connected");
return EasyTask.CompletedTask;
};
#region WebSocket客户端使用Received委托接收数据
client.Received = (c, e) =>
{
Console.WriteLine(e.DataFrame.ToText());
return EasyTask.CompletedTask;
};
#endregion
client.Closed=(c,e)=>
{
Console.WriteLine("Closed");
return EasyTask.CompletedTask;
};
await client.ConnectAsync("ws://127.0.0.1:7789/ws");
client.Logger.Info("通过ws://127.0.0.1:7789/ws连接成功");
#endregion
await Task.Delay(1000000);
}
/// <summary>
/// 通过/wsquery,传入参数连接
/// </summary>
private static async Task ConnectWith_wsquery()
{
#region WebSocketQuery参数连接
using var client = new WebSocketClient();
await client.SetupAsync(new TouchSocketConfig()
.ConfigureContainer(a =>
{
a.AddConsoleLogger();
})
.SetRemoteIPHost("ws://127.0.0.1:7789/wsquery?token=123456"));
await client.ConnectAsync();
client.Logger.Info("通过ws://127.0.0.1:7789/wsquery?token=123456连接成功");
#endregion
}
/// <summary>
/// 通过/wsheader,传入header连接
/// </summary>
private static async Task ConnectWith_wsheader()
{
#region WebSocket使用特定Header连接
using var client = new WebSocketClient();
await client.SetupAsync(new TouchSocketConfig()
.ConfigureContainer(a =>
{
a.AddConsoleLogger();
})
.ConfigurePlugins(a =>
{
a.AddWebSocketConnectedPlugin(async (IWebSocket webSocket, HttpContextEventArgs e) =>
{
e.Context.Request.Headers.Add("token", "123456");
await e.InvokeNext();
});
})
.SetRemoteIPHost("ws://127.0.0.1:7789/wsheader"));
await client.ConnectAsync();
client.Logger.Info("通过ws://127.0.0.1:7789/wsheader连接成功");
#endregion
}
/// <summary>
/// 使用Post方式连接
/// </summary>
private static async Task ConnectWith_Post_ws()
{
#region WebSocket使用Post方式连接
using var client = new WebSocketClient();
await client.SetupAsync(new TouchSocketConfig()
.ConfigureContainer(a =>
{
a.AddConsoleLogger();
})
.ConfigurePlugins(a =>
{
a.AddWebSocketConnectedPlugin(async (IWebSocket webSocket, HttpContextEventArgs e) =>
{
e.Context.Request.Method = HttpMethod.Post;//将请求方法改为Post
await e.InvokeNext();
});
})
.SetRemoteIPHost("ws://127.0.0.1:7789/postws"));
await client.ConnectAsync();
client.Logger.Info("通过ws://127.0.0.1:7789/postws连接成功");
#endregion
}
private static async Task<HttpService> CreateHttpService2()
{
#region 创建WebSocket服务器 {11-16}
var service = new HttpService();
var config = new TouchSocketConfig();
config.SetListenIPHosts(7789)
.ConfigureContainer(a =>
{
a.AddConsoleLogger();
})
.ConfigurePlugins(a =>
{
//添加WebSocket功能
a.UseWebSocket(options =>
{
options.SetUrl("/ws");//设置Url,当客户端使用ws://127.0.0.1:7789/ws直接可以连接。
options.SetAutoPong(true);//当收到Ping报文时自动回应Pong
});
});
//加载配置
await service.SetupAsync(config);
//启动服务
await service.StartAsync();
service.Logger.Info("WebSocket服务器已启动,地址: ws://127.0.0.1:7789/ws");
#endregion
#region WebSocket服务器遍历所有连接并发送数据
var clients = service.Clients;
foreach (var client in clients)
{
if (client.Protocol == Protocol.WebSocket)//先判断是不是websocket协议
{
if (client.Id == "id")//再按指定id发送,或者直接广播发送
{
var webSocket = client.WebSocket;
await webSocket.SendAsync("hello");
}
}
}
#endregion
return service;
}
private static async Task CreateHttpService3()
{
var config = new TouchSocketConfig();
config.SetListenIPHosts(7789)
.ConfigureContainer(a =>
{
a.AddConsoleLogger();
})
.ConfigurePlugins(a =>
{
//添加WebSocket功能
a.UseWebSocket(options =>
{
options.SetAutoPong(true);//当收到Ping报文时自动回应Pong
#region WebSocket连接验证
//添加自定义验证逻辑后,必须自己验证Url
options.SetVerifyConnection(async (client, context) =>
{
if (!context.Request.IsUpgrade())
{
//如果不包含升级协议的header,就直接返回false。
return false;
}
// 在此处添加验证逻辑,例如:匹配特定的URL
if (context.Request.UrlEquals("/ws"))
{
return true; // 允许连接
}
//如果url不匹配,则可以直接拒绝。
//await context.Response
//.SetStatus(403, "url不正确")
//.AnswerAsync();
return false;
});
#endregion
});
});
#region 使用WebSocket接收插件
config.ConfigurePlugins(a =>
{
//添加WebSocket功能
a.UseWebSocket("/ws");
a.Add<MyWebSocketReceivePlugin>();
});
#endregion
}
private static async Task<HttpService> CreateHttpService()
{
#region WebSocket命令行插件注册使用
var service = new HttpService();
var config = new TouchSocketConfig();
await service.SetupAsync(new TouchSocketConfig()//加载配置
.SetListenIPHosts(7789)
.ConfigureContainer(a =>
{
a.AddConsoleLogger();
a.AddRpcStore(store =>
{
store.RegisterServer<MyServer>();
});
})
.ConfigurePlugins(a =>
{
a.UseWebSocket(options =>
{
options.SetUrl("/ws");//设置url直接可以连接。
options.SetVerifyConnection(VerifyConnection);
options.SetAutoPong(true);//当收到ping报文时自动回应pong
});
//a.Add<MyReadTextWebSocketPlugin>();
a.Add<MyWSCommandLinePlugin>();
//a.Add<MyWebSocketPlugin>();
a.UseWebApi();
}));
await service.StartAsync();
service.Logger.Info("服务器已启动");
service.Logger.Info("直接连接地址=>ws://127.0.0.1:7789/ws");
service.Logger.Info("通过query连接地址=>ws://127.0.0.1:7789/wsquery?token=123456");
service.Logger.Info("通过header连接地址=>ws://127.0.0.1:7789/wsheader");//使用此连接时,需要在header中包含token的项
service.Logger.Info("WebApi支持的连接地址=>ws://127.0.0.1:7789/MyServer/ConnectWS");
service.Logger.Info("WebApi支持的连接地址=>ws://127.0.0.1:7789/MyServer/ws");
return service;
#endregion
}
/// <summary>
/// 验证websocket的连接
/// </summary>
/// <param name="client"></param>
/// <param name="context"></param>
/// <returns></returns>
private static async Task<bool> VerifyConnection(IHttpSessionClient client, HttpContext context)
{
if (!context.Request.IsUpgrade())//如果不包含升级协议的header,就直接返回false。
{
return false;
}
//使用Post连接
if (context.Request.Method == HttpMethod.Post)
{
if (context.Request.UrlEquals("/postws"))
{
return true;
}
}
if (context.Request.UrlEquals("/ws"))//以此连接,则直接可以连接
{
return true;
}
else if (context.Request.UrlEquals("/wsquery"))//以此连接,则需要传入token才可以连接
{
if (context.Request.Query.Get("token") == "123456")
{
return true;
}
else
{
await context.Response
.SetStatus(403, "token不正确")
.AnswerAsync();
}
}
else if (context.Request.UrlEquals("/wsheader"))//以此连接,则需要从header传入token才可以连接
{
if (context.Request.Headers.Get("token") == "123456")
{
return true;
}
else
{
await context.Response
.SetStatus(403, "token不正确")
.AnswerAsync();
}
}
return false;
}
public class MyWebSocketPlugin : PluginBase,
IWebSocketConnectingPlugin,
IWebSocketConnectedPlugin,
IWebSocketReceivedPlugin,
IWebSocketClosingPlugin,
IWebSocketClosedPlugin
{
public MyWebSocketPlugin(ILog logger)
{
this.m_logger = logger;
}
public async Task OnWebSocketConnecting(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 OnWebSocketConnected(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)
{
#region WebSocket处理中继数据
var dataFrame = e.DataFrame;
switch (dataFrame.Opcode)
{
case WSDataType.Close:
{
this.m_logger.Info("远程请求断开");
await client.CloseAsync("断开");
}
return;
case WSDataType.Ping:
this.m_logger.Info("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)
{
this.m_logger.Info($"{dataType}|{data.Span.ToString(Encoding.UTF8)}");
//按文本处理
}
else if (dataType == WSDataType.Binary)
{
//按字节处理
}
else
{
//可能是其他自定义协议
}
}
}
}
catch (Exception ex)
{
this.m_logger.Exception(ex);
messageCombinator.Clear();//当组合发生异常时,应该清空组合器数据
}
}
break;
}
#endregion
await e.InvokeNext();
}
public async Task OnWebSocketClosed(IWebSocket webSocket, ClosedEventArgs e)
{
this.m_logger.Info($"WebSocket已断开,状态:{webSocket.CloseStatus},信息:{e.Message}");
await e.InvokeNext();
}
public async Task OnWebSocketClosing(IWebSocket webSocket, ClosingEventArgs e)
{
this.m_logger.Info($"WebSocket请求断开,状态:{webSocket.CloseStatus},信息:{e.Message}");
await e.InvokeNext();
}
}
#region 创建WebSocket接收插件
class MyWebSocketReceivePlugin : PluginBase, IWebSocketReceivedPlugin
{
public async Task OnWebSocketReceived(IWebSocket webSocket, WSDataFrameEventArgs e)
{
//处理接收的数据
var dataFrame = e.DataFrame;
switch (dataFrame.Opcode)
{
case WSDataType.Cont:
//处理中继包
break;
case WSDataType.Text:
//处理文本包
Console.WriteLine(dataFrame.ToText());
break;
case WSDataType.Binary:
//处理二进制包
var data = dataFrame.PayloadData;
Console.WriteLine($"收到二进制数据,长度:{data.Length}");
break;
case WSDataType.Close:
//处理关闭包
break;
case WSDataType.Ping:
//处理Ping包
break;
case WSDataType.Pong:
//处理Pong包
break;
default:
//处理其他包
break;
}
await e.InvokeNext();
}
}
#endregion
#region WebSocket命令行插件声明
/// <summary>
/// 命令行插件。
/// 声明的方法必须为公开实例方法、以"Command"结尾,且支持json字符串,参数之间空格隔开。
/// </summary>
public class MyWSCommandLinePlugin : WebSocketCommandLinePlugin
{
public MyWSCommandLinePlugin(ILog logger) : base(logger)
{
}
public int AddCommand(int a, int b)
{
return a + b;
}
//当第一个参数,直接或间接实现ITcpSession接口时,会收集到当前请求的客户端,从而可以获取IP等。
public SumClass SumCommand(IHttpSession client, SumClass sumClass)
{
sumClass.Sum = sumClass.A + sumClass.B;
return sumClass;
}
}
#endregion
public class SumClass
{
public int A { get; set; }
public int B { get; set; }
public int Sum { get; set; }
}
public class MyServer : SingletonRpcServer
{
private readonly ILog m_logger;
public MyServer(ILog logger)
{
this.m_logger = logger;
}
[Router("/[api]/ws")]
[Router("/[api]/[action]")]
[WebApi(Method = HttpMethodType.Get)]
public async Task ConnectWS(IWebApiCallContext callContext)
{
if (callContext.Caller is HttpSessionClient socketClient)
{
var result = await socketClient.SwitchProtocolToWebSocketAsync(callContext.HttpContext);
if (!result.IsSuccess)
{
this.m_logger.Error(result.Message);
return;
}
this.m_logger.Info("WS通过WebApi连接");
}
}
}
#region 自定义转换WebSocket
class CustomWebSocketPlugin : PluginBase, IHttpPlugin
{
public async Task OnHttpRequest(IHttpSessionClient client, HttpContextEventArgs e)
{
if (e.Context.Request.IsUpgrade())
{
if (e.Context.Request.UrlEquals("/customws"))
{
var result = await client.SwitchProtocolToWebSocketAsync(e.Context);
if (!result.IsSuccess)
{
//切换协议失败
return;
}
//切换协议成功,下面可以使用wsClient进行收发数据了。
var webSocket = client.WebSocket;
}
}
await e.InvokeNext();
}
}
#endregion
#region 从继承创建WebSocket客户端
class MyWebSocketClient:WebSocketClient
{
protected override Task OnWebSocketReceived(WSDataFrameEventArgs e)
{
return base.OnWebSocketReceived(e);
}
}
#endregion
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
C#
1
https://gitee.com/RRQM_Home/TouchSocket.git
git@gitee.com:RRQM_Home/TouchSocket.git
RRQM_Home
TouchSocket
TouchSocket
master

搜索帮助