ASP.NET 即时通讯之SignalR

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ASP.NET 即时通讯之SignalR相关的知识,希望对你有一定的参考价值。

最近比较累,也没时间写博客,今天索性来一篇,很多时候,网站即时通讯,推送必不可少。但是这部分大家都觉得是那么陌生,不敢尝试,今天我就给大家讲讲SignalR的简单使用。


首先我们打开VS,创建一个web应用程序。然后Nuget安装SignalR,如下。

技术分享

安装好之后,我们在项目的引用中发现程序给我们自动引用了相关的dll以及js文件。

技术分享

技术分享

接着我们创建两个类,一个是ChatHub,继承自Hub,如下

using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Web;

namespace Chart.SignalR
{
    [HubName("ChatRoomHub")]
    public class ChatHub : Hub
    {
        static List<UserEntity> users = new List<UserEntity>();
        public async Task UserEnter(string nickName)
        {
            UserEntity userEntity = new UserEntity
            {
                NickName = nickName,
                ConnectionId = Context.ConnectionId
            };

            users.Add(userEntity);
            await Clients.All.NotifyUserEnter(nickName, users);
        }

        public async Task SendMessage(string nickName, string message)
        {
            await Clients.All.NotifySendMessage(nickName, message);
        }

        public override Task OnDisconnected(bool isStop)
        {
            var currentUser = users.FirstOrDefault(u => u.ConnectionId == Context.ConnectionId);
            if (currentUser != null)
            {
                users.Remove(currentUser);
                Clients.Others.NotifyUserLeft(currentUser.NickName, users);
            }
            return base.OnDisconnected(isStop);
        }
    }

    public class UserEntity
    {
        public string NickName { get; set; }

        public string ConnectionId { get; set; }
    }
}

在这里我们给ChatHub起了别名ChatRoomHub。大家从代码上看就知道我写的将是一个聊天室的代码。这里users用来存储在线人员,NickName存放用户进聊天室之前输入的昵称,ConnectionId则是Hub上下文给每个连接分配的唯一标识,这样方便我们判断用户离线的情况。

在这个Hub子类中,

UserEnter方法用来记录加入聊天的用户,并通知到其他客户端浏览器更新UI。

SendMessage方法用来将客户端发送的消息推送到所有的客户端浏览器。

OnDisconnected顾名思义就是客户端离线,离线后将离线用户从用户数组中删除掉,并通知其他用户浏览器更新UI,注意这里的Clients.Others


接着我们再看一下StartUp类

using Microsoft.Owin;
using Owin;

[assembly: OwinStartup(typeof(Chart.SignalR.StartUp))]
namespace Chart.SignalR
{
    public class StartUp
    {
        public void Configuration(IAppBuilder app)
        {
            app.MapSignalR();
        }
    }
}

这里OwinStartup标识了哪个类是自动启动,app.MapSignalR()则是将app builder 管道映射到一个路径上,待会我们会看到这个路径。

