ASP.NET使用SignalR2实现服务器广播

目录
  • 一、概述
  • 二、服务器端代码
    • 1、创建StockTicker和StockTickerHub类
    • 2、StockTickerHub类
    • 3、StockTicker类
      • 1、将单个实例保存在一个静态字段中
      • 2、使用ConcurrentDictionary来存放股票数据
      • 3、定期的更新股票价格
      • 4、通过SignalR Hub的Context对象来实现服务端的推送
    • 4、注册SignalR路由
  • 三、客户端代码
    • StockTicker.html
    • StockTicker.js
  • 四、输出日志

一、概述

这篇教程通过实现一个股票报价的小程序来讲解如何使用SignalR进行服务器端的推送,服务器会模拟股票价格的波动,并把最新的股票价格推送给所有连接的客户端,最终的运行效果如下图所示。

教程:使用 SignalR 2 广播的服务器

可以通过Install-Package Microsoft.AspNet.SignalR.Sample来安装并查看完整的代码。

二、服务器端代码

新建一个名为Stock.cs的实体类,用来作为服务器端推送消息的载体,具体代码如下。

这个实体类只有Symbol和Price这两个属性需要设置,其它属性将会依据Price自动进行计算。

using System;

namespace Microsoft.AspNet.SignalR.StockTicker
{
    public class Stock
    {
        private decimal _price;

        public string Symbol { get; set; }

        public decimal DayOpen { get; private set; }

        public decimal DayLow { get; private set; }

        public decimal DayHigh { get; private set; }

        public decimal LastChange { get; private set; }

        public decimal Price
        {
            get
            {
                return _price;
            }
            set
            {
                if (_price == value)
                {
                    return;
                }

                LastChange = value - _price;
                _price = value;

                if (DayOpen == 0)
                {
                    DayOpen = _price;
                }
                if (_price < DayLow || DayLow == 0)
                {
                    DayLow = _price;
                }
                if (_price > DayHigh)
                {
                    DayHigh = _price;
                }
            }
        }

        public decimal Change
        {
            get
            {
                return Price - DayOpen;
            }
        }

        public double PercentChange
        {
            get
            {
                return (double)Math.Round(Change / Price, 4);
            }
        }
    }
}

1、创建StockTicker和StockTickerHub类

我们将使用SignalR Hub API来处理服务器到客户端的交互,所以新建一个继承自SignalR Hub类的StockTickerHub类来处理客户端的连接及调用。除此之外,我们还需要维护股票的价格数据以及新建一个Timer对象来定期的更新价格,而这些都与客户端连接无关的。由于Hub实例的生命周期很短暂,只有在比如客户端连接和调用的时候,Hub类实例是为Hub上的每个这样的操作才会创建新的实例,所以不要把与客户端连接及调用无关的代码放置到SignalR Hub类中。在这里,我们将维护股票数据、模拟更新股票价格以及向客户端推送股票价格的代码放置到一个名为StockTicker的类中。

我们只需要在服务器端运行一个StockTicker类的实例(单例模式),由于这个StockTicker类维护着股票的价格,所以它也要能够将最新的股票价格推送给所有的客户端。为了达到这个目的,我们需要在这单个实例中引用所有的StockTickerHub实例,而这可以通过SignalR Hub的Context对象来获得,然后它可以使用SignalR连接Context对象向客户端广播。

2、StockTickerHub类

这个Hub类用来定义客户端可以调用的服务端方法,当客户端与服务器建立连接后,将会调用GetAllStocks()方法来获得股票数据以及当前的价格,因为这个方法是直接从内存中读取数据的,所以会立即返回IEnumerable数据。如果这个方法是通过其它可能会有延时的方式来调用最新的股票数据的话,比如从数据库查询,或者调用第三方的Web Service,那么就需要指定Task>来作为返回值,从而实现异步通信,更多信息请参考ASP.NET SignalR Hubs API Guide - Server - When to execute asynchronously

HubName属性指定了该Hub的别名,即客户端脚本调用的Hub名,如果不使用HubName属性指定别名的话,默认将会使用骆驼命名法,那么它在客户端调用的名称将会是stockTickerHub。

using Microsoft.AspNet.SignalR.Hubs;
using System.Collections.Generic;

