C# SuperSocket简单实现

概要

SuperSocket实现服务端
SuperSocket实现客户端
Socket实现js客户端
互相通讯代码实现

技术实现

项目结构
在这里插入图片描述
Project.Application:项目实现,Socket实现
Project.Pack:项目包
SocketTest:项目服务端测试
WindowsSocket:项目客户端测试

Project.Application中代码实现

在这里插入图片描述
SocketDataFilter类

using SuperSocket.ProtoBase;
using System.Buffers;

namespace Project.Application.SupSocket
{
    public class SocketDataFilter : IPipelineFilter<SocketDataPackage>
    {
        public IPackageDecoder<SocketDataPackage> Decoder { get; set; }
        public object Context { get; set; }
        public IPipelineFilter<SocketDataPackage> NextFilter => this;
        public SocketDataPackage Filter(ref SequenceReader<byte> reader)
        {
            SocketDataPackage txtPackage = new SocketDataPackage { Datas = reader.Sequence.ToArray() };
            while (reader.TryRead(out _)) ;
            return txtPackage;
        }
        public void Reset() { }
    }
}

SocketDataPackage类


namespace Project.Application.SupSocket
{
    /// <summary>
    /// 自定义数据包
    /// </summary>
    public class SocketDataPackage
    {
        /// <summary>
        /// 功能码
        /// </summary>
        public int FCode { get; set; } = -1;
        /// <summary>
        /// 数据
        /// </summary>
        public byte[] Datas { get; set; }
    }
}

SocketServerHostedService类

using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System.Net;
using System.Text.RegularExpressions;
using System.Text;
using System.Security.Cryptography;
using SuperSocket;
using SuperSocket.Channel;

namespace Project.Application.SupSocket
{
    /// <summary>
    /// Socket承载服务
    /// </summary>
    public class SocketServerHostedService : IHostedService
    {
        /// <summary>
        /// 使用自定义的数据包和过滤器创建的服务宿主
        /// </summary>
        private static ISuperSocketHostBuilder<SocketDataPackage> _host = SupSocketServer.SocketHost;
        private ILogger<SocketServerHostedService> _logger;
        private IHost _serverhost;
        public SocketServerHostedService(
            IHost serverhost,
            ILogger<SocketServerHostedService> logger)
        {
            _serverhost = serverhost;
            _logger = logger;
        }

        public async Task StartAsync(CancellationToken cancellationToken)
        {
            //启动Socket服务
            await SupSocketServer.StartAsync("0.0.0.0", 8552, OnConnectedAsync, OnClosedAsync, OnPackageAsync);
        }

        public async Task StopAsync(CancellationToken cancellationToken)
        {
            //关闭Socket链接
            await SupSocketServer.DisConnects();
            await Task.CompletedTask;
        }

        /// <summary>
        /// 会话的连接事件
        /// </summary>
        /// <param name="session"></param>
        /// <returns></returns>
        private async ValueTask OnConnectedAsync(IAppSession session)
        {
            await Task.Run(async () =>
            {
                var endpoint = (IPEndPoint)session.RemoteEndPoint;
                await SupSocketServer.AddSession(session);
                #region 处理回话连接逻辑
                #endregion

            });
        }

        /// <summary>
        /// 会话的断开事件
        /// </summary>
        /// <param name="session"></param>
        /// <param name="e"></param>
        /// <returns></returns>
        private async ValueTask OnClosedAsync(IAppSession session, CloseEventArgs e)
        {
            var ses = SupSocketServer.ConnectSessions;
            await Task.Run(async () =>
            {
                var endpoint = (IPEndPoint)session.RemoteEndPoint;
                await SupSocketServer.RemoveSession(session);
                #region 处理回话断开后逻辑
                #endregion

            });
        }

        /// <summary>
        /// 数据接收事件
        /// </summary>
        /// <param name="session"></param>
        /// <param name="package"></param>
        /// <returns></returns>
        private async ValueTask OnPackageAsync(IAppSession session, SocketDataPackage package)
        {
            await Task.Run(async () =>
            {
                var endpoint = (IPEndPoint)session.RemoteEndPoint;
                #region 处理回话连接数据接受后逻辑
                #endregion
                //接受的请求(是否包含掩码)
                string message = AnalyticDatass(package.Datas, package.Datas.Length);
                if (message.Contains("GET"))
                {
                    //是连接请求,返回牵手包
                    string returnmessage = GetSecKeyAccetp(package.Datas, package.Datas.Length);
                    //返回发送握手信息
                    await session.SendAsync(PackHandShakeData(returnmessage));
                }
            });
        }


