C#WebSocket Client底层实现
github地址:https://github.com/jf-acer/WebSocketClient.git
实现方式和官方客户端库一样:
static void Main(string[] args)
{
ClientWebSocket clientWebSocket = new ClientWebSocket();
clientWebSocket.ConnectAsync(new Uri("ws://**.**.**.**:****"),CancellationToken.None).Wait();//
//发送消息
while (true)
{
ArraySegment<byte> bytesToSend = new ArraySegment<byte>(Encoding.UTF8.GetBytes("ok"));
clientWebSocket.SendAsync(bytesToSend, WebSocketMessageType.Text, true, CancellationToken.None).Wait();
Thread.Sleep(1000);
}
}
源码目录:
前言:双工通信WebSocket客户端一般都是JS的,如果想用后台模拟客户端一般都用第三方库,或者微软的官方库ClientWebSocket,但是ClientWebSocket不支持win7平台。
win7中会报以下错误:
查了下文档发现该类实现在win8以上,win7可以用但是只提供抽象类,没有具体实现,如果需要使用可以自行继承抽象类后实现。
需求:封装一个dll,每次调用dll时启动客户端连接WebSocket服务端,系统支持win7和win10,如果用第三方库时每次调用dll时也要加上第三方的库这就显得有点臃肿了,所以只能把底层实现封装在自己的dlll里面:
找了半天在Stackoverflow上面找到有一篇是Java改过来的:
https://stackoverflow.com/questions/2064641/is-there-a-websocket-client-implemented-for-net
调试时:
发现连握手都不成功还发送啥信息;
尝试自己实现:
WebSocket原理:https://developer.mozilla.org/zh-CN/docs/Web/API/WebSockets_API/Writing_WebSocket_servers
一、握手:
public static void HandShake() { HttpClientHandler httpClientHandler = new HttpClientHandler() { Proxy = null }; HttpClient httpClient = new HttpClient(httpClientHandler); httpClient.DefaultRequestHeaders.Add("Upgrade", "websocket"); httpClient.DefaultRequestHeaders.Add("Connection", "Upgrade"); httpClient.DefaultRequestHeaders.Add("Sec-WebSocket-Key", "dGhlIHNhbXBsZSBub25jZQ=="); httpClient.DefaultRequestHeaders.Add("Host", "ip:port"); //httpClient.DefaultRequestHeaders.Add("Origin", @"Http://ip"); httpClient.DefaultRequestHeaders.Add("Sec-WebSocket-Version", "13"); var reponse = httpClient.GetAsync(@"http://ip:port").GetAwaiter().GetResult(); Console.WriteLine(reponse.Headers.ToString()); //已经握手成功,如果不发送信息 一分四十秒后自动关闭连接 }
注意握手时http请求版本要1.1以上,我使用HttpClient默认就是1.1,如果有需要可以修改版本:
//设置http版本 var request = new HttpRequestMessage(HttpMethod.Get, @"http://ip:port") { Version = new Version(1, 1) }; var reponse = httpClient.SendAsync(request).Result;
握手成功:
二:发送信息:
private static Stream _stream; private static TcpClient _tcpClient; private static ClientSslConfiguration _sslConfig; private static RemoteCertificateValidationCallback _certValidationCallback; private static readonly CancellationToken _cancellationToken; private static CancellationTokenRegistration _registration; internal static RemoteCertificateValidationCallback CertificateValidationCallback { get { return (object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) => true; } set { _certValidationCallback = value; } } public static LocalCertificateSelectionCallback CertificateSelection { get; private set; } public Program(ClientSslConfiguration sslAuthConfiguration) { _sslConfig = sslAuthConfiguration; } static void Main(string[] args) { //握手 HandShake(); _registration = _cancellationToken.Register(async delegate { Console.WriteLine("注册"); }); _tcpClient = new TcpClient("ip", port); _stream = _tcpClient.GetStream(); SslStream sslStream = new SslStream(_stream, leaveInnerStreamOpen: false, _sslConfig?.CertificateValidationCallback, _sslConfig?.CertificateSelection); if (_sslConfig != null) { sslStream.AuthenticateAsClientAsync("ip", _sslConfig.ClientCertificates, _sslConfig.EnabledSslProtocols, _sslConfig.CheckCertificateRevocation).ConfigureAwait(continueOnCapturedContext: false); } else { sslStream.AuthenticateAsClientAsync("ip").ConfigureAwait(continueOnCapturedContext: false); } _stream = sslStream; //_stream = new SslStream(_stream, false, CertificateValidationCallback, CertificateSelection); //开始发送信息 try { while (true) { Send("ok"); Thread.Sleep(1000); } } catch (Exception ex) { Console.WriteLine(ex.Message); } Console.ReadKey(); } public static void Send(string str) { byte[] sendBuffer = Encoding.UTF8.GetBytes(str); _stream.WriteAsync(sendBuffer, 0, sendBuffer.Length, _cancellationToken).ConfigureAwait(continueOnCapturedContext: false); _stream.FlushAsync(_cancellationToken).ConfigureAwait(continueOnCapturedContext: false); }
这里ssl认证时失败,报:
sslStream.CipherStrength引发了类型System.InvalidOperationException的异常
只允许使用已成功验证身份的上下文执行此操作。
折腾了好一会最终放弃了去扣人家封装好库的源码:
第三方有:
SignalR
WebSocket4Net
websocket-sharp.clone
SuperSocket.WebSocket
System.Net.WebSockets.Client.Managed等等
看了大半天发现他们都差不多,最终选了一个代码最少的下手:
System.Net.WebSockets.Client.Managed
扣下来单纯连接和发送信息就有三千多行代码:这里附上底层最主要的两段:
连接:

public async Task ConnectAsyncCore(Uri uri, CancellationToken cancellationToken, ClientWebSocketOptions options) { CancellationTokenRegistration registration = cancellationToken.Register(delegate (object s) { ((WebSocketHandle)s).Abort(); }, this); try { Uri httpUri = new UriBuilder(uri) { Scheme = ((uri.Scheme == "ws") ? "http" : "https") }.Uri; Uri connectUri = httpUri; bool useProxy = false; if (options.Proxy != null && !options.Proxy.IsBypassed(httpUri)) { useProxy = true; connectUri = options.Proxy.GetProxy(httpUri); } Stream stream = new NetworkStream(await ConnectSocketAsync(connectUri.Host, connectUri.Port, cancellationToken).ConfigureAwait(continueOnCapturedContext: false), ownsSocket: true); if (useProxy) { stream = await EstablishTunnelTrhoughWebProxy(stream, httpUri, connectUri, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); } if (httpUri.Scheme == "https") { SslStream sslStream = new SslStream(stream); await sslStream.AuthenticateAsClientAsync(httpUri.Host, options.ClientCertificates, SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12, checkCertificateRevocation: false).ConfigureAwait(continueOnCapturedContext: false); stream = sslStream; } KeyValuePair<string, string> secKeyAndSecWebSocketAccept = CreateSecKeyAndSecWebSocketAccept(); byte[] array = BuildRequestHeader(uri, options, secKeyAndSecWebSocketAccept.Key); await stream.WriteAsync(array, 0, array.Length, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); _webSocket = WebSocketUtil.CreateClientWebSocket(stream, await ParseAndValidateConnectResponseAsync(stream, options, secKeyAndSecWebSocketAccept.Value, cancellationToken).ConfigureAwait(continueOnCapturedContext: false), options.ReceiveBufferSize, options.SendBufferSize, options.KeepAliveInterval, useZeroMaskingKey: false, options.Buffer.GetValueOrDefault()); if (_state == WebSocketState.Aborted) { _webSocket.Abort(); } else if (_state == WebSocketState.Closed) { _webSocket.Dispose(); } } catch (Exception ex) { if (_state < WebSocketState.Closed) { _state = WebSocketState.Closed; } Abort(); if (ex is WebSocketException) { throw; } throw new WebSocketException(/*SR.net_webstatus_ConnectFailure, ex*/); } finally { registration.Dispose(); } }
发送信息:

private async Task SendFrameFallbackAsync(MessageOpcode opcode, bool endOfMessage, ArraySegment<byte> payloadBuffer, CancellationToken cancellationToken) { await _sendFrameAsyncLock.WaitAsync().ConfigureAwait(continueOnCapturedContext: false); try { int count = WriteFrameToSendBuffer(opcode, endOfMessage, payloadBuffer); using (cancellationToken.Register(delegate (object s) { ((ManagedWebSocket)s).Abort(); }, this)) { await _stream.WriteAsync(_sendBuffer, 0, count, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); } } catch (Exception innerException) { throw (_state == WebSocketState.Aborted) ? CreateOperationCanceledException(innerException, cancellationToken) : new WebSocketException(WebSocketError.ConnectionClosedPrematurely, innerException); } finally { _sendFrameAsyncLock.Release(); ReleaseSendBuffer(); } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人