SuperSoket的三大对象:
- Session: 每一个用户连接就是一个Session
- AppServer: Socket服务器实例
- 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)
{
}
}