来自 广播 2020-01-06 03:43 的文章
当前位置: AG真人游戏平台 > 广播 > 正文

AG真人游戏平台第1章 掀起你的盖头来——初识android. 1,客户端通过代理对象调用服务端的方法

一、理解SignalR

第1章 掀起你的盖头来——初识android. 1
1.1 认识android 1
1.2 android的背景 2
1.2.1 android的历史 2
1.2.2 android的发展 2
1.3 我的android我做主 2
1.3.1 开发基于android平台的应用 3
1.3.2 参加android开发者大赛 3
1.3.3 个人英雄主义再现——得到更多人的认可和尊重 3
1.3.4 获得应有的收益——android market 3
1.4 真实体验——android模拟器 4
1.4.1 模拟器概述 4
1.4.2 模拟器和真机的区别 4
1.4.3 模拟器使用注意事项 4
1.5 更上一层楼——加入android开发社区 5
1.6 本章小结 6
第2章 工欲善其事 必先利其器——搭建android开发环境 7
2.1 开发android应用前的准备 7
2.1.1 android开发系统要求 7
2.1.2 android软件开发包 7

一、RabbitMQ是什么?

ASP .NET SignalR 是一个ASP .NET 下的类库,可以在ASP .NET 的Web项目中实现实时通信(即:客户端(Web页面)和服务器端可以互相实时的通知消息及调用方法),SignalR有三种传输模式:LongLooping(长轮询)、WebSocket(HTML5的WEB套接字)、Forever Frame(隐藏框架的长请求连接),可以在WEB客户端显式指定一种或几种,也可以采取默认(推荐),若采取默认,SignalR会根据浏览器的环境自动选择合适的传输方式。