        /// <summary>
        /// 解析客户端数据包,防止乱码
        /// </summary>
        /// <param name="recBytes">服务器接收的数据包</param>
        /// <param name="recByteLength">有效数据长度</param>
        /// <returns></returns>
        private static string AnalyticDatass(byte[] recBytes, int recByteLength)
        {
            if (recByteLength < 2) { return string.Empty; }
            bool fin = (recBytes[0] & 0x80) == 0x80; // 1bit,1表示最后一帧  
            if (!fin)
            {
                return System.Text.Encoding.UTF8.GetString(recBytes);// 超过一帧暂
            }
            bool mask_flag = (recBytes[1] & 0x80) == 0x80; // 是否包含掩码  
            if (!mask_flag)
            {
                return System.Text.Encoding.UTF8.GetString(recBytes);// 不包含掩码
            }
            int payload_len = recBytes[1] & 0x7F; // 数据长度  
            byte[] masks = new byte[4];
            byte[] payload_data;
            if (payload_len == 126)
            {
                Array.Copy(recBytes, 4, masks, 0, 4);
                payload_len = (UInt16)(recBytes[2] << 8 | recBytes[3]);
                payload_data = new byte[payload_len];
                Array.Copy(recBytes, 8, payload_data, 0, payload_len);
            }
            else if (payload_len == 127)
            {
                Array.Copy(recBytes, 10, masks, 0, 4);
                byte[] uInt64Bytes = new byte[8];
                for (int i = 0; i < 8; i++)
                {
                    uInt64Bytes[i] = recBytes[9 - i];
                }
                UInt64 len = BitConverter.ToUInt64(uInt64Bytes, 0);
                payload_data = new byte[len];
                for (UInt64 i = 0; i < len; i++)
                {
                    payload_data[i] = recBytes[i + 14];
                }
            }
            else
            {
                Array.Copy(recBytes, 2, masks, 0, 4);
                payload_data = new byte[payload_len];
                Array.Copy(recBytes, 6, payload_data, 0, payload_len);
            }
            for (var i = 0; i < payload_len; i++)
            {
                payload_data[i] = (byte)(payload_data[i] ^ masks[i % 4]);
            }
            return Encoding.UTF8.GetString(payload_data);
        }


        /// <summary>
        /// 打包握手信息
        /// </summary>
        /// <param name="secKeyAccept">Sec-WebSocket-Accept</param>
        /// <returns>数据包</returns>
        private static byte[] PackHandShakeData(string secKeyAccept)
        {
            var responseBuilder = new StringBuilder();
            responseBuilder.Append("HTTP/1.1 101 Switching Protocols" + Environment.NewLine);
            responseBuilder.Append("Upgrade: websocket" + Environment.NewLine);
            responseBuilder.Append("Connection: Upgrade" + Environment.NewLine);
            responseBuilder.Append("Sec-WebSocket-Accept: " + secKeyAccept + Environment.NewLine + Environment.NewLine);
            //如果把上一行换成下面两行,才是thewebsocketprotocol-17协议,但居然握手不成功,目前仍没弄明白!
            //responseBuilder.Append("Sec-WebSocket-Accept: " + secKeyAccept + Environment.NewLine);
            //responseBuilder.Append("Sec-WebSocket-Protocol: chat" + Environment.NewLine);
            return Encoding.UTF8.GetBytes(responseBuilder.ToString());
        }