namespace Microsoft.AspNet.SignalR.StockTicker
{
    [HubName("stockTicker")]
    public class StockTickerHub : Hub
    {
        private readonly StockTicker _stockTicker;

        public StockTickerHub()

            : this(StockTicker.Instance)
        {
        }

        public StockTickerHub(StockTicker stockTicker)
        {
            _stockTicker = stockTicker;
        }

        public IEnumerable GetAllStocks()
        {
            return _stockTicker.GetAllStocks();
        }

    }
}

接下来我们将会创建StockTicker类,并且创建一个静态实例属性。这样不管有多少个客户端连接或者断开,内存中都只有一个StockTicker类的实例,并且还可以通过该实例的GetAllStocks方法来获得当前的股票数据。

3、StockTicker类

由于所有线程都运行 StockTicker 代码的同一个实例,StockTicker 类必须要是线程安全的。

1、将单个实例保存在一个静态字段中

在这个类中,我们新建了一个名为_instance的字段用来存放该类的实例,并且将构造函数的访问权限设置成私有状态,这样其它的类就只能通过Instance这个静态属性来获得该类的实例,而无法通过关键字new来创建一个新的实例。在这个_instance字段上面,我们使用了Lazy特性,虽然会损失一点儿性能,但是它却可以保证以线程安全的方式来创建实例。

每次客户端连接到服务器时,运行在单独线程中的StockTickerHub类的新实例都会从StockTicker获取StockTicker单例实例。实例静态属性,如前面在StockTickerHub类中看到的。

2、使用ConcurrentDictionary来存放股票数据

这个类定义了一个_stocks字段来存放测试用的股票数据,并且通过GetAllStocks这个方法来进行获取。我们前面讲过客户端会通过StockTickerHub.GetAllStocks来获取当前的股票数据,其实就是这里的股票数据。

在这个测试程序中,我们将数据存直接存放在内存中,这样做并没有什么问题,但在实际的应用场景中,则需要将数据存放在数据库之类的文件中以便长久的保存。

3、定期的更新股票价格

在这个类中,我们定义了一个Timer对象来定期的更新股票的价格。

在实际的场景中,TryUpdateStockPrice方法通常会通过调用第三方的Web Service来获取最新的股票价格,而在这个程序中,我们则是通过随机数来进行模拟该实现。

4、通过SignalR Hub的Context对象来实现服务端的推送

因为股票价格变动是在StockTicker对象中,所以这个对象需要调用客户端的updateStockPrice回调方法来推送数据。在Hub类中,我们可以直接使用API来调用客户端的方法,但是这个StockTicker类并没有继承自Hub,所以无法直接使用这些对象。为了能够向客户端广播数据,StockTicker类需要使用SignalR Hub的Context对象来获得StokTickerHub类的实例,并用它来调用客户端的方法。

using Microsoft.AspNet.SignalR.Hubs;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;

namespace Microsoft.AspNet.SignalR.StockTicker
{
    public class StockTicker
    {
        #region 字段

        // 初始化StockTicker为单例实例
        private readonly static Lazy _instance = new Lazy(() => new StockTicker(  GlobalHost.ConnectionManager.GetHubContext<StockTickerHub>().Clients) );
         //在创建StockTicker类静态实例的时候,把SignalR的Context引用通过构造函数传递给Clients这个属性。
         //在这里只需要获取一次SignalR.Context,这样做有2个好处,首先是因为获取SignalR.Context很耗费资源,其次是获取一次SignalR.Context可以保留消息发送到客户端的预定义顺序。

        private readonly object _marketStateLock = new object();
        private readonly object _updateStockPricesLock = new object();

        //为了线程安全,我们使用了ConcurrentDictionary来存放股票数据,当然你也可以使用Dictionary对象来进行存储,但是在更新数据之前需要进行锁定。
        private readonly ConcurrentDictionary<string, Stock> _stocks = new ConcurrentDictionary<string, Stock>();

        // 控制股票价格波动的百分比
        private readonly double _rangePercent = 0.002;

        private readonly TimeSpan _updateInterval = TimeSpan.FromMilliseconds(250);
        private readonly Random _updateOrNotRandom = new Random();

        private Timer _timer;

