No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.
 
 
 

212 líneas
8.3 KiB

  1. using Microsoft.Extensions.Logging;
  2. using System;
  3. using System.Collections.Concurrent;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using DotNetty.Transport.Channels;
  7. using JT808.DotNetty.Abstractions;
  8. using JT808.DotNetty.Core.Metadata;
  9. using JT808.DotNetty.Core.Interfaces;
  10. namespace JT808.DotNetty.Core
  11. {
  12. /// <summary>
  13. /// JT808 Tcp会话管理
  14. /// </summary>
  15. public class JT808TcpSessionManager
  16. {
  17. private readonly ILogger<JT808TcpSessionManager> logger;
  18. private readonly IJT808SessionPublishing jT808SessionPublishing;
  19. public JT808TcpSessionManager(
  20. IJT808SessionPublishing jT808SessionPublishing,
  21. ILoggerFactory loggerFactory)
  22. {
  23. this.jT808SessionPublishing = jT808SessionPublishing;
  24. logger = loggerFactory.CreateLogger<JT808TcpSessionManager>();
  25. }
  26. private ConcurrentDictionary<string, JT808TcpSession> SessionIdDict = new ConcurrentDictionary<string, JT808TcpSession>(StringComparer.OrdinalIgnoreCase);
  27. public int SessionCount
  28. {
  29. get
  30. {
  31. return SessionIdDict.Count;
  32. }
  33. }
  34. public JT808TcpSession GetSession(string terminalPhoneNo)
  35. {
  36. if (string.IsNullOrEmpty(terminalPhoneNo))
  37. return default;
  38. if (SessionIdDict.TryGetValue(terminalPhoneNo, out JT808TcpSession targetSession))
  39. {
  40. return targetSession;
  41. }
  42. else
  43. {
  44. return default;
  45. }
  46. }
  47. public void Heartbeat(string terminalPhoneNo)
  48. {
  49. if (string.IsNullOrEmpty(terminalPhoneNo)) return;
  50. if (SessionIdDict.TryGetValue(terminalPhoneNo, out JT808TcpSession oldjT808Session))
  51. {
  52. oldjT808Session.LastActiveTime = DateTime.Now;
  53. SessionIdDict.TryUpdate(terminalPhoneNo, oldjT808Session, oldjT808Session);
  54. }
  55. }
  56. public bool TrySend(string terminalPhoneNo, byte[] data, out string message)
  57. {
  58. bool isSuccessed;
  59. var session = GetSession(terminalPhoneNo);
  60. if (session != null)
  61. {
  62. //判断转发数据是下发不了消息的
  63. if (SessionIdDict.Select(s=>s.Value).Count(c => c.Channel.Id == session.Channel.Id) > 1)
  64. {
  65. isSuccessed = false;
  66. message = "not support transmit data send.";
  67. }
  68. else
  69. {
  70. session.Channel.WriteAndFlushAsync(new JT808Response(data));
  71. isSuccessed = true;
  72. message = "";
  73. }
  74. }
  75. else
  76. {
  77. isSuccessed = false;
  78. message = "offline";
  79. }
  80. return isSuccessed;
  81. }
  82. public bool TrySend(string terminalPhoneNo, IJT808Reply reply, out string message)
  83. {
  84. bool isSuccessed;
  85. var session = GetSession(terminalPhoneNo);
  86. if (session != null)
  87. {
  88. //判断转发数据是下发不了消息的
  89. if (SessionIdDict.Select(s => s.Value).Count(c => c.Channel.Id == session.Channel.Id) > 1)
  90. {
  91. isSuccessed = false;
  92. message = "not support transmit data send.";
  93. }
  94. else
  95. {
  96. session.Channel.WriteAndFlushAsync(reply);
  97. isSuccessed = true;
  98. message = "";
  99. }
  100. }
  101. else
  102. {
  103. isSuccessed = false;
  104. message = "offline";
  105. }
  106. return isSuccessed;
  107. }
  108. public void TryAdd(string terminalPhoneNo,IChannel channel)
  109. {
  110. // 解决了设备号跟通道绑定到一起,不需要用到通道本身的SessionId
  111. // 不管设备下发更改了设备终端号,只要是没有在内存中就当是新的
  112. // 存在的问题:
  113. // 1.原先老的如何销毁
  114. // 2.这时候用的通道是相同的,设备终端是不同的
  115. // 当设备主动或者服务器断开以后,可以释放,这点内存忽略不计,况且更改设备号不是很频繁。
  116. //修复第一次通过转发过来的数据,再次通过直连后通道没有改变导致下发不成功,所以每次进行通道的更新操作。
  117. if (SessionIdDict.TryGetValue(terminalPhoneNo, out JT808TcpSession oldJT808Session))
  118. {
  119. oldJT808Session.LastActiveTime = DateTime.Now;
  120. oldJT808Session.Channel = channel;
  121. SessionIdDict.TryUpdate(terminalPhoneNo, oldJT808Session, oldJT808Session);
  122. }
  123. else
  124. {
  125. JT808TcpSession jT808TcpSession = new JT808TcpSession(channel, terminalPhoneNo);
  126. if (SessionIdDict.TryAdd(terminalPhoneNo, jT808TcpSession))
  127. {
  128. //使用场景:
  129. //部标的超长待机设备,不会像正常的设备一样一直连着,可能10几分钟连上了,然后发完就关闭连接,
  130. //这时候想下发数据需要知道设备什么时候上线,在这边做通知最好不过了。
  131. //有设备关联上来可以进行通知 例如:使用Redis发布订阅
  132. jT808SessionPublishing.PublishAsync(JT808Constants.SessionOnline, jT808TcpSession.TerminalPhoneNo);
  133. }
  134. }
  135. }
  136. public JT808TcpSession RemoveSession(string terminalPhoneNo)
  137. {
  138. //设备离线可以进行通知
  139. //使用Redis 发布订阅
  140. if (string.IsNullOrEmpty(terminalPhoneNo)) return default;
  141. if (!SessionIdDict.TryGetValue(terminalPhoneNo, out JT808TcpSession jT808Session))
  142. {
  143. return default;
  144. }
  145. // 处理转发过来的是数据 这时候通道对设备是1对多关系,需要清理垃圾数据
  146. //1.用当前会话的通道Id找出通过转发过来的其他设备的终端号
  147. var terminalPhoneNos = SessionIdDict.Where(w => w.Value.Channel.Id == jT808Session.Channel.Id).Select(s => s.Key).ToList();
  148. //2.存在则一个个移除
  149. if (terminalPhoneNos.Count > 1)
  150. {
  151. //3.移除包括当前的设备号
  152. foreach (var key in terminalPhoneNos)
  153. {
  154. SessionIdDict.TryRemove(key, out JT808TcpSession jT808SessionRemove);
  155. }
  156. string nos = string.Join(",", terminalPhoneNos);
  157. logger.LogInformation($">>>{terminalPhoneNo}-{nos} 1-n Session Remove.");
  158. jT808SessionPublishing.PublishAsync(JT808Constants.SessionOffline, nos);
  159. return jT808Session;
  160. }
  161. else
  162. {
  163. if (SessionIdDict.TryRemove(terminalPhoneNo, out JT808TcpSession jT808SessionRemove))
  164. {
  165. logger.LogInformation($">>>{terminalPhoneNo} Session Remove.");
  166. jT808SessionPublishing.PublishAsync(JT808Constants.SessionOffline,terminalPhoneNo);
  167. return jT808SessionRemove;
  168. }
  169. else
  170. {
  171. return default;
  172. }
  173. }
  174. }
  175. public void RemoveSessionByChannel(IChannel channel)
  176. {
  177. //设备离线可以进行通知
  178. //使用Redis 发布订阅
  179. var terminalPhoneNos = SessionIdDict.Where(w => w.Value.Channel.Id == channel.Id).Select(s => s.Key).ToList();
  180. if (terminalPhoneNos.Count > 0)
  181. {
  182. foreach (var key in terminalPhoneNos)
  183. {
  184. SessionIdDict.TryRemove(key, out JT808TcpSession jT808SessionRemove);
  185. }
  186. string nos = string.Join(",", terminalPhoneNos);
  187. logger.LogInformation($">>>{nos} Channel Remove.");
  188. jT808SessionPublishing.PublishAsync(JT808Constants.SessionOffline, nos);
  189. }
  190. }
  191. public IEnumerable<JT808TcpSession> GetAll()
  192. {
  193. return SessionIdDict.Select(s => s.Value).ToList();
  194. }
  195. }
  196. }