        /// <summary>
        /// 生成Sec-WebSocket-Accept
        /// </summary>
        /// <param name="handShakeBytes"></param>
        /// <param name="bytesLength"></param>
        /// <returns>Sec-WebSocket-Accept</returns>
        private static string GetSecKeyAccetp(byte[] handShakeBytes, int bytesLength)
        {
            //客户端握手信息
            string handShakeText = Encoding.UTF8.GetString(handShakeBytes, 0, bytesLength);
            string key = string.Empty;
            Regex r = new Regex(@"Sec\-WebSocket\-Key:(.*?)\r\n");
            Match m = r.Match(handShakeText);
            if (m.Groups.Count != 0)
            {
                key = Regex.Replace(m.Value, @"Sec\-WebSocket\-Key:(.*?)\r\n", "$1").Trim();
            }
            byte[] encryptionString = SHA1.Create().ComputeHash(Encoding.ASCII.GetBytes(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"));
            return Convert.ToBase64String(encryptionString);
        }
    }
}

SupSocketServer类


using SuperSocket.Channel;
using SuperSocket;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;

namespace Project.Application.SupSocket
{
    public class SupSocketServer
    {
        /// <summary>
        /// 使用自定义的数据包和过滤器创建的服务宿主
        /// </summary>
        public static readonly ISuperSocketHostBuilder<SocketDataPackage> SocketHost;

        /// <summary>
        /// 会话集合
        /// </summary>
        public static readonly ConcurrentDictionary<string, IAppSession> ConnectSessions;

        static SupSocketServer()
        {
            if (SocketHost == null)
                SocketHost = SuperSocketHostBuilder.Create<SocketDataPackage, SocketDataFilter>();
            if (ConnectSessions == null)
                ConnectSessions = new ConcurrentDictionary<string, IAppSession>();
        }

        /// <summary>
        /// 启动
        /// </summary>
        /// <param name="onConnected"></param>
        /// <param name="onClosed"></param>
        /// <param name="packageHandler"></param>
        /// <param name="errorHandler"></param>
        /// <returns></returns>
        public static async Task<bool> StartAsync(string ip, int port, Func<IAppSession, ValueTask> onConnected,
                Func<IAppSession, CloseEventArgs, ValueTask> onClosed,
                Func<IAppSession, SocketDataPackage, ValueTask> packageHandler,
                Func<IAppSession, PackageHandlingException<SocketDataPackage>, ValueTask<bool>> errorHandler = null)
        {
            //配置参数
            SocketHost.ConfigureSuperSocket(options =>
            {
                options.Name = "HosSocketServer";
                options.MaxPackageLength = 1024 * 1024;
                options.ReceiveBufferSize = 4 * 1024;
                options.SendBufferSize = 4 * 1024;
                options.ReceiveTimeout = 0;
                options.SendTimeout = 500 * 1000;
                options.Listeners = new[] { new ListenOptions { Ip = ip, Port = port, BackLog = 1024 } }.ToList();
            });

            //使用会话的连接和断开事件(建立和断开Socket连接时触发)
            SocketHost.UseSessionHandler(onConnected, onClosed);
            //使用会话的数据接收时间(接收到数据包时触发)
            SocketHost.UsePackageHandler(packageHandler);
            //复用已经闲置或者失去连接的资源
            //_host.UseClearIdleSession();
            //启动Socket
            await SocketHost.StartAsync();
            await Task.CompletedTask;
            return true;
        }

        /// <summary>
        /// 保存Session
        /// </summary>
        /// <param name="session"></param>
        /// <returns></returns>
        public static async Task<bool> AddSession(IAppSession session)
        {
            while (!ConnectSessions.ContainsKey(session.SessionID))
            {
                //添加不成功则重复添加
                if (!ConnectSessions.TryAdd(session.SessionID, session))
                    Thread.Sleep(1);
            }
            return true;
        }

        /// <summary>
        /// 移除Session
        /// </summary>
        /// <param name="session"></param>
        /// <returns></returns>
        public static async Task<bool> RemoveSession(IAppSession session)
        {
            return await Task.Run(() =>
            {
                while (ConnectSessions.ContainsKey(session.SessionID))
                {
                    //移除不成功则重复移除
                    if (!ConnectSessions.TryRemove(session.SessionID, out _))
                        Thread.Sleep(1);
                }
                return true;
            });
        }

