项目分析:

  一个实时的IM坐席系统,客户端和坐席使用IM通信,客户端使用android和ios的app,坐席使用web。

  web端可以保留自己的登录状态,但为防止意外情况的发生(如浏览器异常关闭,断网,断电),对坐席的实时在线状态造成影响,我们在后台跑一个服务,实时向每个坐席发送一个心跳包,当坐席的状态是在线,但是又不能接收到服务端的心跳包的时候,认为该坐席已经被异常下线。

实时通信Signalr

  使用中发现signalr的服务端必须需要 .net frameword4.5及以上版本,对signalr使用了自行托管,使服务端和页面相互独立。

  配置过程:

控制台部分:
1. 用VS创建一个名为 "SignalRSelfHost" 的控制台项目
2. 在程序包管理器控制台,输入如下命令
   Install-Package Microsoft.AspNet.SignalR.SelfHost
3. 输入如下命令:
   Install-Package Microsoft.Owin.Cors
4. 控制台代码:

using Microsoft.AspNet.SignalR;
using Microsoft.Owin.Cors;
using Microsoft.Owin.Hosting;
using Owin;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;

namespace SignalRSelfHost
{
    class Program
    {
        static void Main(string[] args)
        {
            // This will *ONLY* bind to localhost, if you want to bind to all addresses
            // use http://*:8080 to bind to all addresses. 
            // See http://msdn.microsoft.com/en-us/library/system.net.httplistener.aspx 
            // for more information.
            string url = "http://localhost:8080";
            using (WebApp.Start(url))
            {
                Console.WriteLine("Server running on {0}", url);
                Console.ReadLine();
            }
        }
    }
    class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            app.UseCors(CorsOptions.AllowAll);
            app.MapSignalR();
        }
    }
    public class MyHub : Hub
    {
        public static List<User> onlineUsers = new List<User>();
        public void Send(string name, string message)
        {
            Console.WriteLine("client messsage from ["+name+"],message:"+message);
            //Clients.All.addMessage(name, "voip:[" + name+"],message:"+message);
            var user = onlineUsers.Where(u => u.Voip == name).FirstOrDefault();
            Clients.Client(user.ConnectionId).addMessage(user.ConnectionId, "voip:[" + name + "],message:" + message);
        }

        public void LoginIn(string voip) {
            var user = onlineUsers.Where(u => u.Voip == voip).FirstOrDefault();
            if (user == null)
            {
                string connId = Context.ConnectionId;
                user = new User
                {
                    Voip = voip,
                    Second = 0,
                    ConnectionId=connId
                };
                onlineUsers.Add(user);
                Console.WriteLine(user.Voip + "上线了");
                //Console.ReadLine();
                user.HeartBeatAction += () =>
                {
                    SendHeartBeat(connId);
                };
                user.LogoutAction += () =>
                {
                    LoginOut(voip);
                };
            }
            else {
                user.HeartBeatAction += () =>
                {
                    SendHeartBeat(user.ConnectionId);
                };
                user.LogoutAction += () =>
                {
                    LoginOut(user.Voip);
                };
                Console.WriteLine(user.Voip + "已经在线了");
                Console.ReadLine();
            }
           
        }

        /// <summary>
        /// 发送心跳包
        /// </summary>
        /// <param name="voip"></param>
        private void SendHeartBeat(string connid)
        {
            Clients.Client(connid).recieveHeartBeat(connid);
           // Clients.All.recieveHeartBeat(voip);
        }

        /// <summary>
        /// 接收心跳包
        /// </summary>
        /// <param name="id"></param>
        public void RecieveHeartBeat(string connid)
        {
            var user = onlineUsers.Where(u => u.ConnectionId == connid).FirstOrDefault();
            if (user == null) return;
            user.Second = 0;

        }

        /// <summary>
        /// 用户主动下线
        /// </summary>
        /// <param name="voip"></param>
        public void LoginOut(string voip)
        {

            var user = onlineUsers.Where(u => u.Voip == voip).FirstOrDefault();
            Console.WriteLine(user.Voip + " 下线了");
           
            onlineUsers.Remove(user);
            
        }

        private void UserLoginOut(string voip)
        {
            LoginOut(voip);
        }
    }

    public class User
    {
        public string  Voip { get; set; }
        public int  Second { get; set; }
        public string ConnectionId { get; set; }

        private readonly Timer timer;//定时器
        /// <summary>
        /// 间隔秒数
        /// </summary>
        private int During=30;

        /// <summary>
        /// 掉线后的操作
        /// </summary>
        public event Action LogoutAction;

        /// <summary>
        /// 发送心跳包的动作
        /// </summary>
        public event Action HeartBeatAction;
        public User() {
            Second = 0;
            if (timer == null) {
                timer = new Timer(1000);
            }
            timer.Start();//计时器启动
            timer.Elapsed += (sender, args) =>
            {
                Second++;
                //每5s发送一次心跳包
                if (Second % 5 == 0) {
                    if (HeartBeatAction != null) {
                        HeartBeatAction();
                    }
                }
                if (Second >= During) {
                    timer.Stop();
                    timer.Dispose();
                    //用户30s无心跳包应答,则视为掉线,会抛出事件,然后处理用户掉线动作。
                    if (LogoutAction != null)
                    {
                        LogoutAction();
                    }
                }
            };

            
        }

    }
}
View Code

相关文章: