zoukankan      html  css  js  c++  java
  • asp.net core 实现websocket通信

    项目中需要向网页上主动推送信息并弹层提示用户,目前有两种实现方法

    1. ajax轮询
    2. http 1.1 长连接
    3. websocket主动推送

    网页主要用在手机端。

    • 第一种不断发请求浪费流量且不稳定
    • 第二种对服务器资源占用较大,针对每一次请求都要异步阻塞维持
    • 第三种websocket,客户端与服务器只需第一次通过带有websocket标识的Http请求建立连接,不需要每次解析http请求及用户信息,连接维持交由web服务器,handler也无需每次解析请求状态。

    三者的优缺点及原理讲解主要是看的这两篇文章"看完让你彻底搞懂Websocket原理",WebSocket原理及技术简介

    最终选择了websocket,以下是asp.net core 测试实现方式。


    1. .net core 提供了websocketserver的组件,需要在nuget上引用Microsoft.AspNetCore.WebSockets.Server
      在project.json 文件中添加引用,然后点击restore引用,如图

    image

    1. 在项目中添加一个websocktHandler的处理类,处理接收到请求后的逻辑
    2. 在startup.cs 中注册 websocket 服务,并将处理回调注册到流程中,当请求为"/ws"走websockt处理handler
    3. websocket 制定了标识位来标识当前请求,根据该标识处理不同的逻辑

    opcode的值: 0x1代表此帧为文本数据帧, 0x2代表此帧为二进制数据帧, 0x8为控制帧中的连接关闭帧(close frame), 0x9为控制帧中的Ping帧, 0xA(十进制的10)为控制帧中的Pong帧。

    1. 接收请求后将当前请求的socket以键值对方式存入内存中,以便给指定用户发送信息
    2. 根据接收到的数据内容,按分隔符分割处理,获取到【发送人】【数据】【接收人】
    3. 分隔符在配置文件中配置

    .net core 添加自定义配置,需要引用Microsoft.Extensions.Options.ConfigurationExtensions,引用方式同上。

    8.startup.cs 中注册,新建AppSettings类,并映射

     services.AddOptions();
     services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
    

    以上便是websocket服务端的处理流程,最终效果如下

    image

    下面是代码

    WebSocketHandler.cs

    
    
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net.WebSockets;
    using System.Threading;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Http;
    using Microsoft.AspNetCore.Http.Features;
    using Microsoft.Extensions.Options;
    
    namespace WebApplication
    {
        public class SocketHandler
        {
    
            static Dictionary<string, WebSocket> userDic = new Dictionary<string, WebSocket>();
            SocketHandler(WebSocket socket)
            {
                this.socket = socket;
                this.BufferSize = AppSettingsServices.AppSettings.WebSocketConfig.MaxLenth;
            }
    
            int BufferSize;
            WebSocket socket;
            async Task EchoLoop()
            {
                var buffer = new byte[BufferSize];
                var seg = new ArraySegment<byte>(buffer);
                while (this.socket.State == WebSocketState.Open)
                {
    
                    var incoming = await this.socket.ReceiveAsync(seg, CancellationToken.None);
    
                    if (incoming.MessageType == WebSocketMessageType.Close)
                    {
                        if(userDic.Values.Contains(this.socket))
                        {
                            userDic.Remove(userDic.FirstOrDefault(m=>m.Value==this.socket).Key);
                        }
                        await socket.CloseAsync(WebSocketCloseStatus.NormalClosure,
                        String.Empty, CancellationToken.None);
    
                    }
                    else
                    {
                        // string receviceString = System.Text.Encoding.UTF8.GetString(buffer);
                        string receviceString = System.Text.Encoding.UTF8.GetString(buffer.Where(b => b != 0).ToArray());
                        if (string.IsNullOrEmpty(receviceString)) { return; }
                        string separator = AppSettingsServices.AppSettings.WebSocketConfig.Separator;
                        var msg = receviceString.Split(new[] { separator }, StringSplitOptions.None);
                        string toname = string.Empty;
                        string data = string.Empty;
                        string fromname = string.Empty;
                     
                        if (msg.Length > 2)
                        {
                            toname = msg[0];
                            data = msg[1];
                            fromname = msg[2];
                        }
                        string tomsg = data;
                        List<WebSocket> toSocketList = new List<WebSocket>();
                        if (!string.IsNullOrEmpty(toname))
                        {
                            if (!userDic.ContainsKey(fromname))
                            {
                                userDic.Add(fromname, this.socket);
                            }
                            if (userDic.ContainsKey(toname))
                            {
                                toSocketList.Add(userDic[toname]);
                            }
                            else
                            {
                                toSocketList.Add(userDic[fromname]);
                                tomsg = "用户不在线";
                            }
                        }
                        else
                        {
                            toSocketList.AddRange(userDic.Values.ToList());
                        }
                        byte[] toBuffer = System.Text.Encoding.UTF8.GetBytes(tomsg);
                        var outgoing = new ArraySegment<byte>(toBuffer);
                        foreach(WebSocket s in toSocketList){
                          await  s.SendAsync(outgoing, WebSocketMessageType.Text, true, CancellationToken.None);
                        }
                        // await this.socket.SendAsync(outgoing, WebSocketMessageType.Text, true, CancellationToken.None);
                    }
                }
            }
            static async Task Acceptor(HttpContext hc, Func<Task> n)
            {
                if (!hc.WebSockets.IsWebSocketRequest)
                    return;
                var socket = await hc.WebSockets.AcceptWebSocketAsync();
    
                var h = new SocketHandler(socket);
                await h.EchoLoop();
            }
            /// <summary>
            /// branches the request pipeline for this SocketHandler usage
            /// </summary>
            /// <param name="app"></param>
            public static void Map(IApplicationBuilder app)
            {
    
                app.UseWebSockets();
                app.Use(SocketHandler.Acceptor);
    
            }
        }
    
    }
    

    AppSettings.cs

    namespace WebApplication
    {
        public class AppSettings
        {
            public WebSocketConfig WebSocketConfig { get; set; }
    
        }
        public class WebSocketConfig
        {
            //单次发送最大值
            public int MaxLenth { get; set; }
            //to和data的分割符号
            public string Separator { get; set; }
        }
    }
    

    AppSettingsServices.cs

    using Microsoft.Extensions.Options;
    namespace WebApplication
    {
        public class AppSettingsServices
        {
            public static  AppSettings AppSettings;
            public static void SetAppSettings(IOptions<AppSettings>  v)
            {
              AppSettings= v.Value;
            }
    
        }
    
    }
    

    Startup.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.AspNetCore.Http;
    using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
    using Microsoft.EntityFrameworkCore;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Logging;
    using Microsoft.Extensions.Options;
    using WebApplication;
    using WebApplication.Data;
    using WebApplication.Models;
    using WebApplication.Services;
    
    namespace WebApplication
    {
        public class Startup
        {
            public Startup(IHostingEnvironment env)
            {
                var builder = new ConfigurationBuilder()
                    .SetBasePath(env.ContentRootPath)
                    .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                    .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
    
                if (env.IsDevelopment())
                {
                    // For more details on using the user secret store see https://go.microsoft.com/fwlink/?LinkID=532709
                    builder.AddUserSecrets();
                }
    
                builder.AddEnvironmentVariables();
                Configuration = builder.Build();
            }
    
            public IConfigurationRoot Configuration { get; }
    
            // This method gets called by the runtime. Use this method to add services to the container.
            public void ConfigureServices(IServiceCollection services)
            {
                // Add framework services.
                services.AddDbContext<ApplicationDbContext>(options =>
                    options.UseSqlite(Configuration.GetConnectionString("DefaultConnection")));
    
                services.AddIdentity<ApplicationUser, IdentityRole>()
                    .AddEntityFrameworkStores<ApplicationDbContext>()
                    .AddDefaultTokenProviders();
    
                services.AddMvc();
    
                // Add application services.
                services.AddTransient<IEmailSender, AuthMessageSender>();
                services.AddTransient<ISmsSender, AuthMessageSender>();
    
                services.AddOptions();
                services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
    
    
            }
    
            // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
            public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IOptions<AppSettings> appsettings)
            {
                loggerFactory.AddConsole(Configuration.GetSection("Logging"));
                loggerFactory.AddDebug();
    
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                    app.UseDatabaseErrorPage();
                    app.UseBrowserLink();
                }
                else
                {
                    app.UseExceptionHandler("/Home/Error");
                }
    
                app.UseStaticFiles();
    
                app.UseIdentity();
    
                // Add external authentication middleware below. To configure them please see https://go.microsoft.com/fwlink/?LinkID=532715
    
                app.UseMvc(routes =>
                {
                    
                    routes.MapRoute(
                        name: "default",
                        template: "{controller=Home}/{action=Index}/{id?}");
                });
    
                AppSettingsServices.SetAppSettings(appsettings);
                app.Map("/ws", SocketHandler.Map);
    
            }
            // public async void a (IApplicationBuilder b){
            //       b.UseWebSockets();
            //     b.Use(new SocketHandler.Acceptor);
            // }
        }
    }
    

    客户端JS代码

     //w:向谁发,d 发送内容,f 谁发的
        function sendData(w, d, f) {
            initWebSocket();
            if (webSocket.OPEN && webSocket.readyState == 1) {
                var s = "<custom_separator>";
                webSocket.send(w + s + d + s + f);
            }
            if (webSocket.readyState == 2 || webSocket.readyState == 3) {
                $("#div_receive").append("WebSocket closed");
            }
        }
        function initWebSocket() {
            var url = "ws://localhost:5000/ws";
            if (!webSocket) {
                webSocket = new WebSocket(url);
                //Open connection  handler.
                webSocket.onopen = function () {
                    $("#div_receive").append("WebSocket opened" + "<br>");
    
                };
    
                //Message data handler.
                webSocket.onmessage = function (e) {
                    $("#div_receive").append(e.data + "<br>");
                };
    
                //Close event handler.
                webSocket.onclose = function () {
                    $("#div_receive").append("WebSocket closed." + "<br>");
                };
    
                //Error event handler.
                webSocket.onerror = function (e) {
                    $("#div_receive").append(e.message + "<br>");
                }
            }
        }
    
  • 相关阅读:
    在VMware上安装CentOS-6.5 minimal
    [Android] Gradle 安装
    [WPF] 动画Completed事件里获取执行该动画的UI对象
    Realm数据库的使用(二)数据库的添加、删除、修改、查询
    Realm数据库的使用(一)数据库的简单介绍和模型的创建
    进入JVM的世界:《深入理解JVM虚拟机》-- 思维导图
    图解Disruptor框架(一):初识Ringbuffer
    图解Disruptor框架(二):核心概念
    常用排序算法的总结以及编码(Java实现)
    《Java并发编程实战》读书笔记一 -- 简介
  • 原文地址:https://www.cnblogs.com/rexyang1/p/6323096.html
Copyright © 2011-2022 走看看