OK,接下来我们看下客户端的代码

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <link rel="stylesheet" href="StyleSheet/bootstrap/css/bootstrap.min.css" />
    <script src="Scripts/jquery-1.6.4.min.js"></script>
    <script src="Scripts/jquery.signalR-2.2.0.min.js"></script>
    <script src="Scripts/bootstrap.min.js"></script>
    <script src="signalr/hubs"></script>
    <style type="text/css">
        .demo {
            margin-bottom: 20px;
            padding-left: 50px;
            position: relative;
        }

        .triangle {
            position: absolute;
            top: 50%;
            margin-top: -8px;
            left: 42px;
            display: inline;
            width: 0;
            height: 0;
            overflow: hidden;
            line-height: 0;
            font-size: 0;
            border-bottom: 8px solid #FFF;
            border-top: 8px solid #FFF;
            border-left: none;
            border-right: 8px solid #3079ED;
        }

        .demo .article {
            float: left;
            color: #FFF;
            display: inline;
            zoom: 1;
            padding: 5px 10px;
            border: 1px solid #3079ED;
            background: #eee;
            border-radius: 5px;
            background-color: #4D90FE;
            background-image: -webkit-gradient(linear,left top,left bottom,from(#4D90FE),to(#4787ED));
            background-image: -webkit-linear-gradient(top,#4D90FE,#4787ED);
            background-image: -moz-linear-gradient(center top, #4D90FE, #4787ED);
            background-image: linear-gradient(top,#4D90FE,#4787ED);
        }

        .fr {
            padding-left: 0px;
            padding-right: 50px;
        }

            .fr .triangle {
                left: auto;
                right: 42px;
                border-bottom: 8px solid #FFF;
                border-top: 8px solid #FFF;
                border-right: none;
                border-left: 8px solid #3079ED;
            }

            .fr .article {
                float: right;
            }

        .round-img-list {
            border-radius: 50%;
            border: solid;
            border-width: 1px;
            border-color: #FFCC00;
            width: 45px;
            height: 45px;
        }

        .clear-float {
            clear: both;
        }
    </style>
</head>
<body>
    <div class="container" style="margin-top: 10px">
        <div class="row">
            <div class="col-md-9">
                <div class="panel panel-primary">
                    <div class="panel-heading">
                        聊天室
                    </div>
                    <div id="div_msgbody" class="panel-body" style=‘min-height: 500px; max-height: 500px; overflow: auto; max-width: 850px;‘>
                        <div id="div_msg" style=‘word-wrap: break-word; word-break: break-word;‘></div>
                    </div>
                    <div class="panel-footer">
                        <div class="row">
                            <div class="col-md-11">
                                <div class="input-group">
                                    <span class="input-group-addon">内容:</span>
                                    <input id="message" type="text" class="form-control" maxlength="500" placeholder=‘在此输入聊天内容‘ />
                                </div>
                            </div>
                            <div class="col-md-1">
                                <button id="btn_Send" type="button" class="btn btn-info">发送</button>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            <div class="col-md-3">
                <div class="panel panel-primary">
                    <div class="panel-heading">
                        用户列表
                    </div>
                    <div class="panel-body" style=‘min-height: 500px; max-height: 500px; overflow: auto;‘>
                        <div id="div_member"></div>
                    </div>
                    <div class="panel-footer">
                        <div style="height: 34px; height: 34px; line-height: 34px">
                            人员总数:<label id="lab_total">0</label>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <script type="text/javascript">
        var userNickName;
        jQuery(document).ready(function () {
            while (!userNickName) {
                userNickName = window.prompt("请输入昵称!");
            }

            var chatHub = $.connection.ChatRoomHub;

            chatHub.client.NotifyUserEnter = function (nickName, users) {
                buildUserTemplate(users);
            }

            chatHub.client.NotifyUserLeft = function (nickName, users) {
                buildUserTemplate(users);
            }

            chatHub.client.NotifySendMessage = function (nickName, message) {
                var userAvatar = ‘http://www.wed114.cn/jiehun/uploads/allimg/160426/39_160426110624_1.jpg‘;
                if (nickName == userNickName) {
                    $("#div_msg").append("<div style=‘text-align:right;float:right‘>"
                    + "<span style=‘margin-right:10px‘>" + nickName + "</span>"
                     + "<img src=‘" + userAvatar + "‘ style=‘height:40px;width:40px;position:relative‘/>"
                    + "<div class=‘demo clearfix fr‘>"
                    + "<span class=‘triangle‘></span>"
                    + "<div class=‘article‘ style=‘word‘>" + message
                    + "</div></div></div><div class=‘clear-float‘/>");
                }
                else {
                    $("#div_msg").append("<div>"
                     + "<img src=‘" + userAvatar + "‘ style=‘height:40px;width:40px;position:relative‘/>"
                     + "<span style=‘left:10px;position:relative‘>" + nickName + "</span>"
                     + "<div class=‘demo clearfix‘>"
                     + "<span class=‘triangle‘></span>"
                     + "<div class=‘article‘>" + message
                     + "</div></div></div>");
                }

                var objDiv = document.getElementById("div_msgbody");
                objDiv.scrollTop = objDiv.scrollHeight;
            }

            $.connection.hub.start().done(function () {
                chatHub.server.userEnter(userNickName);
            });

            $("#message").keydown(function (event) {
                if (event.keyCode == 13) {
                    if ($("#message").val() != "") {
                        chatHub.server.sendMessage(userNickName, $("#message").val());
                        $("#message").val("");
                    }
                }
            });

            $("#btn_Send").click(function () {
                if ($("#message").text != "") {
                    chatHub.server.sendMessage(userNickName, $("#message").val());
                    $("#message").val("");
                }
            })

            function buildUserTemplate(users) {
                $("#lab_total").text(users.length);
                var userTemplate = "<ul style=‘list-style:none;‘>"
                $.each(users, function (e, v) {
                    var userAvatar = ‘http://www.wed114.cn/jiehun/uploads/allimg/160426/39_160426110624_1.jpg‘;
                    userTemplate += "<li style=‘padding-top:5px;‘>"
                        + "<img class=‘round-img-list‘ src=‘" + userAvatar + "‘/>"
                        + "<label style=‘color:#666666;margin-left:10px‘>" + v.NickName + "</label>"
                        + "</li>";
                });

                userTemplate += "</ul>";

                $("#div_member").html(userTemplate);
            }
        });
    </script>
</body>
</html>

布局我就不说了,BootStrap布局。这里需要注意这个路径

<script src="signalr/hubs"></script>

这个路径就是上面说的管道map的路径,在这个路径下会生成hubs.js文件,在这个文件中有注册hub代码,生成hub代理等方法。

技术分享

然后在js中,先让用户输入昵称,输入后,得到chatHub对象,然后

实现NotifyUserEnter,NotifyUserLeft以及NotifySendMessage方法。NotifyUserEnter和NotifyUserLeft方法主要是更新右边panel显示的人员信息。而NotifySendMessage方法则是将服务器推送出的聊天信息展示在span中。在这里我们为了区分自己和别人,自己的聊天信息显示在右边,别人的显示在左边。

var objDiv = document.getElementById("div_msgbody");
objDiv.scrollTop = objDiv.scrollHeight;

这句的意思是如果聊天记录太多出现了滚动条,则将滚动条自动滚动到最底部。

$.connection.hub.start().done(function () {        
     chatHub.server.userEnter(userNickName);
});

这句意思是当有新用户时,就会和服务端建立连接,连接完成后,调用Server端的UserEnter方法,在客户端这里写小写开头,记住。最后发送消息,调用server端的SendMessage方法。

OK,至此程序大概就讲完了,很简单的一个demo,大家看了的也评价评价。俗话说无图无真相,看图

技术分享

技术分享


好了今天就到这里,后面我会继续写nodejs相关的博客。

本文出自 “技术创造价值” 博客,请务必保留此出处http://leelei.blog.51cto.com/856755/1787479

以上是关于ASP.NET 即时通讯之SignalR的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET Core的实时库: SignalR -- 预备知识

IdentityServer4 + SignalR Core +RabbitMQ 构建web即时通讯

ASP.NET SignalR

SignalR实现即时通讯技术,支持跨域

[Asp.net 开发系列之SignalR篇]专题二:使用SignalR实现酷炫端对端聊天功能

SignalR 2.1 简单入门项目