.2.1.3 其他注意事项 8
2.2 windows开发环境搭建 8
2.2.1 jdk、eclipse、android sdk软件安装 8
2.2.2 sdk的家在哪里——设定android sdk home 14
2.2.3 真的准备好了吗——开发环境验证 14
2.2.4 创建android 虚拟设备(avd) 15
2.3 linux一族——ubuntu开发环境搭建 17
2.3.1 java、eclipse和adt插件安装 17
2.3.2 设定android sdk home 23
2.4 mac os一族——苹果开发环境搭建 24
2.5 本章小结 24
第3章 清点可用资本——android sdk介绍 25
3.1 android sdk 基础 25
3.2 深入探寻android sdk的密码 25
3.2.1 android sdk目录结构 25
3.2.2 android.jar及内部结构 27
3.2.3 sdk文档及阅读技巧 27
3.2.4 先来热热身——android sdk例子解析 28
3.2.5 sdk提供的工具介绍 31
3.3 android典型包分析 33
3.3.1 开发的基石——android api核心开发包介绍 33
3.3.2 拓展开发外延——android可选api介绍 34
3.4 本章小结 34
第4章 赚钱的市场——android market及应用发布 35
4.1 google market产生背景与目的 35
4.2 体验“选货”的乐趣——在g1上体验market的使用 35
4.3 android开发活动及特色应用 37
4.3.1 开发应用的领域 37
4.3.2 android market特色应用一览 38
4.4 你也可以做东家——申请market账号 43
4.4.1 卖东西要先入伙——准备工作 43
4.4.2 入伙过程——申请 44
4.5 开张了——在market上发布应用 45
4.5.1 发布时可能遇到的错误 45
4.5.2 卖东西也要签名——生成签名文件 46
4.5.3 打包、签名、发布应用 48
4.6 本章小结 51
第5章 千里之行 始于足下——第一个应用helloworld 52
5.1 helloworld应用分析 52
5.1.1 新建一个android工程 52
5.1.2 填写工程的信息 52
5.1.3 编程实现 53
5.1.4 运行项目 54
5.2 调试项目 54
5.2.1 设置断点 54
5.2.2 debug 项目 55
5.2.3 断点调试 55
5.3 本章小结 56
第6章 磨刀不误砍柴工——android应用程序结构介绍 57
6.1 android体系结构介绍 57
6.1.1 应用程序(application) 57
6.1.2 应用程序框架(application framework) 58
6.1.3 库(libraries)和运行环境(runtime) 58
6.2 android 应用程序组成 59
6.2.1 activity介绍 59
6.2.2 broadcast intent receiver介绍 60
6.2.3 service介绍 61
6.2.4 content provider介绍 61
6.3 android应用工程文件组成 61
6.4 本章小结 62
第7章 良好的学习开端——android基本组件介绍 63
7.1 第一印象很重要——界面ui元素介绍 63
7.1.1 视图组件(view) 63
7.1.2 视图容器组件(viewgroup) 63
7.1.3 布局组件(layout) 64
7.1.4 布局参数(layoutparams) 64
7.2 我的美丽我做主——android中应用界面布局 64
7.2.1 实例操作演示 65
7.2.2 实例编程实现 66
7.3 不积跬步 无以至千里——常用widget组件介绍 75
7.3.1 创建widget组件实例 75
7.3.2 按钮(button)介绍与应用 76
7.3.3 文本框(textview)介绍与应用 77
7.3.4 编辑框(edittext)介绍与应用 79
7.3.5 多项选择(checkbox)介绍与应用 81
7.3.6 单项选择(radiogroup )介绍与应用 83
7.3.7 下拉列表(spinner )介绍与应用 85
7.3.8 自动完成文本(autocompletetextview ) 87
7.3.9 日期选择器(datepicker)介绍与应用 89
7.3.10 时间选择器(timepicker)介绍与应用 90
7.3.11 滚动视图(scrollview)介绍与应用 91
7.3.12 进度条(progressbar)介绍与应用 92
7.3.13 拖动条(seekbar)介绍与应用 93
7.3.14 评分组件(ratingbar)介绍与应用 94
7.3.15 图片视图(imageview)介绍与应用 95
7.3.16 图片按钮(imagebutton)介绍与应用 96
7.3.17 切换图片(imageswitcher&gallery) 96
7.3.18 网格视图(gridview)介绍与应用 99
7.3.19 标签(tab)介绍与应用 101
7.4 友好的菜单——menu介绍与实例 102
7.4.1 实例操作演示.. 103
7.4.2 实例编程实现 103
7.5 android应用的灵魂——intent和activity介绍与实例 106
7.5.1 实例操作演示 106
7.5.2 实例编程实现 106
AG真人游戏平台,7.6 用好列表,做好程序——列表(listview)介绍与实例 111
7.6.1 实例程序演示 111
7.6.2 实例编程实现 112
7.7 友好地互动交流——对话框(dialog)介绍与实例 119
7.8 温馨的提醒——toast和notification应用 127
7.8.1 实例操作演示 128
7.8.2 实例编程实现 129
7.9 本章小结 135
第8章 移动信息仓库——android的数据存储操作 136
8.1 android数据存储概述 136
8.2 轻轻地我保护——sharedpreferences存储 136
8.3 谁的文件,谁主宰——文件存储 140
8.4 打造自己的数据库存储——sqlite存储方式 141
8.4.1 android中对数据库操作 141
8.4.2 完整地操作数据库——日记本实例 147
8.5 我的数据你来用——contentprovider介绍 155
8.5.1 初识contentprovider 155
8.5.2 使用contentprovider读取系统数据 156
8.5.3 使用contentprovider操作数据日记本实例 159
8.6 再学一招——网络存储 171
8.7 本章小结 173
第9章 我来“广播”你的“意图”——intent和broadcast面对面 174
9.1 android应用程序的核心——intent 174
9.1.1 intent基础 174
9.1.2 用intent启动一个新的activity 174
9.1.3 intent 详细讲解 177
9.1.4 android解析intent实现 179
9.2 用广播告诉你——利用intent来广播(broadcast)事件 180
9.2.1 实现android中的广播事件 180
9.2.2 broadcast receiver介绍 181
9.3 应用实例详解 181
9.3.1 程序操作演示 182
9.3.2 实例编程实现 182
9.4 本章小结 186
第10章 一切为用户服务——service应用实例 187
10.1 认识service 187
10.2 使用service 188
10.3 service的生命周期 194
10.4 实例学习service 194
10.4.1 精彩实例一——定时提醒 194
10.4.2 精彩实例二——音乐播放器 198
10.5 本章小结 201
第11章 循序渐进——开发android应用的基本步骤 202
11.1 兵马未动 粮草先行——应用规划及架构设计 202
11.2 应用开发步骤 202
11.2.1 界面设计始终是第一位——实现ui 203
11.2.2 必备的动力源泉——数据操作和存储 203
11.2.3 华丽转身——实现多页面跳转 203
11.2.4 始终为用户做好服务——增加service 203
11.2.5 细节决定成败——完善应用细节 203
11.3 成功就在眼前——应用测试和发布 204
11.3.1 只欠东风——应用测试 204
11.3.2 可以赚钱了——发布到android market 204
11.4 本章小结 204
第12章 android综合案例一——rss阅读器实例 205
12.1 rss介绍 205
12.1.1 rss基础 205
12.1.2 rss的历史 205
12.1.3 rss语法介绍 206
12.2 sax介绍 207
12.2.1 sax基础 207
12.2.2 使用sax的作用 207
12.2.3 怎样使用sax 207
12.3 rss阅读器设计 209
12.3.1 rss阅读器功能设计 209
12.3.2 rss阅读器ui和交互流程设计 209
12.4 rss阅读器的实现 210
12.4.1 程序实体解析 210
12.4.2 实现一个自己的contenthandler 214
12.4.3 activity的实现 217
12.5 本章小结 220
第13章 android综合案例二——基于google map开发个人移动地图 221
13.1 项目ui规划 221
13.2 数据存储实现 222
13.2.1 设计数据库及表结构 222
13.2.2 设计sharepreference 存储 223
13.3 项目实现流程 223
13.3.1 创建项目工程 223
13.3.2 项目各功能及界面实现 224
13.3.3 实现数据存取 247
13.3.4 实现service 252
13.3.5 应用流程整体实现 254
13.3.6 后续开发完善 255
13.4 项目程序演示 255
13.5 项目程序签名、打包和发布 257
13.6 本章小结 259
第14章 android综合案例三——基于android的豆瓣网(web 2.0)移动客户端开发 260
14.1 关于豆瓣网和豆瓣网api 260
14.1.1 豆瓣网介绍 260
14.1.2 豆瓣网api介绍 261
14.1.3 豆瓣网api认证 261
14.1.4 豆瓣网api快速入门 264
14.1.5 豆瓣网api java库介绍 265
14.2 豆瓣网(web 2.0)客户端设计 268
14.2.1 客户端功能规划设计 268
14.2.2 ui和交互流程设计 268
14.2.3 数据存储设计 270
14.3 豆瓣网(web 2.0)客户端实现 270
14.3.1 申请api key 270
14.3.2 activity实现 271
14.4 本章小结 282
第15章 android综合案例四——在线音乐播放器 283
15.1 关于yobo和yobo api 283
15.1.1 yobo简介 284
15.1.2 yobo功能特点 284
15.1.3 yobo api介绍 284
15.2 在线播放器架构设计 285
15.2.1 在线播放器功能规划设计 285
15.2.2 在线播放器ui设计 285
15.2.3 在线播放器数据存储设计 286
15.3 在线播放器的编程实现 286
15.3.1 申请api key 286
15.3.2 基础功能实现 287
15.3.3 实现activity 290
15.3.4 实现service 306
15.3.5 后续开发展望 310
15.4 在线音乐播放器应用演示 310
15.5 在线音乐播放器应用打包、签名和发布 311
15.6 本章小结 311
第16章 android综合案例五——手机信息查看助手 312
16.1 手机信息查看助手功能规划和可行性分析 312
16.1.1 手机信息查看助手功能规划 312
16.1.2 手机信息查看助手可行性分析 312
16.2 手机信息查看助手功能实现 313
16.2.1 手机信息查看助手主界面 313
16.2.2 查看系统信息实现 316
16.2.3 查看硬件信息 320
16.2.4 查看软件信息 324
16.2.5 获取运行时信息 326
16.2.6 文件浏览器 328
16.2.7 项目的细节完善 331
16.3 手机信息查看助手功能展望 332
16.4 项目的打包、签名和发布 332
16.5 本章小结 333
第17章 芝麻开门——android底层开发和移植概述 334
17.1 android原始码下载 334
17.2 实机测试 335
17.2.1 neo freerunner 介绍 335
17.2.2 烧录androidfs.jffs2 336
17.3 android移植技术概论 337
17.3.1 android底层技术的重要性 337
17.3.2 android移植项目介绍 338
17.3.3 android的技术优点... 340

