目录
3.1 新建类 MyHub.cs、ClientInfo.cs、Helper.cs
直入主题
一、创建项目
先创建一个控制台应用程序用来搭建扫码登陆的服务端
二、安装Package
使用Nuget安装以下Package
Microsoft.AspNet.SignalR.SelfHost ——用于构建自承载的SignalR服务
Microsoft.AspNet.WebApi.Owin ——用于提供WebApi服务
Microsoft.Owin.Cors ——用于跨域设置
以上包有依赖项,需要全部引入,引用完成后的packages.config文件如下:
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.AspNet.Cors" version="5.0.0" targetFramework="net452" />
<package id="Microsoft.AspNet.SignalR.Core" version="2.4.1" targetFramework="net452" />
<package id="Microsoft.AspNet.SignalR.SelfHost" version="2.4.1" targetFramework="net452" />
<package id="Microsoft.AspNet.WebApi.Client" version="5.2.7" targetFramework="net452" />
<package id="Microsoft.AspNet.WebApi.Core" version="5.2.7" targetFramework="net452" />
<package id="Microsoft.AspNet.WebApi.Owin" version="5.2.7" targetFramework="net452" />
<package id="Microsoft.Owin" version="4.1.1" targetFramework="net452" />
<package id="Microsoft.Owin.Cors" version="4.1.1" targetFramework="net452" />
<package id="Microsoft.Owin.Diagnostics" version="2.1.0" targetFramework="net452" />
<package id="Microsoft.Owin.Host.HttpListener" version="2.1.0" targetFramework="net452" />
<package id="Microsoft.Owin.Hosting" version="2.1.0" targetFramework="net452" />
<package id="Microsoft.Owin.Security" version="2.1.0" targetFramework="net452" />
<package id="Microsoft.Owin.SelfHost" version="2.1.0" targetFramework="net452" />
<package id="Newtonsoft.Json" version="6.0.4" targetFramework="net452" />
<package id="Owin" version="1.0" targetFramework="net452" />
</packages>
我这边准备搭建的是一个自承载式的服务,所以引用的的是Microsoft.AspNet.SignalR.SelfHost。如果你准备使用IIS搭建服务,可以直接引用Microsoft.AspNet.SignalR
三、实现操作类
通讯模型Hub(中心模型,或者叫集线器模型),它是一种RPC模式,允许客户端和服务器端各自自定义方法并且相互调用。是SignalR的灵魂所在
3.1 新建类 MyHub.cs、ClientInfo.cs、Helper.cs
我们新建一个MyHub类继承Microsoft.AspNet.SignalR.Hub
再建一个 ClientInfo 类 用于存储客户端信息
再建一个 Helper类 用于实现辅助方法
我们这三个类建在合适的路径下
其中Helper.cs和ClientInfo.cs 现在代码比较简单,我直接贴一下
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SignalRService.Class
{
public static class Helper
{
/// <summary>
/// 获取UUID
/// </summary>
/// <returns></returns>
public static string GetUUID() {
return Guid.NewGuid().ToString();
//产生的UUID为 字母+数字
//return Guid.NewGuid().ToString("N");
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SignalRService.Model
{
public class ClientInfo
{
public string UUID { get; set; }
public string ConnectionId { get; set; }
}
}
3.2 在Program.cs声明公开静态对象
我们在Program.cs 里声明两个静态的对象,因为这两个对象会在全局中使用,其中 MyHub无需实例化,因为在稍后服务启动的时候会对它进行赋值。
using SignalRService.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SignalRService
{
class Program
{
public static List<ClientInfo> ClientInfoList = new List<ClientInfo>();
public static MyHub MyHub;
static void Main(string[] args)
{
}
}
}
3.3 完善MyHub类
我把想说的东西都放注释里,这边就直接贴代码
using Microsoft.AspNet.SignalR;
using SignalRService.Class;
using SignalRService.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SignalRService
{
public class MyHub : Hub
{
/// <summary>
/// 构造时对Program.MyHub赋值
/// </summary>
public MyHub()
{
Program.MyHub = this;
}
/// <summary>
/// 实现推送扫码成功的用户信息的方法
/// </summary>
/// <param name="connectionId"></param>
/// <param name="userInfo"></param>
public void SendUserInfo(string connectionId, string userInfo) {
//调用客户端的 GetUserInfo 方法 返回用户信息
Clients.Client(connectionId).GetUserInfo(userInfo);
}
/// <summary>
/// 实现注册方法
/// </summary>
public void Register()
{
//获取UUID
var UUID = Helper.GetUUID();
//查询用户
var client = Program.ClientInfoList.Where(u => u.ConnectionId == Context.ConnectionId).SingleOrDefault();
if (client == null)
{
client = new ClientInfo()
{
ConnectionId = Context.ConnectionId,
UUID = UUID
};
Program.ClientInfoList.Add(client);
}
else
{
client.UUID = UUID;
}
//调用客户端的 GetUUID 方法 返回UUID
Clients.Client(Context.ConnectionId).GetUUID(Newtonsoft.Json.JsonConvert.SerializeObject(new
{
IsOk = "Y",
Msg = "",
UUID = UUID
}));
//Helper.Log("LoginLog" + System.DateTime.Now.ToString("yyyyMMdd"), "[Register] " + Newtonsoft.Json.JsonConvert.SerializeObject(client));
}
/// <summary>
/// 重写连接事件 目前没实现功能,你可以在这记日志或者干点别的事情
/// </summary>
/// <returns></returns>
public override Task OnConnected()
{
//Helper.Log("ConnectedLog" + System.DateTime.Now.ToString("yyyyMMdd"), "[Connected] [ConnectionId:" + Context.ConnectionId + " IP:" + Helper.GetClientIp(Context) + "]");
return base.OnConnected();
}
/// <summary>
/// 重写连接断开事件
/// </summary>
/// <param name="stopCalled"></param>
/// <returns></returns>
public override Task OnDisconnected(bool stopCalled)
{
//查询用户
var client = Program.ClientInfoList.Where(u => u.ConnectionId == Context.ConnectionId).SingleOrDefault();
//判断用户是否存在,存在则删除
if (client != null)
{
//删除用户
Program.ClientInfoList.Remove(client);
}
//Helper.Log("ConnectedLog" + System.DateTime.Now.ToString("yyyyMMdd"), "[Disconnected] " + Newtonsoft.Json.JsonConvert.SerializeObject(client));
return base.OnDisconnected(stopCalled);
}
}
}
3.4创建启动类
我们需要先引用一个框架库 System.Web.Http
贴代码
using Microsoft.AspNet.SignalR;
using Microsoft.Owin.Cors;
using Owin;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Http;
namespace SignalRService
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
//配置Api;路由
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
app.UseCors(CorsOptions.AllowAll);//设置Api允许跨域
app.MapSignalR(new HubConfiguration() { EnableJSONP = true });//启用SignalR 并启用JSONP(保证跨域数据传输)
app.UseWebApi(config);//启动WebApi
}
}
}
3.5程式入口启动服务
修改Program.cs文件 启动服务
using Microsoft.Owin.Hosting;
using SignalRService.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SignalRService
{
class Program
{
public static List<ClientInfo> ClientInfoList = new List<ClientInfo>();
public static MyHub MyHub;
static void Main(string[] args)
{
WebApp.Start<Startup>("http://localhost:9999");
Console.Write("SignalRService Started!");
Console.ReadKey();
}
}
}
好了,到这一步,我们的服务就搭建好了,跑起来试一下(这里是使用localhost启动的服务,如果想以IP启动,供网域内电脑访问,需要以管理员身份运行)
服务启动没有问题。
我们访问 http://localhost:9999/signalr/hubs 成功,SignalR服务运行正常
3.6创建扫码成功调用API
在上边我们的SignalR 服务已经搭建好了,现在我们还缺少一个扫码成功调用的接口,其实刚刚我们已经配置好了API的路由和启动方法。所以下面我们直接来写接口,根据我们刚刚设置的路由规则【routeTemplate: "api/{controller}/{id}"】,我们需要把接口写在Controller 文件夹下
我在这里建了两个接口:QRLogin用于接收登录请求;SayHello用于测试
继续贴代码:
using System.Web.Http;
namespace SignalRService.Controller
{
public class SayHelloController : ApiController
{
[HttpGet]
public string SayHello() {
return "Hello World!";
}
}
}
using System.Linq;
using System.Web.Http;
namespace SignalRService.Controller
{
public class QRLoginController : ApiController
{
[HttpGet]
public string QRLgoin(string uuid, string userInfo)
{
var client = Program.ClientInfoList.Where(u => u.UUID == uuid).SingleOrDefault();
if (client != null)
{
Program.MyHub.SendUserInfo(client.ConnectionId, userInfo);
Program.ClientInfoList.Remove(client);
return "登录成功,登录结果:" + userInfo;
}
return "登录失败,二维码已过期";
}
}
}
接口写好了,我们重新启动一下程式,成功。
来验证一下接口:http://localhost:9999/api/SayHello
也没有问题。
好的本章就写到这里
四、了解WebAPI和搭建Windows服务
现在我们写的服务是一个控制台应用程序,启动时才能提供服务,还容易被不小心关掉,你可以把它做成Windows服务安装在Windows上
如果你想了解WebAPI和搭建Windows服务,可以参考我以前写的文章
WebApi多版本管控和插件式开发(一)——WebApi服务搭建
五、相关文章
[C#]SignalR实现扫码登录(B/S,C/S)(一)——服务端搭建
[C#]SignalR实现扫码登录(B/S,C/S)(二)——客户端搭建(Web版)
[C#]SignalR实现扫码登录(B/S,C/S)(三)——客户端搭建(WinForm版)
[C#]SignalR实现扫码登录(B/S,C/S)源码