Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.
 
 
 

145 рядки
5.8 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. namespace JT808.DotNetty.Core
  10. {
  11. /// <summary>
  12. /// JT808 Tcp会话管理
  13. /// </summary>
  14. public class JT808TcpSessionManager
  15. {
  16. private readonly ILogger<JT808TcpSessionManager> logger;
  17. private readonly IJT808SessionPublishing jT808SessionPublishing;
  18. public JT808TcpSessionManager(
  19. IJT808SessionPublishing jT808SessionPublishing,
  20. ILoggerFactory loggerFactory)
  21. {
  22. this.jT808SessionPublishing = jT808SessionPublishing;
  23. logger = loggerFactory.CreateLogger<JT808TcpSessionManager>();
  24. }
  25. private ConcurrentDictionary<string, JT808TcpSession> SessionIdDict = new ConcurrentDictionary<string, JT808TcpSession>(StringComparer.OrdinalIgnoreCase);
  26. public int SessionCount
  27. {
  28. get
  29. {
  30. return SessionIdDict.Count;
  31. }
  32. }
  33. public JT808TcpSession GetSession(string terminalPhoneNo)
  34. {
  35. if (string.IsNullOrEmpty(terminalPhoneNo))
  36. return default;
  37. if (SessionIdDict.TryGetValue(terminalPhoneNo, out JT808TcpSession targetSession))
  38. {
  39. return targetSession;
  40. }
  41. else
  42. {
  43. return default;
  44. }
  45. }
  46. public void Heartbeat(string terminalPhoneNo)
  47. {
  48. if (string.IsNullOrEmpty(terminalPhoneNo)) return;
  49. if (SessionIdDict.TryGetValue(terminalPhoneNo, out JT808TcpSession oldjT808Session))
  50. {
  51. oldjT808Session.LastActiveTime = DateTime.Now;
  52. SessionIdDict.TryUpdate(terminalPhoneNo, oldjT808Session, oldjT808Session);
  53. }
  54. }
  55. public void TryAdd(JT808TcpSession appSession)
  56. {
  57. // 解决了设备号跟通道绑定到一起,不需要用到通道本身的SessionId
  58. // 不管设备下发更改了设备终端号,只要是没有在内存中就当是新的
  59. // 存在的问题:
  60. // 1.原先老的如何销毁
  61. // 2.这时候用的通道是相同的,设备终端是不同的
  62. // 当设备主动或者服务器断开以后,可以释放,这点内存忽略不计,况且更改设备号不是很频繁。
  63. if (SessionIdDict.TryAdd(appSession.TerminalPhoneNo, appSession))
  64. {
  65. //使用场景:
  66. //部标的超长待机设备,不会像正常的设备一样一直连着,可能10几分钟连上了,然后发完就关闭连接,
  67. //这时候想下发数据需要知道设备什么时候上线,在这边做通知最好不过了。
  68. //有设备关联上来可以进行通知 例如:使用Redis发布订阅
  69. jT808SessionPublishing.PublishAsync(JT808Constants.SessionOnline, appSession.TerminalPhoneNo);
  70. }
  71. }
  72. public JT808TcpSession RemoveSession(string terminalPhoneNo)
  73. {
  74. //设备离线可以进行通知
  75. //使用Redis 发布订阅
  76. if (string.IsNullOrEmpty(terminalPhoneNo)) return default;
  77. if (!SessionIdDict.TryGetValue(terminalPhoneNo, out JT808TcpSession jT808Session))
  78. {
  79. return default;
  80. }
  81. // 处理转发过来的是数据 这时候通道对设备是1对多关系,需要清理垃圾数据
  82. //1.用当前会话的通道Id找出通过转发过来的其他设备的终端号
  83. var terminalPhoneNos = SessionIdDict.Where(w => w.Value.Channel.Id == jT808Session.Channel.Id).Select(s => s.Key).ToList();
  84. //2.存在则一个个移除
  85. if (terminalPhoneNos.Count > 1)
  86. {
  87. //3.移除包括当前的设备号
  88. foreach (var key in terminalPhoneNos)
  89. {
  90. SessionIdDict.TryRemove(key, out JT808TcpSession jT808SessionRemove);
  91. }
  92. string nos = string.Join(",", terminalPhoneNos);
  93. logger.LogInformation($">>>{terminalPhoneNo}-{nos} 1-n Session Remove.");
  94. jT808SessionPublishing.PublishAsync(JT808Constants.SessionOffline, nos);
  95. return jT808Session;
  96. }
  97. else
  98. {
  99. if (SessionIdDict.TryRemove(terminalPhoneNo, out JT808TcpSession jT808SessionRemove))
  100. {
  101. logger.LogInformation($">>>{terminalPhoneNo} Session Remove.");
  102. jT808SessionPublishing.PublishAsync(JT808Constants.SessionOffline,terminalPhoneNo);
  103. return jT808SessionRemove;
  104. }
  105. else
  106. {
  107. return default;
  108. }
  109. }
  110. }
  111. public void RemoveSessionByChannel(IChannel channel)
  112. {
  113. //设备离线可以进行通知
  114. //使用Redis 发布订阅
  115. var terminalPhoneNos = SessionIdDict.Where(w => w.Value.Channel.Id == channel.Id).Select(s => s.Key).ToList();
  116. if (terminalPhoneNos.Count > 0)
  117. {
  118. foreach (var key in terminalPhoneNos)
  119. {
  120. SessionIdDict.TryRemove(key, out JT808TcpSession jT808SessionRemove);
  121. }
  122. string nos = string.Join(",", terminalPhoneNos);
  123. logger.LogInformation($">>>{nos} Channel Remove.");
  124. jT808SessionPublishing.PublishAsync(JT808Constants.SessionOffline, nos);
  125. }
  126. }
  127. public IEnumerable<JT808TcpSession> GetAll()
  128. {
  129. return SessionIdDict.Select(s => s.Value).ToList();
  130. }
  131. }
  132. }