AMQP,即Advanced Message Queuing Protocol,高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。消息中间件主要用于组件之间的解耦,消息的发送者无需知道消息使用者的存在,反之亦然。
AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。
RabbitMQ是一个开源的AMQP实现,服务器端用Erlang语言编写,支持多种客户端,如:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP等,支持AJAX。用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。

二、SignalR的三种实现方式

二、消息队列的特性

第一种:采用集线器类(Hub)+非自动生成代理模式:服务端与客户端分别定义的相对应的方法,客户端通过代理对象调用服务端的方法,服务端通过IHubConnectionContext回调客户端的方法,客户端通过回调方法接收结果。

解耦:消息的生产者与消费者均基于AMQP协议(相同的接口与规范)进行发送与接收消息,互相不存依赖;

之前我写过一篇文章《分享一个基于长连接+长轮询+原生的JS及AJAX实现的多人在线即时交流聊天室》,是通过长轮询+长连接的方式来实现的在线多人聊天室功能,从代码量来看就知道实现起来并不简单,而如今有了SignalR,会简单很多,我这里使用SignalR再来写一个简单的在线多人聊天室示例,以便大家快速掌握SignalR。

冗余:消息只有处理了才会被删除,除非明确允许多个消费者可以收到同一消息的多个副本,否则每个消息只会被单个消费者接收并处理;

DEMO - 1 示例代码如下:

扩展性:可增加或减少多个消息的生产者与消费者,两者的改动均不会影响到双方;

服务端:

灵活性 & 峰值处理能力:因为有良好的扩展性,所以可视服务器的处理情况【可称为:消费者】(比如:高并发负载过大)动态的增减服务器,以提提高处理能力(可称为:负载均衡);

//Startup类文件

using System;
using System.Threading.Tasks;
using Microsoft.Owin;
using Owin;
using Microsoft.AspNet.SignalR;

[assembly: OwinStartup(typeof(TestWebApp.Models.Startup))]

namespace TestWebApp.Models
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            app.MapSignalR();
        }
    }
}


//ChatHub类文件

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

namespace TestWebApp.Models
{
    [HubName("chat")]
    public class ChatHub : Hub
    {
        public static ConcurrentDictionary<string, string> OnLineUsers = new ConcurrentDictionary<string, string>();

        [HubMethodName("send")]
        public void Send(string message)
        {
            string clientName = OnLineUsers[Context.ConnectionId];
            message = HttpUtility.HtmlEncode(message).Replace("rn", "<br/>").Replace("n", "<br/>");
            Clients.All.receiveMessage(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), clientName, message);
        }

        [HubMethodName("sendOne")]
        public void Send(string toUserId, string message)
        {
            string clientName = OnLineUsers[Context.ConnectionId];
            message = HttpUtility.HtmlEncode(message).Replace("rn", "<br/>").Replace("n", "<br/>");
            Clients.Caller.receiveMessage(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), string.Format("您对 {1}", clientName, OnLineUsers[toUserId]), message);
            Clients.Client(toUserId).receiveMessage(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), string.Format("{0} 对您", clientName), message);
        }

        public override System.Threading.Tasks.Task OnConnected()
        {
            string clientName = Context.QueryString["clientName"].ToString();
            OnLineUsers.AddOrUpdate(Context.ConnectionId, clientName, (key, value) => clientName);
            Clients.All.userChange(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), string.Format("{0} 加入了。", clientName), OnLineUsers.ToArray());
            return base.OnConnected();
        }

        public override System.Threading.Tasks.Task OnDisconnected(bool stopCalled)
        {
            string clientName = Context.QueryString["clientName"].ToString();
            Clients.All.userChange(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), string.Format("{0} 离开了。", clientName), OnLineUsers.ToArray());
            OnLineUsers.TryRemove(Context.ConnectionId, out clientName);
            return base.OnDisconnected(stopCalled);
        }

    }
}

可恢复性:消息的生产者与消费者不论哪一方出现问题,均不会影响消息的正常发出与接收(当然单一的生产者与消费者除外,如果是这样也就没有必要使用分布式消息队列);

 

送达保证:只有消息被确认成功处理后才会被删除,否则会重新分发给其它的消费者进行处理,直到确认处理成功为止;

        public ActionResult Index()
        {
            ViewBag.ClientName = "聊客-" + Guid.NewGuid().ToString("N");
            var onLineUserList = ChatHub.OnLineUsers.Select(u => new SelectListItem() { Text = u.Value, Value = u.Key }).ToList();
            onLineUserList.Insert(0, new SelectListItem() { Text = "-所有人-", Value = "" });
            ViewBag.OnLineUsers = onLineUserList;
            return View();
        }

排序保证:先进先出是队列的基本特性;

 

缓冲:同一时间有多个消息进入消息队列,但是同一时间可以指定一个多个消息被消息者接收并处理,其余的消息处理等待状态,这样可以降低服务器的压力,起到缓冲的作用;

WEB客户端:

理解数据流:传递的消息内容以字节数组为主,但可以将对象序列化后成字节数组,然后在消费者接收到消息后,可反序列化成对象并进行相关的处理,应用场景:CQRS;

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <meta charset="utf-8" />
    <title>聊天室</title>
    <script src="~/Scripts/jquery-1.6.4.min.js" type="text/javascript"></script>
    <script src="~/Scripts/jquery.signalR-2.2.0.min.js" type="text/javascript"></script>
    <style type="text/css">
        #chatbox {
            width: 100%;
            height: 500px;
            border: 2px solid blue;
            padding: 5px;
            margin: 5px 0px;
            overflow-x: hidden;
            overflow-y: auto;
        }

        .linfo {
        }

        .rinfo {
            text-align: right;
        }
    </style>
    <script type="text/javascript">
        $(function () {

            var clientName = $("#clientname").val();
            var eChatBox = $("#chatbox");
            var eUsers = $("#users");

            var conn = $.hubConnection();
            conn.qs = { "clientName": clientName };


            conn.start().done(function () {

                $("#btnSend").click(function () {
                    var toUserId = eUsers.val();
                    if (toUserId != "") {
                        chat.invoke("sendOne", toUserId, $("#message").val())
                        .done(function () {
                            //alert("发送成功!");
                            $("#message").val("").focus();
                        })
                        .fail(function (e) {
                            alert(e);
                            $("#message").focus();
                        });
                    }
                    else {
                        chat.invoke("send", $("#message").val())
                        .done(function () {
                            //alert("发送成功!");
                            $("#message").val("").focus();
                        })
                        .fail(function (e) {
                            alert(e);
                            $("#message").focus();
                        });
                    }
                });

            });

            var chat = conn.createHubProxy("chat");

            chat.on("receiveMessage", function (dt, cn, msg) {
                var clsName = "linfo";
                if (cn == clientName || cn.indexOf("您对") >= 0) clsName = "rinfo";
                eChatBox.append("<p class='" + clsName + "'>" + dt + " <strong>" + cn + "</strong> 说:<br/>" + msg + "</p>");
                eChatBox.scrollTop(eChatBox[0].scrollHeight);
            });

            chat.on("userChange", function (dt, msg, users) {
                eChatBox.append("<p>" + dt + " " + msg + "</p>");
                eUsers.find("option[value!='']").remove();
                for (var i = 0; i < users.length; i++) {
                    if (users[i].Value == clientName) continue;
                    eUsers.append("<option value='" + users[i].Key + "'>" + users[i].Value + "</option>")
                }
            });



        });
    </script>
</head>
<body>
    <h3>大众聊天室</h3>
    <div id="chatbox">
    </div>
    <div>
        聊天名称:
        @Html.TextBox("clientname", ViewBag.ClientName as string, new { @readonly = "readonly", style = "width:300px;" })
        聊天对象:
        @Html.DropDownList("users", ViewBag.OnLineUsers as IEnumerable<SelectListItem>)
    </div>
    <div>
        @Html.TextArea("message", new { rows = 5, style = "width:500px;" })
        <input type="button" value="发送消息" id="btnSend" />
    </div>
</body>
</html>

异步通信:允许将一个或多个消息放入消息队列,但并不立即处理它,而是在恰当的时候再去由一个或多个消费者分别接收并处理它们;

服务端与客户端代码都比较简单,网上相关的说明也有,这里就不再解说了,只说一下这种方式JS端调用服务端方法采用:chat.invoke,而被服务端回调的方法则采用:chat.on (这里的chat是createHubProxy创建得来的)

以上是我的个人理解,也可参看《使用消息队列的 10 个理由》

第二种:采用集线器类(Hub)+自动生成代理模式

应用场景:针对高并发且无需立即返回处理结果的时候,可以考虑使用消息队列,如果处理需要立即返回结果则不适合;

DEMO - 2 示例代码如下:

三、RabbitMQ环境的安装

服务端与DEMO 1相同,无需改变

1.服务器端:

客户端:

A.需要先安装Erlang环境,下载地址:,可能有时无法正常访问,可以通过VPN代理来访问该网站或在其它网站上下载(比如:CSDN)

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <meta charset="utf-8" />
    <title>聊天室</title>
    <script src="~/Scripts/jquery-1.6.4.min.js" type="text/javascript"></script>
    <script src="~/Scripts/jquery.signalR-2.2.0.min.js" type="text/javascript"></script>
    <script src="~/signalr/hubs" type="text/javascript"></script>
    <style type="text/css">
        #chatbox {
            width: 100%;
            height: 500px;
            border: 2px solid blue;
            padding: 5px;
            margin: 5px 0px;
            overflow-x: hidden;
            overflow-y: auto;
        }

        .linfo {
        }

        .rinfo {
            text-align: right;
        }
    </style>
    <script type="text/javascript">
        $(function () {

            var clientName = $("#clientname").val();
            var eChatBox = $("#chatbox");
            var eUsers = $("#users");

            var chat = $.connection.chat;
            $.connection.hub.qs = { "clientName": clientName };
            chat.state.test = "test";

            chat.client.receiveMessage = function (dt, cn, msg) {
                var clsName = "linfo";
                if (cn == clientName || cn.indexOf("您对")>=0) clsName = "rinfo";
                eChatBox.append("<p class='" + clsName + "'>" + dt + " <strong>" + cn + "</strong> 说:<br/>" + msg + "</p>");
                eChatBox.scrollTop(eChatBox[0].scrollHeight);
            }

            chat.client.userChange = function (dt, msg, users) {
                eChatBox.append("<p>" + dt + " " + msg + "</p>");
                eUsers.find("option[value!='']").remove();
                for (var i = 0; i < users.length; i++) {
                    if (users[i].Value == clientName) continue;
                    eUsers.append("<option value='" + users[i].Key + "'>" + users[i].Value + "</option>")
                }
            }

            $.connection.hub.start().done(function () {

                $("#btnSend").click(function () {
                    var toUserId = eUsers.val();
                    if (toUserId != "") {
                        chat.server.sendOne(toUserId, $("#message").val())
                            .done(function () {
                                //alert("发送成功!");
                                $("#message").val("").focus();
                            })
                            .fail(function (e) {
                                alert(e);
                                $("#message").focus();
                            });
                    }
                    else {
                        chat.server.send($("#message").val())
                        .done(function () {
                            //alert("发送成功!");
                            $("#message").val("").focus();
                        })
                        .fail(function (e) {
                            alert(e);
                            $("#message").focus();
                        });
                    }
                });

            });

        });
    </script>