        //使用volatile修饰符来标记_updatingStockPrices变量,该修饰符指示一个字段可以由多个同时执行的线程修改,声明为volatile的字段不受编译器优化(假定由单个线程访问)的限制,这样可以确保该字段在任何时间呈现的都是最新的值。
        //该修饰符通常用于由多个线程访问但不使用lock语句对访问进行序列化的字段。
        private volatile bool _updatingStockPrices;

        #endregion 字段

        #region 构造函数

        private StockTicker(IHubConnectionContext<dynamic> clients)
        {
            Clients = clients;

            _stocks.Clear();

            var stocks = new List
            {
                new Stock { Symbol = "MSFT", Price = 41.68m },
                new Stock { Symbol = "AAPL", Price = 92.08m },
                new Stock { Symbol = "GOOG", Price = 543.01m }
            };

            stocks.ForEach(stock => _stocks.TryAdd(stock.Symbol, stock));

            _timer = new Timer(UpdateStockPrices, null, _updateInterval, _updateInterval);//定时更新股价
        }

        #endregion 构造函数

        #region 属性

        private IHubConnectionContext<dynamic> Clients { get; set; } //使用Clients属性,可以使您和在Hub类中一样,通过它来调用客户端的方法。

        public static StockTicker Instance
        {
            get
            {
                return _instance.Value;
            }
        }

        #endregion 属性

        #region 方法

        //获取所有股价
        public IEnumerable GetAllStocks()
        {
            return _stocks.Values;
        }

        //在更新之前,我们使用了_updateStockPricesLock对象将需要更新的部份进行锁定,并通过_updatingStockPrices变量来确定是否有其它线程已经更新了股票的价格。
        //然后通过对每一个股票代码执行TryUpdateStockPrice方法来确定是否更新股票价格以及股票价格的波动幅度。如果检测到股票价格变动,将会通过BroadcastStockPrice方法将最新的股票价格推送给每一个连接的客户端。
        private void UpdateStockPrices(object state)
        {
            // 此函数必须可重入re-entrant,因为它是作为计时器间隔处理程序运行的。
            lock (_updateStockPricesLock)
            {
                if (!_updatingStockPrices)
                {
                    _updatingStockPrices = true;

                    foreach (var stock in _stocks.Values)
                    {
                        if (TryUpdateStockPrice(stock))
                        {
                            BroadcastStockPrice(stock);
                        }
                    }

                    _updatingStockPrices = false;
                }
            }
        }

        private bool TryUpdateStockPrice(Stock stock)
        {
            // 随机选择是否更新该股票
            var r = _updateOrNotRandom.NextDouble();
            if (r > 0.1)
            {
                return false;
            }

            // 以范围百分比的随机因子更新股票价格
            var random = new Random((int)Math.Floor(stock.Price));
            var percentChange = random.NextDouble() * _rangePercent;
            var pos = random.NextDouble() > 0.51;
            var change = Math.Round(stock.Price * (decimal)percentChange, 2);
            change = pos ? change : -change;

            stock.Price += change;
            return true;
        }

        private void BroadcastStockPrice(Stock stock)
        {
            Clients.All.updateStockPrice(stock); //Clients.All是dynamic类型的,意味着发送给所有的客户端,同时SignalR还提供了用来指定具体的客户端或组的属性,具体信息可以参考HubConnectionContext。
        }

        #endregion 方法
    }
}

4、注册SignalR路由

服务器需要知道把哪些请求交由SignalR进行操作,为了实现这个功能,我们需要在OWIN的Startup文件中进行相应的设置。

using Owin;

namespace Microsoft.AspNet.SignalR.StockTicker
{
    public static class Startup
    {
        public static void ConfigureSignalR(IAppBuilder app)
        {
            // For more information on how to configure your application using OWIN startup, visit http://go.microsoft.com/fwlink/?LinkID=316888

            app.MapSignalR();
        }
    }
}

三、客户端代码

StockTicker.html

页面分别引入了jQuery、SignalR、SignalR代理,以及StockTicker脚本文件。SignalR代理文件(/signalr/hubs)将会根据服务器端编写的Hub文件动态的生成相应的脚本(生成关于StockTickerHub.GetAllStocks的相关代码),如果你愿意,你还可以通过SignalR Utilities来手动生成脚本文件,但是需要在MapHubs方法中禁用动态文件创建的功能。

注意:请确保StockTicker.html文件中引入的脚本文件在你的项目中是实际存在的。

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>ASP.NET SignalR Stock Ticker</title>
    <link href="StockTicker.css" rel="external nofollow"  rel="stylesheet" />
</head>
<body>
    <h1>ASP.NET SignalR Stock Ticker Sample</h1>

    <h2>Live Stock Table</h2>
    <div id="stockTable">
        <table border="1">
            <thead>
                <tr><th>Symbol</th><th>Price</th><th>Open</th><th>High</th><th>Low</th><th>Change</th><th>%</th></tr>
            </thead>
            <tbody>
                <tr class="loading"><td colspan="7">loading...</td></tr>
            </tbody>
        </table>
    </div>

    <h2>Live Stock Ticker</h2>
    <div id="stockTicker">
        <div class="inner">
            <ul>
                <li class="loading">loading...</li>
            </ul>
        </div>
    </div>
    <script src="jquery-1.10.2.min.js"></script>
    <script src="jquery.color-2.1.2.min.js"></script>
    <script src="../Scripts/jquery.signalR-2.4.1.js"></script>
    <script src="../signalr/hubs"></script>
    <script src="SignalR.StockTicker.js"></script>
</body>
</html>

StockTicker.js

// Crockford's supplant method (poor man's templating)自定义的模板方法
if (!String.prototype.supplant) {
    String.prototype.supplant = function (o) {
        return this.replace(/{([^{}]*)}/g,
            function (a, b) {
                var r = o[b];
                return typeof r === 'string' || typeof r === 'number' ? r : a;
            }
        );
    };
}

// A simple background color flash effect that uses jQuery Color plugin
jQuery.fn.flash = function (color, duration) {
    var current = this.css('backgroundColor');
    this.animate({ backgroundColor: 'rgb(' + color + ')' }, duration / 2)
        .animate({ backgroundColor: current }, duration / 2);
};

$(function () {

    var ticker = $.connection.stockTicker, //$.connection即是指SignalR代理,这行代码表示将StockTickerHub类的代理的引用保存在变量ticker中,代理的名称即为服务器端通过[HubName]属性设置的名称。
        up = '▲',
        down = '▼',
        $stockTable = $('#stockTable'),
        $stockTableBody = $stockTable.find('tbody'),
        rowTemplate = '{Symbol}{Price}{DayOpen}{DayHigh}{DayLow}{Direction} {Change}{PercentChange}',
        $stockTicker = $('#stockTicker'),
        $stockTickerUl = $stockTicker.find('ul'),
        liTemplate = '
{Symbol} {Price} {Direction} {Change} ({PercentChange})
';

    function formatStock(stock) {
        return $.extend(stock, {
            Price: stock.Price.toFixed(2),
            PercentChange: (stock.PercentChange * 100).toFixed(2) + '%',
            Direction: stock.Change === 0 ? '' : stock.Change >= 0 ? up : down,
            DirectionClass: stock.Change === 0 ? 'even' : stock.Change >= 0 ? 'up' : 'down'
        });
    }

    function scrollTicker() {
        var w = $stockTickerUl.width();
        $stockTickerUl.css({ marginLeft: w });
        $stockTickerUl.animate({ marginLeft: -w }, 15000, 'linear', scrollTicker);
    }

    function stopTicker() {
        $stockTickerUl.stop();
    }

    //init方法调用服务端的getAllStocks方法,并将返回的数据显示在Table中。服务端我们默认会使用帕斯卡命名法,而SignalR会在生成客户端的代理类时,自动将服务端的方法改成骆驼命名法,不过该规则只对方法名及Hub名称有效。
    //而对于对象的属性名,则仍然和服务器端的一样,比如stock.Symbol、stock.Price,而不是stock.symbol、stock.price。
    //如果你想在客户端使用与服务器商相同的名称(包括大小写),或者想自己定义其它的名称,那么你可以通过给Hub方法加上HubMethodName标签来实现这个功能,而HubName标签则可以实现自定义的Hub名称。
    function init() {
        return ticker.server.getAllStocks().done(function (stocks) {
            $stockTableBody.empty();
            $stockTickerUl.empty();
            //遍历服务端返回的股票数据,然后通过调用formatStock来格式化成我们想要的格式,接着通过supplant方法(在StockTicker.js的最顶端)来生成一条新行,并把这个新行插入到表格里面。
            $.each(stocks, function () {
                var stock = formatStock(this);
                $stockTableBody.append(rowTemplate.supplant(stock));
                $stockTickerUl.append(liTemplate.supplant(stock));
            });
        });
    }

    //为了让服务器能够调用客户的代码,我们需要把updateStockPrice添加到stockTicker代理的client对象中
    $.extend(ticker.client, {
        updateStockPrice: function (stock) {
            var displayStock = formatStock(stock),
                $row = $(rowTemplate.supplant(displayStock)),
                $li = $(liTemplate.supplant(displayStock)),
                bg = stock.LastChange < 0
                        ? '255,148,148' // red
                        : '154,240,117'; // green
            //该updateStockPrice方法和init方法一样,通过调用formatStock来格式化成我们想要的格式,接着通过supplant方法(在StockTicker.js的最顶端)来生成一条新行,不过它并不是将该新行追加到Table中,而是找到Table中现有的行,然后使用新行替换它。
            $stockTableBody.find('tr[data-symbol=' + stock.Symbol + ']')
                .replaceWith($row);
            $stockTickerUl.find('li[data-symbol=' + stock.Symbol + ']')
                .replaceWith($li);

            $row.flash(bg, 1000);
            $li.flash(bg, 1000);
            scrollTicker();
        }
    });

    // 客户端的代码编写好之后,就可以通过最后的这行代码来与服务器建立连接,由于这个start方法执行的是异步操作,并会返回一个jQuery延时对象,所以我们要使用jQuery.done函数来处理连接成功之后的操作。
    //这个init方法其实是在start方法完成异步操作后作为回调函数执行的,如果你把init作为一个独立的JavaScript语句放在start方法之后的话,那么程序将会出错,因为这样会导致服务端的方法在客户端还没有与服务器建立连接之前就被调用。
    $.connection.hub.start().done(init);

});