        /// <summary>
        /// 发送
        /// </summary>
        /// <param name="body"></param>
        /// <returns></returns>
        public static async Task<bool> SendAsync(string ip, string body)
        {
            if (await IsConnected(ip))
            {
                var session = await GetSession(ip);
                if (session != null)
                {
                    var data = EncSocketStr(body);
                    var byteBody = PackData(data);
                    await session.SendAsync(byteBody);
                }
            }
            return true;
        }

      
        /// <summary>
        /// 打包服务器数据,防止乱码
        /// </summary>
        /// <param name="message">数据</param>
        /// <returns>数据包</returns>
        private static byte[] PackData(string message)
        {
            byte[] contentBytes = null;
            byte[] temp = Encoding.UTF8.GetBytes(message);
            if (temp.Length < 126)
            {
                contentBytes = new byte[temp.Length + 2];
                contentBytes[0] = 0x81;
                contentBytes[1] = (byte)temp.Length;
                Array.Copy(temp, 0, contentBytes, 2, temp.Length);
            }
            else if (temp.Length < 0xFFFF)
            {
                contentBytes = new byte[temp.Length + 4];
                contentBytes[0] = 0x81;
                contentBytes[1] = 126;
                contentBytes[2] = (byte)(temp.Length & 0xFF);
                contentBytes[3] = (byte)(temp.Length >> 8 & 0xFF);
                Array.Copy(temp, 0, contentBytes, 4, temp.Length);
            }
            else
            {
                // 暂不处理超长内容  
            }
            return contentBytes;
        }
        /// <summary>
        /// 停止服务,关闭所有链接
        /// </summary>
        /// <returns></returns>
        public static async Task<bool> DisConnects()
        {
            return await Task.Run(() =>
            {
                bool isSuccess = false;
                foreach (var sessions in ConnectSessions)
                {
                    sessions.Value.CloseAsync(CloseReason.ServerShutdown);
                }
                ConnectSessions.Clear();
                return isSuccess;
            });
        }

        /// <summary>
        /// IP是否链接
        /// </summary>
        /// <param name="ip"></param>
        /// <returns></returns>
        private static async Task<bool> IsConnected(string ip)
        {
            return await Task.Run(() =>
            {
                foreach (var keyValuePair in ConnectSessions)
                {
                    var endpoint = (IPEndPoint)keyValuePair.Value.RemoteEndPoint;
                    if (endpoint.Address.ToString() == ip)
                        return true;
                }
                return false;
            });
        }

        /// <summary>
        /// 获取链接对象
        /// </summary>
        /// <param name="deviceIp"></param>
        /// <returns></returns>
        private static async Task<IAppSession?> GetSession(string ip)
        {
            return await Task.Run(() =>
            {
                foreach (var keyValuePair in ConnectSessions)
                {
                    var endpoint = (IPEndPoint)keyValuePair.Value.RemoteEndPoint;
                    if (endpoint.Address.ToString() == ip)
                        return keyValuePair.Value;
                }
                return null;
            });
        }
        /// <summary>
        /// 发送消息加标识
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static string EncSocketStr(string str)
        {
            string returnstr = "";
            int lenght = str.Length;
            returnstr = lenght.ToString().PadLeft(5, '0');
            returnstr = $"{returnstr}#{str}#";
            return returnstr;
        }

        /// <summary>
        /// 接收消息解除标识
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static string DecSocketStr(string str)
        {
            string returnstr = "";
            string[] sArray = str.Split('#');
            int lenght = Convert.ToInt32(sArray[0]);
            if (lenght == sArray[1].Length)
            {
                returnstr = sArray[1];
            }
            return returnstr;
        }
    }
}


Project.Pack中代码实现

引用公共包
在这里插入图片描述

SocketTest中代码实现

在这里插入图片描述
AutoFacExtensions类

using Autofac;
using Autofac.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyModel;
using System.Reflection;


namespace SocketTest.Extentions
{
    /// <summary>
    /// AutoFacExtensions
    /// </summary>
    public static class AutoFacExtensions
    {
        /// <summary>
        /// 初始化
        /// </summary>
        public static void AddAutoFac(this WebApplicationBuilder builder)
        {
            //Autofac 
            var assemblies = DependencyContext.Default.RuntimeLibraries
                            .Where(x => x.Name.StartsWith("Project.Application"))
                            .Select(o => Assembly.Load(new AssemblyName(o.Name))).ToArray();

            builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());  
        }
    }
}

Program 中添加

builder.Services.AddHostedService<SocketServerHostedService>();

在这里插入图片描述

WindowsSocket客户端代码实现

在这里插入图片描述
引用NuGet包
在这里插入图片描述
SocketExtension 类

using SuperSocket.ClientEngine;
using System;
using System.Net;
using System.Text;


namespace WindowsSocket
{
    public class SocketExtension
    {
        static SocketExtension()
        {
        }

