using Assets.Scenes.Ride.Scripts.Model; using Assets.Scenes.Ride.Scripts.Network; using Assets.Scripts; using Assets.Scripts.Apis; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.IO.Compression; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Assets.Scenes.Ride.Scripts { public class MapUDPService { private static IService _udpService; //private static UdpClient udpClient; //private static IPEndPoint iPEndPoint; private static bool isExit = false; //private static Task task = null; //private static CancellationTokenSource cts { get; set; } private static CancellationToken ct { get; set; } //private static List onlineUsers { get; set; } = new List(); private static System.Timers.Timer heartbeat { get; set; } public static bool Pause { get; set; } = false; private static OnlineUserHelper _onlineUserHelper; private static OnlineUserHelper onlineUserHelper { get { if (_onlineUserHelper == null) { _onlineUserHelper = new OnlineUserHelper(); } return _onlineUserHelper; } } private static DateTime LastActiveTime = DateTime.Now; private static List msgs = new List(); /// /// 初始化(如果UDP服务未启动,则会已没5秒一次不断尝试,直到连接上为止,前端不需要考虑任何UDP连接的事情) /// public static void Init(int competitionid = 0, bool isWatch = false) { try { Competitionid = competitionid; IsWatch = isWatch; //var ddd = new MapWorkoutService().GetRealOnlineUserInfo(new List { 6 }); isExit = false; Pause = false; if (_udpService == null) { _udpService = new TcpService1(); _udpService.Start(list => { LastActiveTime = DateTime.Now; //onlineUserHelper.RemoveExceptionData(list); msgs = list; onlineUserHelper.SetOnlineUser(msgs); }); heartbeat = new System.Timers.Timer(); heartbeat.Interval = 1000; heartbeat.AutoReset = true; heartbeat.Enabled = true; heartbeat.Elapsed += Heartbeat_Elapsed; } } catch (Exception e) { Trace.WriteLine("UDP:" + e.Message); } finally { //发送一条心跳包 //SendHeartbeat(); //告诉tcp服务器gzip } } private static void Heartbeat_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { var now = DateTime.Now; //超过一秒没有收到消息 Console.WriteLine($"Heartbeat_Elapsed{(now - LastActiveTime).TotalSeconds}"); if ((now - LastActiveTime).TotalSeconds > 1) { onlineUserHelper.SetOnlineUser(new List()); } #if UNITY_IOS //超过5s没收到消息断开重连一次 if ((now - LastActiveTime).TotalSeconds > 5) { LastActiveTime = now; _udpService.Disconnect(); } #endif if (now.Second % 20 == 0) { //LastActiveTime = now; SendHeartbeat(); } } public static byte[] Compress(string text) { var bytes = Encoding.Unicode.GetBytes(text); using (var mso = new MemoryStream()) { using (var gs = new GZipStream(mso, CompressionMode.Compress)) { gs.Write(bytes, 0, bytes.Length); } return mso.ToArray(); } } public static string Decompress(byte[] data) { // Read the last 4 bytes to get the length byte[] lengthBuffer = new byte[4]; Array.Copy(data, data.Length - 4, lengthBuffer, 0, 4); int uncompressedSize = BitConverter.ToInt32(lengthBuffer, 0); var buffer = new byte[uncompressedSize]; using (var ms = new MemoryStream(data)) { using (var gzip = new GZipStream(ms, CompressionMode.Decompress)) { gzip.Read(buffer, 0, uncompressedSize); } } return Encoding.Unicode.GetString(buffer); } /// /// 发送心跳包 /// private static void SendHeartbeat() { try { var model = new { CommandType = 0, }; var sendBytes = Encoding.UTF8.GetBytes(Newtonsoft.Json.JsonConvert.SerializeObject(model)); _udpService.Send(sendBytes, sendBytes.Length); } catch (Exception e) { Console.WriteLine("发送心跳失败:" + e); } } public static int Competitionid { get; set; } public static bool IsWatch { get; set; } /// /// 告诉TCP服务器给我Gzip的格式 /// public static void SendGizpModel() { LastActiveTime = DateTime.Now; var client = App.AppVersion; #if UNITY_ANDROID client = "Unity-Android" + App.AppVersion; #elif UNITY_IOS client = "Unity-ios" + App.AppVersion; #else client = "Unity" + App.AppVersion; #endif try { var model = new GzipMsgModel { CommandType = 2, V = 2, Encoding = "gzip", Client = client, MemberId = App.CurrentUser.Id, Competitionid = Competitionid, IsWatch = IsWatch }; var sendBytes = Encoding.UTF8.GetBytes(Newtonsoft.Json.JsonConvert.SerializeObject(model)); _udpService.Send(sendBytes, sendBytes.Length); } catch (Exception e) { Console.WriteLine("发送失败:" + e); } } /// /// 发送数据 /// /// 路书Id /// 用户id /// 当前点位 /// 属性 /// 是否完成 /// 是否退出 public static void Send(int RouteId, int MemberId, double[] Point, bool IsCompleted = false, bool exit = false, double endDistance = 0, bool showVirtual = false, byte commandType = 1, double speed = 0, bool isVirtual = false, double preDistance = 0, double weightKg = 0, int competitionId = 0, bool saved = false,double? heartRate = 0, double power = 0, double? cadence = 0,int totalTicks = 0) { try { Point[0] = Math.Round(Point[0], 6, MidpointRounding.AwayFromZero); Point[1] = Math.Round(Point[1], 6, MidpointRounding.AwayFromZero); var model = new MsgModel { RouteId = RouteId, MemberId = MemberId, Point = Point, IsCompleted = IsCompleted, //exit = exit, EndDistance = endDistance, //ShowVirtual = showVirtual, CommandType = commandType, Speed = speed, Power = power, HeartRate = heartRate, Cadence = cadence, TotalTicks = totalTicks, //IsVirtual = isVirtual, PreDistance = preDistance, WeightKg = weightKg, Competitionid = competitionId, Saved = saved, //V = 1 }; var sendBytes = Encoding.UTF8.GetBytes(Newtonsoft.Json.JsonConvert.SerializeObject(model)); _udpService.Send(sendBytes, sendBytes.Length); } catch (Exception e) { Console.WriteLine("发送失败:" + e); } } public static void Dispose() { try { isExit = true; if (MapUDPService._udpService == null) return; for (int i = 0; i < 3; i++) { Send(0, App.CurrentUser.Id, new double[] { 1, 1 }, true, isExit); } _udpService.Close(); //task.Dispose(); _udpService = null; _onlineUserHelper = null; } catch (Exception) { } finally { if (_udpService != null) { _udpService.Close(); } //停止计时 if (heartbeat != null) { heartbeat.Stop(); heartbeat.Close(); } } } public static OnlineUser GetOnlineUserLastDataById(int userId) { //return _onlineUserHelper.GetOnlineUserLastDataById(userId); //var msg = msgs.Where(m => m.MemberId == userId); //if (msg == null) return null; return onlineUserHelper.OnlineUsers.FirstOrDefault(u => u.Id == userId); } /// /// 根据路书获取在线用户(非比赛用户不能看到比赛中的用户) /// /// /// public static List GetOnlineUsers(params int[] routeId) { var result = onlineUserHelper.OnlineUsers.Where(u => routeId.Contains(u.RouteId) && u.CompetitionId == 0).ToList(); return result; } public static List GetAllOnlineUserList() { var result = onlineUserHelper.OnlineUsers.Where(c => !c.IsSelf).ToList(); return result; } //比赛中的人只能看到当前比赛的人 public static List GetCompetitionOnlineUsers(int competitionId) { var result = onlineUserHelper.OnlineUsers.Where(u => u.CompetitionId == competitionId && !u.IsWatcher).ToList(); //var result = onlineUserHelper.OnlineUsers.Where(u =>!u.IsWatcher).ToList(); return result; } //当前比赛观察者 public static List GetCompetitionWatchers(int competitionId,int size = 5) { //var result = onlineUserHelper.OnlineUsers.Where(u => u.CompetitionId == competitionId && u.IsWatcher).Take(size).ToList(); var result = onlineUserHelper.OnlineUsers.Where(u =>u.IsWatcher).Take(size).ToList(); return result; } public static int GetNearRiderCount() { int count = 0; try { count = onlineUserHelper.OnlineUsers.Where(c => (c.Point[0] != 0 && c.Point[1] != 0) && (c.Point[0] != -1 && c.Point[1] != -1)).Count(); } catch (Exception) { } return count; } public static int GetAllOnlineUserCount() { int count = 0; try { count = onlineUserHelper.OnlineUsers.Count(); } catch (Exception) { } return count; } /// /// 获取附近的人(当前在线的所有人) /// /// public static IList GetNearRiderData(int pageIndex, int pageSize = 5, double[] point = null) { if (point == null) return null; try { var dict = new List>(); var allOnlineUsers = onlineUserHelper.OnlineUsers.Where(c => c.CompetitionId == 0); foreach (var item in allOnlineUsers) { if (item.Id == App.CurrentUser.Id) { dict.Add(new Tuple(0, item)); continue; } if (item.Point[0] != 0 && item.Point[1] != 0) { var distance = Math.Round(TurfHelper.GetDistances(point, item.Point) / 1000, 2); dict.Add(new Tuple(distance, item)); } } var allList = dict.OrderBy(d => d.Item1);//.Select(d=>d.Item2); var list = allList.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList(); var nearData = list.Select(n => new NearRiderModel() { Headimage = n.Item2.HeadImage, Name = n.Item2.Name, Near = n.Item1, //Math.Round(TurfHelper.GetDistances(point, n.Point) / 1000, 2), KGWeight = n.Item2.WeightKg.ToString(), Speed = Math.Round(n.Item2.Speed, 1), CountryImg = App.Host + $"User/GetCountryImg?userid={n.Item2.Id}", IsSelf = n.Item2.IsSelf, Id = n.Item2.Id, Country = n.Item2.Country, }).ToList(); return nearData; } catch (Exception) { return null; } } private class OnlineUserHelper { //public static List users { get; private set; } private static readonly object locker = new object(); private readonly List onlineUsers = new List(); public List OnlineUsers { get { //if((DateTime.Now - LastActiveTime).TotalSeconds > 1) //{ // SetOnlineUser(new List()); //} return onlineUsers.ToList(); } } /// /// 设置在线的人员 /// public void SetOnlineUser(List mes) { try { var msgs = RemoveExceptionData(mes); //LastActiveTime = DateTime.Now; var ids = msgs.Where(m => onlineUsers.All(u => u.Id != m.MemberId)).Select(m => m.MemberId); var routeIds = msgs.Where(m => onlineUsers.All(u => u.Id != m.MemberId)).Select(m => m.RouteId); if (ids.Any()) { List notCachedIds = new List(); foreach (var userId in ids) { var user = App.userList.Where(c => c.Id == userId).FirstOrDefault(); if (user != null) { onlineUsers.Add(user); } else { notCachedIds.Add(userId); } } if (notCachedIds.Count > 0) { var rangeUser = ConfigHelper.mapApi.GetOnlineUserInfo(ids).data; var rangeRoute = ConfigHelper.mapApi.GetMapRouteInfo(routeIds).data; App.RouteList.AddRange(rangeRoute); onlineUsers.AddRange(rangeUser); App.userList.AddRange(rangeUser); } } foreach (var user in onlineUsers) { var item = msgs.FirstOrDefault(u => u.MemberId == user.Id); if (item != null) { var route = App.RouteList.Where(c => c.Id == item.RouteId).FirstOrDefault(); if (route != null) { user.RouteName = route.Name; } user.LastActiveTime = DateTime.Now; user.RouteId = item.RouteId; user.Point = item.Point; user.IsCompleted = item.IsCompleted; //user.exit = item.exit; user.EndDistance = item.EndDistance; user.IsVirtual = item.MemberId < 0; user.Speed = item.Speed; user.WeightKg = item.WeightKg; user.PreDistance = item.PreDistance; user.CompetitionId = item.Competitionid; user.Saved = item.Saved; user.Power = item.Power; user.HeartRate = item.HeartRate; user.Cadence = item.Cadence; user.TotalTicks = item.TotoalTicks; user.WatcherList = new List(); foreach (var o in item.WatchIdList.Split('|')) { if (!string.IsNullOrEmpty(o)) { user.WatcherList.Add(Convert.ToInt32(o)); } } user.IsWatcher = user.WatcherList.Contains(item.MemberId); user.CreateTime = DateTime.MaxValue; } else { user.PreDistance = user.EndDistance; } } onlineUsers.RemoveAll(u => u.IsLost); } catch (Exception ex) { Thread.Sleep(3000); Debug.WriteLine(ex.Message); //throw; //return new List(); } } /// /// 去除重复数据,去除坐标为0的数据 /// /// private List RemoveExceptionData(List mes) { var list = mes.ToList(); //去除自己的数据和命令包 //mes.RemoveAll(item => item.MemberId == App.CurrentUser.Id || item.Point[0] == 0); //list.RemoveAll(item => item.Point[0] == 0 && item.Point[1] == 0); list.RemoveAll(item => item.Point[0] == null || item.Point[1] == null); //去除重复数据 list = list.Distinct(new OlineUserComparer()).ToList(); return list; } public class NearUserComparer : IEqualityComparer { public bool Equals(NearRiderModel x, NearRiderModel y) { if (x == null) return y == null; return x.Name == y.Name; } public int GetHashCode(NearRiderModel obj) { if (obj == null) return 0; return obj.Name.GetHashCode(); } } public class OlineUserComparer : IEqualityComparer { public bool Equals(ReceiveMsgModel x, ReceiveMsgModel y) { if (x == null) return y == null; return x.MemberId == y.MemberId; } public int GetHashCode(ReceiveMsgModel obj) { if (obj == null) return 0; return obj.MemberId.GetHashCode(); } } } } }