四、输出日志

SignalR内置了日志功能,你可以在客户端选择开启该功能来帮助你调试程序,接下来我们将会通过开启SignalR的日志功能来展示一下在不同的环境下SignalR所使用的传输技术,大至总结如下:

  • WebSocket,至少需要IIS8及以上版本的支持。
  • Server-sent events,支持IE以外的浏览器。
  • Forever frame,支持IE浏览器。
  • Ajax long polling,支持所有浏览器。

在服务器端及客户端都支持的情况下,SignalR默认会选择最佳的传输方式。

1.打开StockTicker.js,然后在客户端与服务端建立连接之前加上下面这段代码。

// Start the connection
$.connection.hub.logging = true;
$.connection.hub.start().done(init);

2.重新运行程序,并打开浏览器的开发者工具,选择控制台标签,就可以看到SignalR输出的日志(如果想看到全部的日志,请刷新页面)。

如果你是在Windows 8(IIS 8)上用IE10打开的话,将会看到WebSocket的连接方式。

如果你是在Windows 7(IIS 7.5)上用IE10打开的话,将会看到使用iframe的连接方式。

Windows 8(IIS 8)上用Firefox的话,将会看到WebSocket的连接方式。

在Windows 7(IIS 7.5)上用Firefox打开的话,将会看到使用Server-sent events的连接方式。