</head>
<body>
    <h3>大众聊天室</h3>
    <div id="chatbox">
    </div>
    <div>
        聊天名称:
        @Html.TextBox("clientname", ViewBag.ClientName as string, new { @readonly = "readonly", style = "width:300px;" })
        聊天对象:
        @Html.DropDownList("users", ViewBag.OnLineUsers as IEnumerable<SelectListItem>)
    </div>
    <div>
        @Html.TextArea("message", new { rows = 5, style = "width:500px;" })
        <input type="button" value="发送消息" id="btnSend" />
    </div>
</body>
</html>

B.安装RabbitMQ Server(有针对多个操作系统的下载,我这边以WINDOWS平台为主),下载地址:,

上述代码中特别需要注意的是,需要引用一个“不存在的JS目录”:<script src="~/signalr/hubs" type="text/javascript"></script>,为什么要打引号,是因为我们在写代码的时候是不存在的,而当运行后就会自动生成signalr的代理脚本,这就是与非自动生成代理脚本最根本的区别,也正是因为这个自动生成的脚本,我们可以在JS中更加方便的调用服务端方法及定义回调方法,调用服务端方法采用:chat.server.XXX,而被服务端回调的客户端方法则采用:chat.client.XXX

说明:最新版的Erlang及abbitMQ Server安装后,一般WINDOWS环境变量及服务均都已正常安装与并正常启动,可不是最新版或没有安装好,则可执行以下命令:

看一下上述两种的运行效果截图吧:

Setx ERLANG_HOME “C:Program Fileserl7.1″ -Erlang的-安装目录,也可通过系统属性-->高级-->环境变量来手动设置;

AG真人游戏平台 1

cd C:Program Files (x86)RabbitMQ Serverrabbitmq_server-3.5.6sbin --切换到RabbitMQ Server的sbin目录下,然后执行如下命令:

 

rabbitmq-service install
rabbitmq-service enable
rabbitmq-service start

AG真人游戏平台 2

安装并设置OK后,可以通过:rabbitmqctl status查看运行情况、rabbitmqctl list_users查看当前用户、以下命令增加一个新用户:

第三种:采用持久化连接类(PersistentConnection)

rabbitmqctl add_user username password
rabbitmqctl set_permissions username ".*" ".*" ".*"
rabbitmqctl set_user_tags username administrator

 DEMO - 3 示例代码如下:

修改密码:rabbitmqctl change_password username newpassowrd

服务端:

删除指定的用户:rabbitmqctl delete_user username 

//Startup类:

using System;
using System.Threading.Tasks;
using Microsoft.Owin;
using Owin;
using Microsoft.AspNet.SignalR;

[assembly: OwinStartup(typeof(TestWebApp.Models.Startup))]

namespace TestWebApp.Models
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            app.MapSignalR<MyConnection>("/MyConnection");
        }
    }
}


//MyConnection类:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Web;
using Microsoft.AspNet.SignalR;

namespace TestWebApp.Models
{
    public class MyConnection : PersistentConnection
    {
        private static List<string> monitoringIdList = new List<string>();
        protected override Task OnConnected(IRequest request, string connectionId)
        {
            bool IsMonitoring = (request.QueryString["Monitoring"] ?? "").ToString() == "Y";
            if (IsMonitoring)
            {
                if (!monitoringIdList.Contains(connectionId))
                {
                    monitoringIdList.Add(connectionId);
                }
                return Connection.Send(connectionId, "ready");
            }
            else
            {
                if (monitoringIdList.Count > 0)
                {
                    return Connection.Send(monitoringIdList, "in_" + connectionId);
                }
                else
                {
                    return Connection.Send(connectionId, "nobody");
                }
            }
        }

        protected override Task OnReceived(IRequest request, string connectionId, string data)
        {
            if (monitoringIdList.Contains(connectionId))
            {
                return Connection.Send(data, "pass");
            }
            return null;
        }

        protected override Task OnDisconnected(IRequest request, string connectionId, bool stopCalled)
        {
            if (!monitoringIdList.Contains(connectionId))
            {
                return Connection.Send(monitoringIdList, "out_" + connectionId);
            }
            return null;
        }
    }
}

列出所有queue:rabbitmqctl list_queues

WEB客户端:

列出指定queue的信息:rabbitmqctl list_queues [the queue name] messages_ready messages_unacknowledged

<!-- MonitoringPage.cshtml 监控管理页面-->


