C#/.NET SuperSocket的使用

首先安装:SuperSocket.Engine

SuperSoket的三大对象:

  1. Session: 每一个用户连接就是一个Session
  2. AppServer: Socket服务器实例
  3. Commands: 客户端向服务器发送消息的命令集合

首先在配置文件加入如下配置

<configSections>
  <section name="superSocket" type="SuperSocket.SocketEngine.Configuration.SocketServiceConfig, SuperSocket.SocketEngine"/>
</configSections>
<superSocket>
  <servers>
    <server name="ChatSocket" textEncoding="gb2312"
            serverType="XT.SocketService.AppServer.ChatServer, XT.SocketService"
            ip="Any" port="2020"
            maxConnectionNumber="1000">
    </server>
    <!-- 可以配置多个Server-->
  </servers>
</superSocket>

AppServer代码如下

[AuthorisizeFilter]
public class ChatServer:AppServer<ChatSession>
{
    protected override bool Setup(IRootConfig rootConfig, IServerConfig config)
    {
        Console.WriteLine("准备读取配置文件。。。。");
        return base.Setup(rootConfig, config);
    }

    protected override void OnStarted()
    {
        Console.WriteLine("Chat服务启动。。。");
        base.OnStarted();
    }

    protected override void OnStopped()
    {
        Console.WriteLine("Chat服务停止。。。");
        base.OnStopped();
    }

    /// <summary>
    /// 新的连接
    /// </summary>
    /// <param name="session"></param>
    protected override void OnNewSessionConnected(ChatSession session)
    {
        Console.WriteLine($"Chat服务新加入的连接:{session.LocalEndPoint.Address.ToString()}");
        base.OnNewSessionConnected(session);
    }
}

Session代码如下

/// <summary>
/// 表示用户连接
/// </summary>
//[AuthorisizeFilter]
public class ChatSession : AppSession<ChatSession>
{
    public string Id { get; set; }

    public string PassWord { get; set; }

    public bool IsLogin { get; set; }

    public DateTime LoginTime { get; set; }

    public DateTime LastHbTime { get; set; }

    public bool IsOnline
    { 
        get
        {
            return this.LastHbTime.AddSeconds(10) > DateTime.Now;
        }
    }

    /// <summary>
    /// 消息发送
    /// </summary>
    /// <param name="message"></param>
    public override void Send(string message)
    {
        Console.WriteLine($"准备发送给{this.Id}:{message}");
        base.Send(message.Format());
    }

    protected override void OnSessionStarted()
    {
        this.Send("Welcome to SuperSocket Chat Server");
    }

    protected override void OnInit()
    {
        this.Charset = Encoding.GetEncoding("gb2312");
        base.OnInit();
    }

    protected override void HandleUnknownRequest(StringRequestInfo requestInfo)
    {
        Console.WriteLine("收到命令:" + requestInfo.Key.ToString());
        this.Send("不知道如何处理 " + requestInfo.Key.ToString() + " 命令");
    }

    /// <summary>
    /// 异常捕捉
    /// </summary>
    /// <param name="e"></param>
    protected override void HandleException(Exception e)
    {
        this.Send($"\n\r异常信息:{ e.Message}");
        //base.HandleException(e);
    }

    /// <summary>
    /// 连接关闭
    /// </summary>
    /// <param name="reason"></param>
    protected override void OnSessionClosed(CloseReason reason)
    {
        Console.WriteLine("链接已关闭。。。");
        base.OnSessionClosed(reason);
    }
}

Commands代码如下 : 客户端发送消息命令 Check 1 123456
Check 代表类名 ,1代表session.id(会话ID),1代表session.PassWord (会话密码)

public class Check : CommandBase<ChatSession, StringRequestInfo>
{
    public override void ExecuteCommand(ChatSession session, StringRequestInfo requestInfo)
    {
        if (requestInfo.Parameters != null && requestInfo.Parameters.Length == 2)
        {
            ChatSession oldSession = session.AppServer.GetAllSessions().FirstOrDefault(a => requestInfo.Parameters[0].Equals(a.Id));
            if (oldSession != null) // 说过之前有用户用这个Id 登录过
            {
                oldSession.Send("您的账号已经在他处登录,您已经被踢下线了");
                oldSession.Close();
            }

            #region 这里就可以连接数据库进行数据验证做登录
            ///---------------------
            #endregion
            session.Id = requestInfo.Parameters[0];
            session.PassWord = requestInfo.Parameters[1];
            session.IsLogin = true;
            session.LoginTime = DateTime.Now;

            session.Send("登录成功");

            { // 获取当前登录用户的离线消息 
                ChatDataManager.SendLogin(session.Id, c =>
                { 
                    session.Send($"{c.FromId} 给你发送消息:{c.Message} {c.Id}");
                });

            }
        }
        else
        {
            session.Send("参数错误");
        }
    }
}

离线消息存储的相关类

public class ChatDataManager
{
    /// <summary>
    /// key是用户id
    /// List 这个用户的全部消息
    /// </summary>
    private static Dictionary<string, List<ChatModel>> Dictionary = new Dictionary<string, List<ChatModel>>();

    public static void Add(string userId, ChatModel model)
    {
        if (Dictionary.ContainsKey(userId))
        {
            Dictionary[userId].Add(model);
        }
        else
        {
            Dictionary[userId] = new List<ChatModel>() { model };
        }
    }
    public static void Remove(string userId, string modelId)
    {
        if (Dictionary.ContainsKey(userId))
        {
            Dictionary[userId] = Dictionary[userId].Where(m => m.Id != modelId).ToList();
        }
    }

    public static void SendLogin(string userId, Action<ChatModel> action)
    {
        if (Dictionary.ContainsKey(userId))
        {
            foreach (var item in Dictionary[userId])
            {
                action.Invoke(item);
                item.State = 1;
            }
        }
    }
}
/// <summary>
/// 一条消息的记录
/// </summary>
public class ChatModel
{
    /// <summary>
    /// 每条分配个唯一Id
    /// </summary>
    public string Id { get; set; }
    /// <summary>
    /// 来源编号
    /// </summary>
    public string FromId { get; set; }
    /// <summary>
    /// 目标编号
    /// </summary>
    public string ToId { get; set; }
    /// <summary>
    /// 消息内容
    /// </summary>
    public string Message { get; set; }
    /// <summary>
    /// 消息时间
    /// </summary>
    public DateTime CreateTime { get; set; }
    /// <summary>
    /// 消息状态  0未发送 1已发送待确认  2确认收到
    /// </summary>
    public int State { get; set; }
}

基本使用获取离线消息

public class Chat : CommandBase<ChatSession, StringRequestInfo>
{
   public override void ExecuteCommand(ChatSession session, StringRequestInfo requestInfo)
   {
       // 还是传递两个参数  1、 要发给谁 ToId    2、消息内容
       if (requestInfo.Parameters != null && requestInfo.Parameters.Length == 2)
       {
           string toId = requestInfo.Parameters[0];
           string message = requestInfo.Parameters[1];
           ChatSession toSession = session.AppServer.GetAllSessions().FirstOrDefault(a => toId.Equals(a.Id));
            
           string modelId = Guid.NewGuid().ToString();
           if (toSession != null) // 说过之前有用户用这个Id 登录过
           {
               toSession.Send($"{session.Id} 给你发消息:{message} {modelId}");
               ChatDataManager.Add(toId, new ChatModel()
               {
                   FromId = session.Id,
                   ToId = toId,
                   Message = message,
                   Id = modelId,
                   State = 1,// 待确认
                   CreateTime = DateTime.Now
               });
           }
           else
           {
               ChatDataManager.Add(toId, new ChatModel()
               {
                   FromId = session.Id,
                   ToId = toId,
                   Message = message,
                   Id = modelId,
                   State = 0,// 未发送
                   CreateTime = DateTime.Now
               }); 
               session.Send("消息未发送成功");
           }
       }
       else
       {
           session.Send("参数错误");
       }
   }
}
public class Confirm : CommandBase<ChatSession, StringRequestInfo>
{
    public override void ExecuteCommand(ChatSession session, StringRequestInfo requestInfo)
    { 
        if (requestInfo.Parameters != null && requestInfo.Parameters.Length == 1)
        {
            string modelId = requestInfo.Parameters[0]; 
            Console.WriteLine($"用户{session.Id} 已确认,收到消息{modelId}");
            ChatDataManager.Remove(session.Id, modelId);
        }
        else
        {
            session.Send("参数错误");
        }
    }
}

心跳检测:主要就是定时发送消息,没接到消息就发起重连

public class HB : CommandBase<ChatSession, StringRequestInfo>
{
    public override void ExecuteCommand(ChatSession session, StringRequestInfo requestInfo)
    {
        if (requestInfo.Parameters != null && requestInfo.Parameters.Length == 1)
        {
            if ("R".Equals(requestInfo.Parameters[0]))
            {
                session.LastHbTime = DateTime.Now;
                session.Send("R");
            }
            else
            {
                session.Send("参数错误");
            }
        }
        else
        {
            session.Send("参数错误");
        }
    }
}

SuperSocket的AOP的使用

class AuthorisizeFilterAttribute : CommandFilterAttribute
{
     
    public override void OnCommandExecuting(CommandExecutingContext commandContext)
    { 
        ChatSession session = (ChatSession)commandContext.Session;
        string command = commandContext.CurrentCommand.Name; 
        if (!session.IsLogin)
        {
            if (!command.Equals("Check"))
            {
                session.Send($"请先登录,再操作");
                commandContext.Cancel = true;
            }
            else
            {

            }
        }
        else if (!session.IsOnline)
        {
            session.LastHbTime = DateTime.Now;
        }

    }

    public override void OnCommandExecuted(CommandExecutingContext commandContext)
    {
        
    } 
}
发布了169 篇原创文章 · 获赞 136 · 访问量 9213

猜你喜欢

转载自blog.csdn.net/weixin_41181778/article/details/103994362