当我们的应用中客户端与AS之间是通过Tcp进行通信的时候,通常,应用也要求管理所有在线的用户。这种管理至少包含以下几点:
(1)当用户上线时,记录上线时间
(2)当用户请求服务时,记录请求服务的时间、服务的类型、本次服务下载的数据量
(3)当用户下线时,记录下线时间。并把本次用户登录、请求服务过程中的所有信息持久化保存(如记录到数据库)

在ESFramework中,实现这种管理的是ITcpUserManager组件,通常,该组件由AS使用(因为在AS、FS、IRAS中只有AS是必须要求提供用户管理功能的)。除了上述功能外,ITcpUserManager组件还提供:
(1)控制UI上用户实时状态的显示
(2)对用户意外掉线进行定时检查
(3)提供用户是否在线的查询
(4)提供通过用户ID获取Tcp连接的查询

为什么ITcpUserManager组件需要提供这些功能?为什么有些功能不在其他组件中提供、而非在ITcpUserManager组件之中不可?这些问题都需要你进行深入的思考,就像我们当初所做的那样。ITcpUserManager组件一开始并不是这样的设计,在经过几次重构改善后才是这个样子,现在这个设计已经稳定下来了。在思考的过程中,你需要记住一点:ITcpUserManager组件是用户状态最全面、最实时发布的地方。
ITcpUserManager接口定义如下:

1 publicinterfaceITcpUserManager
2{
3voidStart();
4voidStop();
5
6voidDisposeOneUser(stringuserID,DisconnectedCausecause);
7voidDisposeOneConnection(intconnectID,DisconnectedCausecause);
8voidServiceCommited(intconnectID,stringuserID,intserviceKey,intdataCount);
9voidActivateUser(stringuserID);
10
11boolIsUserOnLine(stringuserID);
12intGetUserConnectID(stringuserID);//如果不在线,返回-1
13string[]GetOnlineUserList();//IList中是在线的userID
14
15eventCbForTcpUserDisconnSomeOneDisconnected;
16eventCbSimpleStrSomeOneConnected;//UserID
17eventCbSimpleRestarted;
18
19ITcpUserDisplayerTcpUserDisplayer{set;}//控制UI
20IUserTaskReporterUserTaskReporter{set;}//持久化服务记录清单
21intOnLineCheckSpan{get;set;}//OnLineCheckSpan单位为分钟,如果不使用定时检查,则onLineCheckSpan为-1
22
23}

那么,ITcpUserManager组件从哪里获取用户的这些实时信息了?
(1) 网络插件的ServiceCommited事件、网络插件的SomeOneDisConnected事件
(2) 定时检查器的掉线事件
(3) 基本请求处理者的RequestWithoutRespondArrived事件=》**定时检查器中的某个用户
(4) Logout请求
上述的几个信息来源决定了ITcpUserManager组件与其他几个组件的依赖关系。综合上面所有的,下面是ITcpUserManager组件与其他主要组件的依赖关系图:
ESFramework介绍之(18)―― Tcp用户管理器组件
这些依赖关系的组装是由TcpUserManagerBridge类完成的,看看它的代码就知道怎么回事了:

ESFramework介绍之(18)―― Tcp用户管理器组件ESFramework介绍之(18)―― Tcp用户管理器组件TcpUserManagerBridge
<!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>-->1publicclassTcpUserManagerBridge
2{
3privateIBasicRequestDealerbasicRequestDealer=null;
4privateITcpUserManagertcpUserManager=null;
5privateITcptheTcp=null;
6privateIContractHelpercontractHelper=null;
7privatebooldirectSeviceCommitEnabled=false;
8
9
10publicTcpUserManagerBridge()
11{
12
13}
14
15publicvoidInitialize()
16{
17this.theTcp.ServiceCommitted+=newCallBackRespond(theTcp_ServiceCommitted);
18this.theTcp.SomeOneDisConnected+=newCallBackDisconnect(theTcp_SomeOneDisConnected);
19this.basicRequestDealer.RequestWithoutRespondArrived+=newCbSimpleStr(basicRequestDealer_RequestWithoutRespondArrived);
20this.basicRequestDealer.SomeOneLogout+=newCbSimpleStr(basicRequestDealer_SomeOneLogout);
21this.theTcp.ServiceDirectCommitted+=newCallBackRespond(theTcp_ServiceDirectCommitted);
22}
23
24#regionproperty
25publicITcpUserManagerTcpUserManager
26{
27set
28{
29this.tcpUserManager=value;
30}
31}
32
33publicITcpTcp
34{
35set
36{
37this.theTcp=value;
38}
39}
40
41publicIBasicRequestDealerBasicRequestDealer
42{
43set
44{
45this.basicRequestDealer=value;
46}
47}
48
49publicIContractHelperContractHelper
50{
51set
52{
53this.contractHelper=value;
54}
55}
56
57publicboolDirectSeviceCommitEnabled
58{
59set
60{
61this.directSeviceCommitEnabled=value;
62}
63}
64#endregion
65
66privatevoidtheTcp_SomeOneDisConnected(intConnectID,DisconnectedCausecause)
67{
68this.tcpUserManager.DisposeOneConnection(ConnectID,cause);
69}
70
71privatevoidbasicRequestDealer_RequestWithoutRespondArrived(stringuserID)
72{
73this.tcpUserManager.ActivateUser(userID);
74}
75
76privatevoidbasicRequestDealer_SomeOneLogout(stringuserID)
77{
78this.tcpUserManager.DisposeOneUser(userID,DisconnectedCause.Logoff);
79}
80
81privatevoidtheTcp_ServiceCommitted(intconnectID,NetMessagemsg)
82{
83this.tcpUserManager.ServiceCommited(connectID,msg.Header.UserID,msg.Header.ServiceKey,msg.Header.MessageBodyLength+this.contractHelper.MessageHeaderLength);
84}
85
86privatevoidtheTcp_ServiceDirectCommitted(intconnectID,NetMessagemsg)
87{
88if(this.directSeviceCommitEnabled)
89{
90this.tcpUserManager.ServiceCommited(connectID,msg.Header.UserID,msg.Header.ServiceKey,msg.Header.MessageBodyLength+this.contractHelper.MessageHeaderLength);
91}
92
93}
94}

TcpUserManagerBridge相当与一种桥接模式(可从这里了解更多),使得组件与组件之间的耦合关系达到最小。

在ESFramework内部,很多地方需要使用到ITcpUserManager组件,比如P2PMessageDealer组件中、ToLocalClientSender组件中等等,它们主要通过ITcpUserManager组件获取某用户的Tcp连接、或查看某用户是否在线。

最后提一下ITcpUserManager用到的两个组件:IUserOnLineChecker和IUserTaskReporter。
IUserOnLineChecker用于实现前面文章中谈到的“定时check消息”机制,当某个用户在指定的时间内没有任何消息发给服务器时,IUserOnLineChecker触发SomeConnectionTimeOuted事件通知该用户已经掉线。IUserOnLineChecker接口定义非常简单:

1 publicinterfaceIUserOnLineChecker
2{
3voidStart();
4voidStop();
5
6voidRegisterOrActivateUser(stringuserID);
7voidUnregisterUser(stringuserID);//被外界调用UnregisterUser时,不触发CheckSomeOneDisConnected事件
8
9intCheckSpan{get;set;}//Minutes
10
11eventCbSimpleStrSomeConnectionTimeOuted;//仅是当定时检查出用户掉线时才触发
12}

前面我们提到,当用户下线(掉线)时,需要把该用户本次登录的服务记录永久存储,这个功能是由IUserTaskReporter来完成的。

1 publicinterfaceIUserTaskReporter
2{
3voidRecordTaskList(ITaskMainRecordmainRecord);
4ITaskMainRecordCreateRudeTaskMainRecord();
5ITaskDetailRecordCreateRudeTaskDetailRecord();
6}
7
8publicinterfaceITaskMainRecord
9{
10stringUserID{get;set;}
11intTotalDataCount{get;set;}
12DateTimeTimeLogon{get;set;}
13DateTimeTimeLogout{get;set;}
14intRequestCount{get;set;}
15
16ArrayListDetails{get;set;}//details中为ITaskDetailRecord
17}
18
19publicinterfaceITaskDetailRecord
20{
21intServiceKey{get;set;}
22intDataCount{get;set;}
23DateTimeRequestTime{get;set;}
24}

持久化的具体介质由你的应用决定。如果你使用数据库来存储,则可以将ITaskMainRecord和ITaskDetailRecord在数据库中设计成主从表关系。


上一篇文章:ESFramework介绍之(17)―― 支持漫游用户和跨区域功能请求

转到:ESFramework 可复用的通信框架(序)




相关文章:

  • 2022-03-09
  • 2021-08-07
  • 2021-05-07
  • 2021-11-28
  • 2021-10-03
猜你喜欢
  • 2021-08-03
  • 2021-10-23
  • 2022-02-06
  • 2021-10-02
相关资源
相似解决方案