        /// <summary>
        /// Socket是否链接成功
        /// </summary>
        public static bool SocketStatus
        {
            get
            {
                return TcpSession != null && TcpSession.IsConnected;
            }
        }

        /// <summary>
        /// 启动屏Socket
        /// </summary>
        private static AsyncTcpSession TcpSession;

        public static void InitSocket()
        {
            var socketIp = "192.168.31.71"; //服务器ip
            var socketPort = "8552";  //服务器端口

            if (TcpSession != null)
            {
                TcpSession.Close();
                TcpSession = null;
            }
            try
            {
                IPAddress ip = IPAddress.Parse(socketIp.Trim());
                IPEndPoint remotePoint = new IPEndPoint(ip, int.Parse(socketPort));
                TcpSession = new AsyncTcpSession();
                TcpSession.Connect(remotePoint);
                TcpSession.Connected += TcpSession_Connected;
                TcpSession.DataReceived += TcpSession_DataReceived;
                TcpSession.Closed += TcpSession_Closed;
            }
            catch (System.StackOverflowException exception)
            {
                //Logger.Error("尝试连接服务,稍后将重新尝试链接。", exception);
            }
            catch (Exception exception)
            {
                //Logger.Error("尝试连接屏服务,稍后将重新尝试链接。", exception);
            }
        }

        private static void TcpSession_Connected(object sender, EventArgs e)
        {
            //Logger.Info("连接Socket服务成功");
            if (TcpSession != null && TcpSession.IsConnected)
            {
                Send("100101");//发送心跳
            }
        }

        private static void TcpSession_DataReceived(object sender, DataEventArgs e)
        {
            try
            {
                //客户端接收:
                string body = Encoding.UTF8.GetString(e.Data);
                if (!string.IsNullOrEmpty(body))
                {
                    //Logger.Info($"收到命令:{body}");   处理逻辑代码
                    
                }
            }
            catch (Exception exception)
            {
                //Logger.Error(exception);
            }
        }

        private static void TcpSession_Closed(object sender, EventArgs e)
        {
            //Logger.Info("Socket连接已关闭");
        }

        public static void Send(string body)
        {
            //Logger.Info($"Socket连接状态:{TcpSession?.IsConnected}");
            if (TcpSession != null && TcpSession.IsConnected)
            {
                //Logger.Info("Socket发送命令:" + body);
                var data = System.Text.Encoding.UTF8.GetBytes(body);
                TcpSession.Send(data, 0, data.Length);
            }
        }
    }
}

JS客户端代码实现

<html>
<head >
<meta charset="UTF-8" />
<title>测试Socket</title>
</head>
<body>
  <h1>测试Socket</h1>
  <div id="stock-price"></div>

<button onclick="handleButtonClick()">点击我</button>
  <script>
    // 创建WebSocket连接
    const socket = new WebSocket('ws://192.168.31.71:8552');
    // 监听连接建立事件
    socket.onopen = function(event) {
        console.log('WebSocket连接已建立');
        console.log('连接状态:', socket.readyState );

        //socket.send('1001');//发送消息给后台
        console.log('连接状态:', socket.readyState );
    };
    // 监听消息接收事件
    socket.onmessage = function(event) {
          const data = event.data;
          console.log('收到的服务端消息:', data );

    };
    // 监听连接关闭事件
    socket.onclose = function(event) {
      console.log('WebSocket连接已关闭');
    };
    // 监听连接错误事件
    socket.onerror = function(event) {
      console.error('WebSocket连接错误');
    };

function handleButtonClick() {
    alert('按钮被点击了!');
    socket.send('1');//发送消息给后台
}
  </script>
</body>
</html>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值