到此这篇关于ASP.NET使用 SignalR2实现服务器广播的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Asp.net core 使用SignalR推送消息过程详解

    1).SignalR简介 ASP.NET Core SignalR 是为 ASP.NET 开发人员提供的一个库,可以简化开发人员将实时 Web 功能添加到应用程序的过程. 实时 Web 功能是指这样一种功能:当所连接的客户端变得可用时服务器代码可以立即向其推送内容,而不是让服务器等待客户端请求新的数据. 2).SignalR主要用途: 它出现的主要用途:可以用在聊天室.Web实时推送消息 (Real-Push-Message).单点和多点通讯.扫码登陆.甚至可以结合其他技术用来做视频聊天等等.

  • Asp.Net Core使用SignalR进行服务间调用方法示例

    网上查询过很多关于ASP.NET core使用SignalR的简单例子,但是大部分都是简易聊天功能,今天心血来潮就搞了个使用SignalR进行服务间调用的简单DEMO. 至于SignalR是什么我就不多说了,微软官方文档也不少. 第一步新建项目 所有VS开发第一步都是新建一个解决方案哈,这里我就不多介绍如何新建项目啦~~ 开发环境,VS2017,.NET CORE 2.1 新建两个asp.net core项目 如此简单的操作大家都懂的 注入SignalR 在被调用的服务端的Startup.cs中

  • Asp.net SignalR 让实时通讯变得如此简单

    巡更项目中,需要发送实时消息,以及需要任务开始提醒,于是便有机会接触到SignalR,在使用过程中,发现用SignalR实现通信非常简单,下面我思明将从三个方面分享一下: 一.SignalR是什么 Asp.net SignalR是微软为实现实时通信的一个类库.一般情况下,SignalR会使用JavaScript的长轮询(long polling)的方式来实现客户端和服务器通信,随着Html5中WebSockets出现,SignalR也支持WebSockets通信.另外SignalR开发的程序不仅

  • asp.net core 2.0 webapi集成signalr(实例讲解)

    在博客园也很多年了,一直未曾分享过什么东西,也没有写过博客,但自己也是汲取着博客园的知识成长的: 这两天想着不能这么无私,最近.NET CORE貌似挺流行的,闲来无事也自己搞了个asp.net core signalr 博客园里面也有人在.net core 2.0下面集成了signalr,但是是集成在同一个项目里面的,但是大家都知道我们很多的项目都是分离的: 而且signalr涉及到连接数和内存资源的占用问题,如果都集成在一个项目里面当访问量多大的时候容易造成网站访问缓慢,具体原因就不多说了 所

  • ASP.NET MVC中SignalR的简单应用

    一.简介 ASP.NET SignalR 是为 ASP.NET 开发人员提供的一个库,可以简化开发人员将实时 Web 功能添加到应用程序的过程.实时 Web 功能是指这样一种功能:当所连接的客户端变得可用时服务器代码可以立即向其推送内容,而不是让服务器等待客户端请求新的数据.--百度百科 首先ASP.NET SignalR 是一个ASP .NET 下的类库,可以在ASP .NET 的Web项目中实现实时通信.让客户端(Web页面)和服务器端可以互相通知消息及调用方法. SignalR自动处理连接

  • ASP.NET Core SignalR中的流式传输深入讲解

    前言 什么是流式传输? 流式传输是这一种以稳定持续流的形式传输数据的技术. 流式传输的使用场景 有些场景中,服务器返回的数据量较大,等待时间较长,客户端不得不等待服务器返回所有数据后,再进行相应的操作.这时候使用流式传输,可以将服务器数据碎片化,当每个数据碎片读取完成之后,就只传输完成的部分,而不需要等待所有数据都读取完成. SignalR SignalR是一个.NET Core/.NET Framework的开源实时框架. SignalR的可使用Web Socket, Server Sent

  • Asp.net通过SignalR2进行实时聊天

    目录 一:什么是signalR 1.SignalR传输方式 1.HTML5 传输 2.Comet 传输 2.SignalR连接模式 二.服务端 1.添加服务端Hub 2.添加OWIN 启动类 三.客户端 四.运行结果 五.一对一聊天实例 一:什么是signalR Asp.net SignalR是微软为实现实时通信的一个类库. 一般情况下,signalR会使用JavaScript的长轮询(long polling)的方式来实现客户端和服务器通信,随着Html5中WebSockets出现,Signa

  • ASP.NET Core实时库SignalR简介及使用

    目录 何为实时 什么是SignalR 回落机制 三种通信方式 long polling(长轮询) server sent events(sse) web socket 进入正题 何为实时 先从理论上解释一下两者的区别. 大多数传统的web应用是这样的:客户端发起http请求到服务端,服务端返回对应的结果.像这样: 也就是说,传统的web应用都是客户端主动发起请求到服务端. 那么实时web应用呢?它不需要主动发起请求,服务端可以主动推送信息到客户端. 举栗子的话,实时聊天工具.web游戏等都可以算

  • ASP.NET使用SignalR2实现服务器广播

    目录 一.概述 二.服务器端代码 1.创建StockTicker和StockTickerHub类 2.StockTickerHub类 3.StockTicker类 1.将单个实例保存在一个静态字段中 2.使用ConcurrentDictionary来存放股票数据 3.定期的更新股票价格 4.通过SignalR Hub的Context对象来实现服务端的推送 4.注册SignalR路由 三.客户端代码 StockTicker.html StockTicker.js 四.输出日志 一.概述 这篇教程通

  • asp.net保存网上图片到服务器的实例

    本文讲述的是根据一个图片的url地址,保存图片到asp.net服务器端的实现方法. 建立GetImage.aspx页面,代码如下: <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="GetImage.aspx.cs" Inherits="KeleyiTestWeb.KImage.GetImage" %> <!DOCTYPE html PUBLI

  • asp 实现显示所有的服务器变量值的函数

    <% '****************************** '函数:ShowServerVar(VarName) '参数:VarName,如提供变量名,则显示该变量值,如果参数为空则显示所有变量值 '作者:阿里西西 '日期:2007/7/13 '描述:显示所有的服务器变量值,返回值:对应值的字符串 '示例:<%=ShowServerVar(VarName)%> '****************************** Function ShowServerVar(VarN

  • ASP脚本组件实现服务器重启

    大家知道直接使用ASP是不能够重启服务器的,这时我们需要制作一个组件来实现功能,ASP通过这个组件调用系统API,然后按照不同的重启和关机方式进行操作! 下面先说COM的制作,在VB中新建一工程,当然是AceiveX dll的. 1)先修改工程属性,在工程属性窗口将工程名称改为system,在类模块窗口将模块名称改为contral,保存工程; 2)然后添加一个模块,用来声明需要使用的API和常数,下面是模块中的内容. Declare Function ExitWindowsEx Lib "use

  • asp.net服务器上几种常见异常的解决方案.

    如下 (1)配置Asp.net站点ISS报出:服务器应用程序不可用.具体异常信息如下: 服务器应用程序不可用 您试图在此 Web 服务器上访问的 Web 应用程序当前不可用.请点击 Web 浏览器中的"刷新"按钮重试您的请求. 管理员注意事项: 详述此特定请求失败原因的错误信息可在 Web 服务器的系统事件日志中找到.请检查此日志项以查明导致该错误发生的原因. 我检查ISS上其他的配置.发现全部都是Asp编写的网站.属性中查看运行的环境竟是Asp.net Framework 1.1版本

  • ASP问答集

    问:为什么我的记录集的RecordCount值总是返回-1? 答:你应当使用这种模式来打开存取数据库的记录集:   rec.open strSQL,conn,1,1   其中的strSQL是操作数据库的SQL语句;conn是联接数据库的Connection 变量. 问:我在ASP脚本中写了很多的注释,这会不会影响服务器处理ASP文件的速度? 答:经国外技术人员测试,带有过多注释的ASP文件整体性能仅仅会下降0.1%,也就是说基本上不会影响到服务器的性能下降的. 问:我需不需要在每个ASP文件的开

  • asp.net 文件上传实例汇总

    ASP.NET依托.net framework类库,封装了大量的功能,使得上传文件非常简单,主要有以下三种基本方法. 方法一:用Web控件FileUpload,上传到网站根目录. Test.aspx关键代码: 复制代码 代码如下: <form id="form1" runat="server">     <asp:FileUpload ID="FileUpload1" runat="server" />

  • ASP.NET Web Page应用深入探讨第1/2页

    一.服务器脚本基础介绍 首先,我们先复习一下Web服务器页面的基本执行方式: 1.客户端通过在浏览器的地址栏敲入地址来发送请求到服务器端 2.服务器接收到请求之后,发给相应的服务器端页面(也就是脚本)来执行,脚本产生客户端的响应,发送回客户端 3.客户端浏览器接收到服务器传回的响应,对Html进行解析,将图形化的网页呈现在用户面前 对于服务器和客户端的交互,通常通过下面几种主要方式: 1.Form:这是最主要的方式,标准化的控件来获取用户的输入,Form的提交将数据发送给服务器端处理 2.Que

  • Win 2003中配置ASP.net环境

    大家知道,Microsoft为了更好地预防恶意用户和攻击者的攻击,在默认情况下,没有将 IIS6.0 安装到 Windows Server 2003 家族的成员上.而且,当我们最初安装 IIS6.0 时,该服务在高度安全和"锁定"模式下安装.在默认情况下,IIS6.0 只为静态内容提供服务即,诸如 ASP.ASP.NET.在服务器端的包含文件.WebDAV 发布和 FrontPage Server Extensions 功能只有在启用时才工作.在windows 2003 IIS 6.0

随机推荐