<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>MonitoringPage</title>
    <script src="~/Scripts/jquery-1.6.4.min.js" type="text/javascript"></script>
    <script src="~/Scripts/jquery.signalR-2.2.0.min.js" type="text/javascript"></script>
    <style type="text/css">
        table {
            border:1px solid #808080;
            width:600px;
        }
        td {
            border:1px solid #808080;
            padding:3px;
        }
        .odd{ background-color: #bbf;}
        .even{ background-color:#ffc; }
        .non-temptr {
            display:none;
        }
    </style>
    <script type="text/javascript">
        $(function () {
            $("#userstable tbody tr:odd").addClass("odd");
            $("#userstable tbody tr:even").addClass("even");

            var conn = $.connection("/MyConnection", {"Monitoring":"Y"});

            conn.start().done(function () {
                $("#userstable").delegate("button.pass", "click", function () {
                    var rid = $(this).parent("td").prev().attr("data-rid");
                    conn.send(rid);
                    var tr = $(this).parents("tr");
                    tr.remove();
                });

            }).fail(function (msg) {
                alert(msg);
            });

            conn.received(function (msg) {
                if (msg == "ready")
                {
                    $("#spstatus").html("监控服务已就绪");
                    return;
                }
                else if (msg.indexOf("in_") == 0) {
                    var tr = $(".non-temptr").clone(true);
                    tr.removeClass("non-temptr");
                    var td = tr.children().first();
                    var rid = msg.toString().substr("in_".length);
                    td.html(rid + "进入被监控页面,是否允许?");
                    td.attr("data-rid", rid);
                    $("#userstable tbody").append(tr);
                }
                else
                {
                    var rid = msg.toString().substr("out_".length);
                    $("td[data-rid=" + rid + "]").parent("tr").remove();
                }
            });

        });
    </script>
</head>
<body>
    <div>
        以下是实时监控到进入EnterPage页面的用户情况:(服务状况:<strong></strong>)
    </div>
    <table id="userstable">
        <tr>
            <td>用户进入消息</td>
            <td>授 权</td>
        </tr>
        <tr class="non-temptr">
            <td></td>
            <td style="width:100px"><button class="pass">允许</button></td>
        </tr>
    </table>
</body>
</html>


<!-- EnterPage.cshtml 监控受限页面-->
<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>EnterPage</title>
    <script src="~/Scripts/jquery-1.6.4.min.js" type="text/javascript"></script>
    <script src="~/Scripts/jquery.signalR-2.2.0.min.js" type="text/javascript"></script>
</head>
<body>
    <script type="text/javascript">
        $(function () {
            var conn = $.connection("/MyConnection");

            conn.start().fail(function (msg) {
                alert(msg);
            });

            conn.received(function (data) {
                if (data == "pass") {
                    $("#msg").html("管理员已审核通过,可以进入浏览详情。");
                    setTimeout(function () {
                        self.location = "http://www.zuowenjun.cn";
                    }, 3000);
                }
                else
                {
                    $("#msg").html("无管理员在线,请稍候再重新进入该页面。");
                }
            });
        });
    </script>
    <div id="msg"> 
        该页面浏览受限,已自动将您的浏览请求发给管理员,请稍候。。。
    </div>
</body>
</html>

列出所有exchange:rabbitmqctl list_exchanges

上述代码可以看出与采用Hub(集线器类)的不同之处,一是:Startup.Configuration中是需要指定app.MapSignalR<MyConnection>("/MyConnection"),二是需实现继承自PersistentConnection类的自定义的持久化连接类,在这个连接中可以重写:OnConnected、OnDisconnected、OnReceived、OnReconnected、ProcessRequest方法,同时有几个重要的属性成员Connection、Groups,服务端发消息给客户端采用:Connection.Broadcast(广播,所有客户端都可以收到消息),Connection.Send(发送给指定的客户端)

列出所有binding:rabbitmqctl list_bindings

运行效果如下截图示:

安装基于web的管理插件:rabbitmq-plugins.bat enable rabbitmq_management

AG真人游戏平台 3

当然还有其它的命令,大家可以去查看官网及其它资料,但我认为知道以上的命令足够用了

 

四、RabbitMQ的基本用法

AG真人游戏平台 4

使用RabbitMQ客户端就必需在项目中引用其相关的组件,这里可以通过NuGet安装或从官网下载再引用均可,方法很简单,不再重述;

 

1.普通用法:采用默认的exchange(交换机,或称路由器)+默认的exchange类型:direct+noAck(自动应答,接收就应答)

AG真人游戏平台 5

    /// <summary>
    /// 消息发送者,一般用在客户端
    /// </summary>
    class RabbitMQPublish
    {
        static void Main(string[] args)
        {
            var factory = new ConnectionFactory();//创建连接工厂并初始连接
            factory.HostName = "localhost";
            factory.UserName = "zwj";
            factory.Password = "www.zuowenjun.cn";

            using (var connection = factory.CreateConnection())//创建一个连接
            {
                using (var channel = connection.CreateModel()) //创建一个通道
                {
                    channel.QueueDeclare("hello", false, false, false, null);//创建一个队列

                    string message = "";
                    while (message!="exit")
                    {
                        Console.Write("Please enter the message to be sent:");
                        message = Console.ReadLine();
                        var body = Encoding.UTF8.GetBytes(message);
                        channel.BasicPublish("", "hello", null, body); //发送消息
                        Console.WriteLine("set message: {0}", message);
                    }
                }
            }
        }
    }



    /// <summary>
    /// 消费者,一般用在服务端
    /// </summary>
    class RabbitMQConsume
    {
        static void Main(string[] args)
        {
            var factory = new ConnectionFactory();//创建连接工厂并初始连接
            factory.HostName = "localhost";
            factory.UserName = "zwj";
            factory.Password = "www.zuowenjun.cn";

            using (var connection = factory.CreateConnection())//创建一个连接
            {
                using (var channel = connection.CreateModel())//创建一个通道
                {
                    channel.QueueDeclare("hello", false, false, false, null);//创建一个队列

                    var consumer = new QueueingBasicConsumer(channel);//创建一个消费者
                    channel.BasicConsume("hello", true, consumer);//开启消息者与通道、队列关联

                    Console.WriteLine(" waiting for message.");
                    while (true)
                    {
                        var ea = (BasicDeliverEventArgs)consumer.Queue.Dequeue();//接收消息并出列

                        var body = ea.Body;//消息主体
                        var message = Encoding.UTF8.GetString(body);
                        Console.WriteLine("Received {0}", message);
                        if (message == "exit")
                        {
                            Console.WriteLine("exit!");
                            break;
                        }

                    }
                }
            }

        }
    }

 

2.负载均衡处理模式:采用默认的exchange(交换机)+智能分发+默认的exchange类型:direct+手动应答

 

消息生产者/发布者代码与上面相同;

SignalR支持额外附加:QueryString、Cookie、State,具体的客户端设置与服务端接收请见上面的代码,同时也可以参见如下其它博主总结的表格(SignalR的Javascript客户端API使用方式整理):

以下是消费者代码:

AG真人游戏平台 6

    /// <summary>
    /// 消费者,一般用在服务端
    /// </summary>
    class RabbitMQConsume
    {
        static void Main(string[] args)
        {
            var factory = new ConnectionFactory();//创建连接工厂并初始连接
            factory.HostName = "localhost";
            factory.UserName = "zwj";
            factory.Password = "www.zuowenjun.cn";

            using (var connection = factory.CreateConnection())//创建一个连接
            {
                using (var channel = connection.CreateModel())//创建一个通道
                {
                    channel.QueueDeclare("hello", false, false, false, null);//创建一个队列
                    channel.BasicQos(0, 1, false);//在一个工作者还在处理消息,并且没有响应消息之前,不要给他分发新的消息。相反,将这条新的消息发送给下一个不那么忙碌的工作者。

                    var consumer = new QueueingBasicConsumer(channel);//创建一个消费者
                    channel.BasicConsume("hello", false, consumer);//开启消息者与通道、队列关联

                    Console.WriteLine(" waiting for message.");
                    while (true)
                    {
                        var ea = (BasicDeliverEventArgs)consumer.Queue.Dequeue();//接收消息并出列

                        var body = ea.Body;//消息主体
                        var message = Encoding.UTF8.GetString(body);
                        Console.WriteLine("Received {0}", message);
                        channel.BasicAck(ea.DeliveryTag, false);
                        if (message == "exit")
                        {
                            Console.WriteLine("exit!");
                            break;
                        }
                        Thread.Sleep(1000);
                    }
                }
            }

        }
    }

3.消息持久化模式:在2的基础上加上持久化,这样即使生产者或消费者或服务端断开,消息均不会丢失

    /// <summary>
    /// 消息发送者,一般用在客户端
    /// </summary>
    class RabbitMQPublish
    {
        static void Main(string[] args)
        {
            var factory = new ConnectionFactory();//创建连接工厂并初始连接
            factory.HostName = "localhost";
            factory.UserName = "zwj";
            factory.Password = "www.zuowenjun.cn";

            using (var connection = factory.CreateConnection())//创建一个连接
            {
                using (var channel = connection.CreateModel()) //创建一个通道
                {
                    channel.QueueDeclare("hello", true, false, false, null);//创建一个队列,第2个参数为true表示为持久队列
                    var properties = channel.CreateBasicProperties();
                    //properties.SetPersistent(true);这个方法提示过时,不建议使用
                    properties.DeliveryMode = 2;//1表示不持久,2.表示持久化
                    string message = "";
                    while (message!="exit")
                    {
                        Console.Write("Please enter the message to be sent:");
                        message = Console.ReadLine();
                        var body = Encoding.UTF8.GetBytes(message);
                        channel.BasicPublish("", "hello", properties, body); //发送消息
                        Console.WriteLine("set message: {0}", message);
                    }
                }
            }
        }
    }

    /// <summary>
    /// 消费者,一般用在服务端
    /// </summary>
    class RabbitMQConsume
    {
        static void Main(string[] args)
        {
            var factory = new ConnectionFactory();//创建连接工厂并初始连接
            factory.HostName = "localhost";
            factory.UserName = "zwj";
            factory.Password = "www.zuowenjun.cn";

            using (var connection = factory.CreateConnection())//创建一个连接
            {
                using (var channel = connection.CreateModel())//创建一个通道
                {
                    channel.QueueDeclare("hello", true, false, false, null);//创建一个队列,第2个参数为true表示为持久队列
                    channel.BasicQos(0, 1, false);//在一个工作者还在处理消息,并且没有响应消息之前,不要给他分发新的消息。相反,将这条新的消息发送给下一个不那么忙碌的工作者。

                    var consumer = new QueueingBasicConsumer(channel);//创建一个消费者
                    channel.BasicConsume("hello", false, consumer);//开启消息者与通道、队列关联

                    Console.WriteLine(" waiting for message.");
                    while (true)
                    {
                        var ea = (BasicDeliverEventArgs)consumer.Queue.Dequeue();//接收消息并出列

                        var body = ea.Body;//消息主体
                        var message = Encoding.UTF8.GetString(body);
                        Console.WriteLine("Received {0}", message);
                        channel.BasicAck(ea.DeliveryTag, false);
                        if (message == "exit")
                        {
                            Console.WriteLine("exit!");
                            break;
                        }
                        Thread.Sleep(1000);
                    }
                }
            }

        }
    }

4.广播订阅模式:定义一个交换机,其类型设为广播类型,发送消息时指定这个交换机,消费者的消息队列绑定到该交换机实现消息的订阅,订阅后则可接收消息,未订阅则无法收到消息

    /// <summary>
    /// 消息发送者/生产者,一般用在客户端
    /// </summary>
    class RabbitMQPublish
    {
        static void Main(string[] args)
        {
            var factory = new ConnectionFactory();//创建连接工厂并初始连接
            factory.HostName = "localhost";
            factory.UserName = "zwj";
            factory.Password = "www.zuowenjun.cn";

            using (var connection = factory.CreateConnection())//创建一个连接
            {
                using (var channel = connection.CreateModel()) //创建一个通道
                {
                    channel.ExchangeDeclare("publish", "fanout",true);//定义一个交换机,且采用广播类型,并设为持久化
                    string queueName = channel.QueueDeclare("hello", true, false, false, null);//创建一个队列,第2个参数为true表示为持久队列,这里将结果隐式转换成string
                    var properties = channel.CreateBasicProperties();
                    //properties.SetPersistent(true);这个方法提示过时,不建议使用
                    properties.DeliveryMode = 2;//1表示不持久,2.表示持久化
                    string message = "";
                    while (message!="exit")
                    {
                        Console.Write("Please enter the message to be sent:");
                        message = Console.ReadLine();
                        var body = Encoding.UTF8.GetBytes(message);
                        channel.BasicPublish("publish", "hello", properties, body); //发送消息,这里指定了交换机名称,且routeKey会被忽略
                        Console.WriteLine("set message: {0}", message);
                    }
                }
            }
        }
    }

    /// <summary>
    /// 消费者,一般用在服务端
    /// </summary>
    class RabbitMQConsume
    {
        static void Main(string[] args)
        {
            var factory = new ConnectionFactory();//创建连接工厂并初始连接
            factory.HostName = "localhost";
            factory.UserName = "zwj";
            factory.Password = "www.zuowenjun.cn";

            using (var connection = factory.CreateConnection())//创建一个连接
            {
                using (var channel = connection.CreateModel())//创建一个通道
                {
                    channel.ExchangeDeclare("publish", "fanout", true);//定义一个交换机,且采用广播类型,并持久化该交换机,并设为持久化
                    string queueName = channel.QueueDeclare("hello", true, false, false, null);//创建一个队列,第2个参数为true表示为持久队列
                    channel.QueueBind(queueName, "publish", "");//将队列绑定到名publish的交换机上,实现消息订阅
                    channel.BasicQos(0, 1, false);//在一个工作者还在处理消息,并且没有响应消息之前,不要给他分发新的消息。相反,将这条新的消息发送给下一个不那么忙碌的工作者。

                    var consumer = new QueueingBasicConsumer(channel);//创建一个消费者
                    channel.BasicConsume(queueName, false, consumer);//开启消息者与通道、队列关联

                    Console.WriteLine(" waiting for message.");
                    while (true)
                    {
                        var ea = (BasicDeliverEventArgs)consumer.Queue.Dequeue();//接收消息并出列

                        var body = ea.Body;//消息主体
                        var message = Encoding.UTF8.GetString(body);
                        Console.WriteLine("Received {0}", message);
                        channel.BasicAck(ea.DeliveryTag, false);//应答
                        if (message == "exit")
                        {
                            Console.WriteLine("exit!");
                            break;
                        }
                        Thread.Sleep(1000);
                    }
                }
            }

        }
    }

5.主题订阅模式:定义一个交换机,其类型设为主题订阅类型,发送消息时指定这个交换机及RoutingKey,消费者的消息队列绑定到该交换机并匹配到RoutingKey实现消息的订阅,订阅后则可接收消息,未订阅则无法收到消息

    /// <summary>
    /// 消息发送者/生产者,一般用在客户端
    /// </summary>
    class RabbitMQPublish
    {
        static void Main(string[] args)
        {
            var factory = new ConnectionFactory();//创建连接工厂并初始连接
            factory.HostName = "localhost";
            factory.UserName = "zwj";
            factory.Password = "www.zuowenjun.cn";

            using (var connection = factory.CreateConnection())//创建一个连接
            {
                using (var channel = connection.CreateModel()) //创建一个通道
                {
                    channel.ExchangeDeclare("publish-topic", "topic", true);//定义一个交换机,且采用广播类型,并持久化该交换机
                   channel.QueueDeclare("hello-mq", true, false, false, null);//创建一个队列,第2个参数为true表示为持久队列
                    var properties = channel.CreateBasicProperties();
                    //properties.SetPersistent(true);这个方法提示过时,不建议使用
                    properties.DeliveryMode = 2;//1表示不持久,2.表示持久化
                    string message = "";
                    while (message!="exit")
                    {
                        Console.Write("Please enter the message to be sent:");
                        message = Console.ReadLine();
                        var body = Encoding.UTF8.GetBytes(message);
                        channel.BasicPublish("publish-topic", "hello.test", properties, body); //发送消息,这里指定了交换机名称,且routeKey会被忽略
                        Console.WriteLine("set message: {0}", message);
                    }
                }
            }
        }
    }


    /// <summary>
    /// 消费者,一般用在服务端
    /// </summary>
    class RabbitMQConsume
    {
        static void Main(string[] args)
        {
            var factory = new ConnectionFactory();//创建连接工厂并初始连接
            factory.HostName = "localhost";
            factory.UserName = "zwj";
            factory.Password = "www.zuowenjun.cn";

            using (var connection = factory.CreateConnection())//创建一个连接
            {
                using (var channel = connection.CreateModel())//创建一个通道
                {
                    channel.ExchangeDeclare("publish-topic", "topic",true);//定义一个交换机,且采用广播类型,并持久化该交换机
                    string queueName = channel.QueueDeclare("hello-mq", true, false, false, null);//创建一个队列,第2个参数为true表示为持久队列
                    channel.QueueBind(queueName, "publish-topic", "*.test");//将队列绑定到路由上,实现消息订阅
                    channel.BasicQos(0, 1, false);//在一个工作者还在处理消息,并且没有响应消息之前,不要给他分发新的消息。相反,将这条新的消息发送给下一个不那么忙碌的工作者。

                    var consumer = new QueueingBasicConsumer(channel);//创建一个消费者
                    channel.BasicConsume(queueName, false, consumer);//开启消息者与通道、队列关联

                    Console.WriteLine(" waiting for message.");
                    while (true)
                    {
                        var ea = (BasicDeliverEventArgs)consumer.Queue.Dequeue();//接收消息并出列

                        var body = ea.Body;//消息主体
                        var message = Encoding.UTF8.GetString(body);
                        Console.WriteLine("Received {0}", message);
                        channel.BasicAck(ea.DeliveryTag, false);//应答
                        if (message == "exit")
                        {
                            Console.WriteLine("exit!");
                            break;
                        }
                        Thread.Sleep(1000);
                    }
                }
            }

        }
    }

  

交换机路由类型如下:

Direct Exchange:直接匹配,通过Exchange名称+RoutingKey来发送与接收消息;

Fanout Exchange:广播订阅,向所有消费者发布消息,但只有消费者将队列绑定到该路由才能收到消息,忽略RoutingKey;

Topic Exchange:主题匹配订阅,这里的主题指的是RoutingKey,RoutingKey可以采用通配符,如:*或#,RoutingKey命名采用.来分隔多个词,只有消费者将队列绑定到该路由且指定的RoutingKey符合匹配规则时才能收到消息;

Headers Exchange:消息头订阅,消息发布前,为消息定义一个或多个键值对的消息头,然后消费者接收消息时同样需要定义类似的键值对请求头,里面需要多包含一个匹配模式(有:x-mactch=all,或者x-mactch=any),只有请求头与消息头相匹配,才能接收到消息,忽略RoutingKey;

本文内容参考了以下文章:

.NET 环境中使用RabbitMQ

.Net下RabbitMQ的使用系列文章

本文由AG真人游戏平台发布于广播,转载请注明出处:AG真人游戏平台第1章 掀起你的盖头来——初识android. 1,客户端通过代理对象调用服务端的方法

关键词: