一些功能调整

增加tcp协议支持;
增加模拟多用户的功能;
This commit is contained in:
andy 2021-01-18 20:24:19 +08:00
parent e8b6418aa6
commit 0e931a3ac9
21 changed files with 890 additions and 174 deletions

View File

@ -1,14 +1,12 @@
using Newtonsoft.Json;
using OnlineUserPool.Model;
using OnlineUserPool.Unility;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace OnlineUserPool.Unility
namespace OnlineUserPool.Api
{
public class BaseApi
{
@ -19,7 +17,7 @@ namespace OnlineUserPool.Unility
httpClient.DefaultRequestHeaders.UserAgent.Add(new System.Net.Http.Headers.ProductInfoHeaderValue("OnlineUserPool", "1.0.0"));
}
protected static async Task<T> PostAsync<T>(string url, object data)
public static async Task<T> PostAsync<T>(string url, object data)
{
StringContent stringContent;
if (data != null)
@ -35,36 +33,11 @@ namespace OnlineUserPool.Unility
return JsonConvert.DeserializeObject<T>(result);
}
protected static async Task<T> GetAsync<T>(string url)
public static async Task<T> GetAsync<T>(string url)
{
var response = await httpClient.GetAsync(ConfigHelp.Host + url, HttpCompletionOption.ResponseContentRead).ConfigureAwait(false);
var result = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T>(result);
}
}
public class WebService : BaseApi
{
/// <summary>
/// 从服务端获取记录信息
/// </summary>
/// <returns></returns>
public static List<MapRecordRanking> GetRecordFileFromServer(List<long> id)
{
if (!id.Any())
{
return new List<MapRecordRanking>();
}
return PostAsync<JsonResult<List<MapRecordRanking>>>("MapRecord/GetRandomList", id).ConfigureAwait(false).GetAwaiter().GetResult().data;
}
public static List<MapRouteAndUserQueryVM> GetMapRouteRandomRecord(int top, IEnumerable<int> routeIds)
{
var routeIdsStr = "";
if(routeIds != null)
{
routeIdsStr = string.Join(",", routeIds);
}
return GetAsync<JsonResult<List<MapRouteAndUserQueryVM>>>($"MapRecord/GetRandomRankingUserRecord?top={ top }&routeIds={ routeIdsStr }").ConfigureAwait(false).GetAwaiter().GetResult().data;
}
}
}

View File

@ -0,0 +1,41 @@
using OnlineUserPool.Model;
using OnlineUserPool.Unility;
using System;
using System.Collections.Generic;
using System.Text;
namespace OnlineUserPool.Api.Model
{
public class MultiUserModel
{
public List<User> users { get; set; }
public MapDataModel route { get; set; }
public class User
{
public int Id { get; set; }
public string NickName { get; set; }
public string WxHeadImg { get; set; }
public double Weight { get; set; }
public User()
{
Speed = CommonHelper.GenerateRandomInteger(0, 30);
//Distance = CommonHelper.GenerateRandomInteger(0, 30)/1000D;
}
public double Speed {
get;set;
}
public double Distance { get; set; }
}
}
}

38
Api/WebService.cs Normal file
View File

@ -0,0 +1,38 @@
using Newtonsoft.Json;
using OnlineUserPool.Model;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace OnlineUserPool.Api
{
public class WebService : BaseApi
{
/// <summary>
/// 从服务端获取记录信息
/// </summary>
/// <returns></returns>
public static List<MapRecordRanking> GetRecordFileFromServer(List<long> id)
{
if (!id.Any())
{
return new List<MapRecordRanking>();
}
return PostAsync<JsonResult<List<MapRecordRanking>>>("MapRecord/GetRandomList", id).ConfigureAwait(false).GetAwaiter().GetResult().data;
}
public static List<MapRouteAndUserQueryVM> GetMapRouteRandomRecord(int top, IEnumerable<int> routeIds)
{
var routeIdsStr = "";
if(routeIds != null)
{
routeIdsStr = string.Join(",", routeIds);
}
return GetAsync<JsonResult<List<MapRouteAndUserQueryVM>>>($"MapRecord/GetRandomRankingUserRecord?top={ top }&routeIds={ routeIdsStr }").ConfigureAwait(false).GetAwaiter().GetResult().data;
}
}
}

View File

@ -2,9 +2,8 @@
<configuration>
<appSettings>
<add key="Host" value="http://192.168.0.97:5082/"/>
<add key="Top" value="10"/>
<add key="Top" value="0"/>
<add key="ShowVirtualUser" value="true"/>
<add key="Ip" value="192.168.0.97"/>
<add key="Port" value="11000"/>
</appSettings>
</configuration>

14
Hander/IHandle.cs Normal file
View File

@ -0,0 +1,14 @@
using OnlineUserPool.Model;
using System;
using System.Collections.Generic;
using System.Text;
namespace OnlineUserPool.Hander
{
interface IHandle
{
List<MsgModel> GetVirtualUserData();
void RemoveEndAndAddNewVirtualUser(int customerCount);
}
}

View File

@ -1,4 +1,5 @@
using OnlineUserPool.Model;
using OnlineUserPool.Api;
using OnlineUserPool.Model;
using OnlineUserPool.Unility;
using Serilog;
@ -10,11 +11,11 @@ using System.Text;
namespace OnlineUserPool.Hander
{
public class MapRecordRankingHander
public class MapRecordRankingHander : IHandle
{
private static List<MapRecordRanking> mapRecordRankings;
private static List<MapRecordRanking> mapRecordRankings = new List<MapRecordRanking>();
private static object locker = new object();//防止服务端响应慢,多次加载
private int top = ConfigHelp.Top;
//private int top = ConfigHelp.Top;
public MapRecordRankingHander()
{
@ -23,13 +24,15 @@ namespace OnlineUserPool.Hander
private void Init()
{
var records = WebService.GetMapRouteRandomRecord(top, null);
var pageSize = 10;
var pageCount = (int)Math.Ceiling(records.Count / (double)pageSize);
mapRecordRankings = new List<MapRecordRanking>();
for (int i = 0; i < pageCount; i++)
if (ConfigHelp.Top > 0)
{
mapRecordRankings.AddRange(WebService.GetRecordFileFromServer(records.Skip(i* pageSize).Take(pageSize).Select(n => n.Id).ToList()));
var records = WebService.GetMapRouteRandomRecord(ConfigHelp.Top, null);
var pageSize = 10;
var pageCount = (int)Math.Ceiling(records.Count / (double)pageSize);
for (int i = 0; i < pageCount; i++)
{
mapRecordRankings.AddRange(WebService.GetRecordFileFromServer(records.Skip(i * pageSize).Take(pageSize).Select(n => n.Id).ToList()));
}
}
}
@ -73,9 +76,9 @@ namespace OnlineUserPool.Hander
//Prop = string.Join(',', prop),
RouteId = item.RouteId,
EndDistance = Math.Round(targetData._Distance, 6),
ShowVirtual = true,
//ShowVirtual = true,
CommandType = 1,
IsVirtual = true,
//IsVirtual = true,
Speed = targetData._Speed,
WeightKg = weightKg,
PreDistance = item.GetPreDistance()
@ -94,31 +97,31 @@ namespace OnlineUserPool.Hander
/// <summary>
/// 删除已经骑行完的人,添加新的人物进去
/// </summary>
public void RemoveEndAndAddNewVirtualUser()
public void RemoveEndAndAddNewVirtualUser(int customerCount)
{
lock (locker)
{
var end = mapRecordRankings.FindAll(n => n.End);
//if (end.Count == mapRecordRankings.Count)
//{
// Init();
//}
if (end.Count > 0)
{
var virutalEndCount = mapRecordRankings.Count(d => d.UserId < 0 && d.End);
var routeIds = mapRecordRankings.Select(n => n.RouteId).Distinct().ToList();
mapRecordRankings.RemoveAll(n => n.End);
if (virutalEndCount > 0)
{
var randomUser = WebService.GetMapRouteRandomRecord(virutalEndCount, routeIds);
mapRecordRankings.RemoveAll(n => n.End);
//var virutalEndCount = mapRecordRankings.Count(d => d.UserId < 0 && d.End);
var routeIds = mapRecordRankings.Select(n => n.RouteId).Distinct().ToList();
//var str = "参数:" + (ConfigHelp.Top - virutalEndCount) + "," + string.Join(",",routeIds) + "\r\n";
//str += "服务端返回:" + Newtonsoft.Json.JsonConvert.SerializeObject(randomUser) +"\r\n";
//Log.Information(str);
var addRankings = WebService.GetRecordFileFromServer(randomUser.Select(n => n.Id).ToList());
mapRecordRankings.AddRange(addRankings);
}
var top = ConfigHelp.Top;
if(customerCount > 9)
{
top = 0;
}
if(top - mapRecordRankings.Count(d=>d.UserId<0) > 0)
{
var count = top - mapRecordRankings.Count(d => d.UserId < 0);
var randomUser = WebService.GetMapRouteRandomRecord(count, routeIds);
//var str = "参数:" + (ConfigHelp.Top - virutalEndCount) + "," + string.Join(",",routeIds) + "\r\n";
//str += "服务端返回:" + Newtonsoft.Json.JsonConvert.SerializeObject(randomUser) +"\r\n";
//Log.Information(str);
var addRankings = WebService.GetRecordFileFromServer(randomUser.Select(n => n.Id).ToList());
mapRecordRankings.AddRange(addRankings);
}
}
}
}

84
Hander/MultiUserHandle.cs Normal file
View File

@ -0,0 +1,84 @@
using OnlineUserPool.Api;
using OnlineUserPool.Api.Model;
using OnlineUserPool.Model;
using OnlineUserPool.Unility;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
namespace OnlineUserPool.Hander
{
public class MultiUserHandle : IHandle
{
private int _index = -1;
private MultiUserModel model;
private TurfHelper _turfHelper;
private int _routeId = 4297; //1660;
private int _size = 200;
public MultiUserHandle()
{
Init();
}
public async void Init()
{
model = await BaseApi.GetAsync<MultiUserModel>($"/Map/sss?routeId={ _routeId }&size={ _size }");
double i = 0;
foreach (var item in model.users)
{
i += 0.0005;
item.Distance = i;
}
_turfHelper = new TurfHelper(model.route.List.Select(d => d.Point));
//Debug.WriteLine(res);
}
public List<MsgModel> GetVirtualUserData()
{
_index++;
var msgModels = new List<MsgModel>();
if(model == null)
{
return msgModels;
}
foreach (var item in model.users)
{
var preDistance = 0D;
//item.Distance = CommonHelper.GenerateRandomInteger(0, 30) / 1000D;
if (_index > 0)
{
preDistance = item.Distance * (_index -1);
}
var point = _turfHelper.Along(item.Distance * _index);
var info = new MsgModel()
{
exit = false,
IsCompleted = false,
MemberId = item.Id,//虚拟的人Id变为负数
Point = new double[] { Math.Round(point.Latitude, 6), Math.Round(point.Longitude, 6) },
//Prop = string.Join(',', prop),
RouteId = _routeId,
EndDistance = item.Distance * _index,
//ShowVirtual = true,
CommandType = 1,
//IsVirtual = true,
Speed = item.Speed,
WeightKg = 0,
PreDistance = preDistance,
Competitionid = 30
};
msgModels.Add(info);
}
return msgModels;
}
public void RemoveEndAndAddNewVirtualUser(int customerCount)
{
}
}
}

View File

@ -1,4 +1,5 @@
using Prism.Mvvm;
using OnlineUserPool.Services;
using Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.Net;
@ -50,9 +51,26 @@ namespace OnlineUserPool.Model
/// </summary>
public List<int> ShowRouteId { get; set; }
///// <summary>
///// 是否展示在线的人
///// </summary>
//public bool ShowVirtual { get; set; }
/// <summary>
/// 是否展示在线的人
/// 客户端支持的消息格式的版本号
/// </summary>
public bool ShowVirtual { get; set; }
public int V { get; set; }
[Newtonsoft.Json.JsonIgnore]
public IService Service { get; set; }
public String ServiceName
{
get
{
return Service.ToString();
}
}
}
}

103
Model/MapDataModel.cs Normal file
View File

@ -0,0 +1,103 @@
using Newtonsoft.Json;
using OnlineUserPool.Unility;
using System;
using System.Collections.Generic;
using System.Text;
namespace OnlineUserPool.Model
{
public class MapDataModel
{
public string Type => "LineString";
/// <summary>
/// 总距离(km)
/// </summary>
public double TotalDistance { get; set; }
private List<Item> _List;
public List<Item> List
{
get
{
return _List;
}
set
{
_List = value;
if (_List == null) return;
this.CalcDistance();
this.CalcGrade();
}
}
public void CalcDistance()
{
for (int i = 0; i < _List.Count - 1; i++)
{
var value = CommonHelper.GetDistances(_List[i].Point[1], _List[i].Point[0], _List[i + 1].Point[1], _List[i + 1].Point[0]);
//var value = Turf.Distance(pt1, pt2, "kilometers") * 1000;
_List[i].Distance = Math.Round(value, 2);
}
}
/// <summary>
/// 计算坡度数据
/// </summary>
private void CalcGrade()
{
for (int i = 0; i < this._List.Count - 1; i++)
{
var a = _List[i + 1].Elevation - _List[i].Elevation;
if (a == 0)
{
//grade.Add(0);
_List[i].Grade = 0;
continue;
}
//勾股定理
var c = _List[i].Distance;//如果车停了下来c为0
if (c == 0 && i > 0)
{
_List[i].Grade = _List[i - 1].Grade;
}
else
{
var b = Math.Sqrt(c * c - a * a);
if (b == 0)//如果b等于这就是垂直的墙壁
{
_List[i].Grade = _List[i - 1].Grade;
}
else
{
_List[i].Grade = Math.Round(a / b * 100, 4);
}
}
}
}
public MapDataModel()
{
//_List = new List<Item>();
}
public class Item
{
/// <summary>
/// 坐标(lat,lon)
/// </summary>
public double[] Point { get; set; }
/// <summary>
/// 距离(单位米)
/// </summary>
[JsonIgnore]
public double Distance { get; set; }
/// <summary>
/// 海拔m
/// </summary>
public double Elevation { get; set; }
[JsonIgnore]
public double Grade { get; set; }
}
}
}

View File

@ -25,13 +25,13 @@ namespace OnlineUserPool.Model
/// <summary>
/// 是否展示在线的人
/// </summary>
public bool ShowVirtual { get; set; }
//public bool ShowVirtual { get; set; }
/// <summary>
/// 命令类型,0命令1消息
/// </summary>
public byte CommandType { get; set; }
public bool IsVirtual { get; set; }
//public bool IsVirtual { get; set; }
//public double Power { get; set; }
//public double Weight { get; set; }
@ -43,5 +43,15 @@ namespace OnlineUserPool.Model
public int Competitionid { get; set; }
public bool Saved { get; set; }
/// <summary>
/// 能处理的消息格式的版本号
/// </summary>
public int V { get; set; }
public string ToString(int v)
{
return $"{ RouteId },{ MemberId },{ string.Join(":", Point) },{ Convert.ToInt32(IsCompleted) },{ Speed },{ PreDistance },{ EndDistance },{ WeightKg },{ Competitionid },{ Convert.ToInt32(Saved) }";
}
}
}

View File

@ -46,7 +46,11 @@ namespace OnlineUserPool.Model
target._Speed = double.Parse(split[2]);
target._Distance = double.Parse(split[3]);
target._Cadence = double.Parse(split[4]);
target._HeartRate = int.Parse(split[5]);
if (!string.IsNullOrWhiteSpace(split[5]) && split[5] != "null")
{
target._HeartRate = int.Parse(split[5]);
}
if (split.Length > 6)
{
target._Lat = double.Parse(split[6]);

View File

@ -5,7 +5,13 @@
<TargetFramework>netcoreapp3.1</TargetFramework>
<UseWPF>true</UseWPF>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Services\TcpService.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="GeoJSON.Net" Version="0.1.51" />
<PackageReference Include="NetCoreServer" Version="3.0.22" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="Prism.Unity" Version="7.2.0.1422" />
<PackageReference Include="Prism.Wpf" Version="7.2.0.1422" />
@ -14,5 +20,10 @@
<PackageReference Include="Serilog.Sinks.File" Version="4.1.0" />
<PackageReference Include="System.Configuration.ConfigurationManager" Version="4.7.0" />
</ItemGroup>
<ItemGroup>
<Reference Include="TurfCS">
<HintPath>..\PowerFun Server\PowerFun Server\lib\TurfCS.dll</HintPath>
</Reference>
</ItemGroup>
</Project>

17
Services/IService.cs Normal file
View File

@ -0,0 +1,17 @@
using OnlineUserPool.Model;
using System;
using System.Collections.Generic;
using System.Net;
using System.Text;
namespace OnlineUserPool.Services
{
public interface IService
{
public void RunServer(Action<IPEndPoint, MsgModel, IService> action);
public void Send(byte[] dgram, int bytes, IPEndPoint endPoint);
public void Close();
}
}

93
Services/TcpService.cs Normal file
View File

@ -0,0 +1,93 @@
using OnlineUserPool.Model;
using OnlineUserPool.Unility;
using Serilog;
using SimpleTcp;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace OnlineUserPool.Services
{
public class TcpService : IService
{
private bool _stop = false;
SimpleTcpServer socket;
Action<IPEndPoint, MsgModel, IService> _action;
public void RunServer(Action<IPEndPoint, MsgModel, IService> action)
{
//throw new NotImplementedException();
_action = action;
//var ip = IPAddress.Parse("192.168.0.97");
var b = IPAddress.Any;
socket = new SimpleTcpServer($"{ IPAddress.Any }:11001");
socket.Events.ClientConnected += Events_ClientConnected;
socket.Events.ClientDisconnected += Events_ClientDisconnected;
socket.Events.DataReceived += Events_DataReceived;
Task.Run(() =>
{
socket.Start();
});
}
private void Events_DataReceived(object sender, SimpleTcp.DataReceivedEventArgs e)
{
var returnData = Encoding.UTF8.GetString(e.Data);
Debug.WriteLine(returnData);
foreach (var item in returnData.Split(new string[] { "}" }, StringSplitOptions.RemoveEmptyEntries))
{
var msg = Newtonsoft.Json.JsonConvert.DeserializeObject<MsgModel>(item +"}");
var ipEndPoint = IPEndPoint.Parse(e.IpPort);
_action(ipEndPoint, msg, this);
}
}
private void Events_ClientDisconnected(object sender, ClientDisconnectedEventArgs e)
{
}
private void Events_ClientConnected(object sender, ClientConnectedEventArgs e)
{
Debug.WriteLine("有新客户端连接" + e.IpPort);
}
public void Send(byte[] dgram, int bytes, IPEndPoint endPoint)
{
if(dgram == null || !dgram.Any())
{
return;
}
foreach (var item in socket.GetClients())
{
//try
//{
if(socket.IsConnected(item) == false)
{
continue;
}
socket.Send(item, dgram);
//}
//catch(Exception e)
//{
// Log.Error($"{ item.ToString() }:{ e.Message }\r\n{ e.StackTrace }");
//}
}
}
public void Close()
{
_stop = true;
socket.Dispose();
}
}
}

118
Services/TcpService1.cs Normal file
View File

@ -0,0 +1,118 @@
using NetCoreServer;
using OnlineUserPool.Model;
using Serilog;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace OnlineUserPool.Services
{
public class TcpService1 : IService
{
private PfTcpServer _server;
public void Close()
{
//throw new NotImplementedException();
}
public void RunServer(Action<IPEndPoint, MsgModel, IService> action)
{
//throw new NotImplementedException();
//_action = action;
_server = new PfTcpServer(IPAddress.Any, 11001, (ip, model)=> {
action(ip, model, this);
});
//_server.OptionNoDelay = true;
_server.Start();
}
public void Send(byte[] dgram, int bytes, IPEndPoint endPoint)
{
if (dgram == null || !dgram.Any())
{
return;
}
_server.Multicast(dgram);
}
class PfTcpServer : TcpServer
{
Action<IPEndPoint, MsgModel> _action;
public PfTcpServer(IPAddress address, int port, Action<IPEndPoint, MsgModel> action) :base(address, port)
{
_action = action;
}
protected override TcpSession CreateSession()
{
//return base.CreateSession()
return new PfTcpSession(this, _action);
}
protected override void OnError(SocketError error)
{
//base.OnError(error);
Debug.WriteLine(error);
}
}
class PfTcpSession : TcpSession
{
Action<IPEndPoint, MsgModel> _action;
public PfTcpSession(TcpServer server, Action<IPEndPoint, MsgModel> action) : base(server) {
_action = action;
}
protected override void OnConnected()
{
//base.OnConnected();
Debug.WriteLine("有新的Tcp连接");
}
protected override void OnDisconnected()
{
//base.OnDisconnected();
Debug.WriteLine("Tcp断开连接");
}
protected override void OnReceived(byte[] buffer, long offset, long size)
{
//base.OnReceived(buffer, offset, size);
var returnData = Encoding.UTF8.GetString(buffer, (int)offset, (int)size);
//Debug.WriteLine(returnData);
//Log.Information(returnData + "\r\n");
try
{
//var msg = Newtonsoft.Json.JsonConvert.DeserializeObject<MsgModel>(returnData);
//var ipEndPoint = IPEndPoint.Parse(this.Socket.RemoteEndPoint.ToString());
//_action(ipEndPoint, msg);
foreach (var item in returnData.Split(new string[] { "}" }, StringSplitOptions.RemoveEmptyEntries))
{
//if (string.IsNullOrWhiteSpace(item)) continue;
var msg = Newtonsoft.Json.JsonConvert.DeserializeObject<MsgModel>(item + "}");
var ipEndPoint = IPEndPoint.Parse(this.Socket.RemoteEndPoint.ToString());
_action(ipEndPoint, msg);
}
}
catch (Exception ex)
{
Log.Error($"接受到的数据处理时出错:{ returnData }\r\n{ ex.Message }\r\n{ ex.StackTrace }");
}
}
protected override void OnError(SocketError error)
{
//base.OnError(error);
Debug.WriteLine(error);
}
}
}
}

80
Services/UdpService.cs Normal file
View File

@ -0,0 +1,80 @@
using OnlineUserPool.Model;
using OnlineUserPool.Unility;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace OnlineUserPool.Services
{
public class UdpService : IService
{
private bool _stop = false;
//private static IPEndPoint serverIpEndPoint;
private static UdpClient udpServer;
public UdpService()
{
//serverIpEndPoint = new IPEndPoint(IPAddress.Parse(ConfigHelp.Ip), ConfigHelp.Port);
}
public void RunServer(Action<IPEndPoint, MsgModel, IService> action)
{
udpServer = new UdpClient(ConfigHelp.Port);
uint IOC_IN = 0x80000000;
uint IOC_VENDOR = 0x18000000;
uint SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12;
udpServer.Client.IOControl((int)SIO_UDP_CONNRESET, new byte[] { Convert.ToByte(false) }, null);
var remoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);
Task.Run(() =>
{
while (_stop == false)
{
try
{
byte[] receiveBytes = udpServer.Receive(ref remoteIpEndPoint);
var returnData = Encoding.ASCII.GetString(receiveBytes);
var msg = Newtonsoft.Json.JsonConvert.DeserializeObject<MsgModel>(returnData);
//lock (locker)
{
#if DEBUG
WriteLine($"本次接收:{ remoteIpEndPoint.Address.ToString() }:{ remoteIpEndPoint.Port }收到消息:{ returnData }");
#endif
action(remoteIpEndPoint, msg, this);
//if (timer.AutoReset == false)
//{
// timer.AutoReset = true;
//}
}
}
catch (Exception e)
{
Serilog.Log.Error("RunServer" + e.Message);
}
}
});
}
public void Send(byte[] dgram, int bytes, IPEndPoint endPoint)
{
udpServer.Send(dgram, bytes, endPoint);
}
public void Close()
{
_stop = true;
udpServer.Close();
}
void WriteLine(string str)
{
//Debug.WriteLine(str);
}
}
}

27
Unility/CommonHelper.cs Normal file
View File

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using TurfCS;
namespace OnlineUserPool.Unility
{
public class CommonHelper
{
public static double GetDistances(double lon, double lat, double lon1, double lat1)
{
var pt1 = Turf.Point(new double[] { lon, lat });
var pt2 = Turf.Point(new double[] { lon1, lat1 });
var value = Turf.Distance(pt1, pt2, "kilometers") * 1000;
return Math.Round(value, 2);
}
public static int GenerateRandomInteger(int min = 0, int max = int.MaxValue)
{
var randomNumberBuffer = new byte[10];
new RNGCryptoServiceProvider().GetBytes(randomNumberBuffer);
return new Random(BitConverter.ToInt32(randomNumberBuffer, 0)).Next(min, max);
}
}
}

View File

@ -7,20 +7,26 @@ namespace OnlineUserPool.Unility
class ConfigHelp
{
public static string Host { get; set; }
public static int Top { get; private set; }
public static int Top
{
get
{
return Convert.ToInt32(System.Configuration.ConfigurationManager.AppSettings["Top"]);
}
}
public static bool ShowVirtualUser { get; set; }
public static string Ip { get; set; }
//public static string Ip { get; private set; }
public static int Port { get; set; }
public static int Port { get; private set; }
static ConfigHelp()
{
Host = System.Configuration.ConfigurationManager.AppSettings["Host"];
Top = Convert.ToInt32(System.Configuration.ConfigurationManager.AppSettings["Top"]);
//Top = Convert.ToInt32(System.Configuration.ConfigurationManager.AppSettings["Top"]);
ShowVirtualUser = bool.Parse(System.Configuration.ConfigurationManager.AppSettings["ShowVirtualUser"]);
Ip = System.Configuration.ConfigurationManager.AppSettings["Ip"];
//Ip = System.Configuration.ConfigurationManager.AppSettings["Ip"];
Port = int.Parse(System.Configuration.ConfigurationManager.AppSettings["Port"]);
}
}

38
Unility/TurfHelper.cs Normal file
View File

@ -0,0 +1,38 @@
using GeoJSON.Net.Geometry;
using System;
using System.Collections.Generic;
using System.Text;
using TurfCS;
using System.Linq;
namespace OnlineUserPool.Unility
{
public class TurfHelper
{
private LineString _line;
/// <summary>
///
/// </summary>
/// <param name="points">坐标(lat,lon)</param>
public TurfHelper(IEnumerable<double[]> points)
{
//var list = new GeoJSON.Net.Geometry.GeographicPosition();
var list = points.Select(p => new GeoJSON.Net.Geometry.GeographicPosition(p[0], p[1]));
_line = new LineString(list);
}
/// <summary>
///
/// </summary>
/// <param name="distance">km</param>
public GeographicPosition Along(double distance)
{
var pt1 = Turf.Along(_line, distance);
return ((GeographicPosition)((GeoJSON.Net.Geometry.Point)pt1.Geometry).Coordinates);
//new LineString()
//new GeoJSON.Net.Geometry.Point(new GeoJSON.Net.Geometry.GeographicPosition())
//pt1.BoundingBoxes
}
}
}

View File

@ -13,18 +13,19 @@ using System.Net.Sockets;
using System.Threading.Tasks;
using System.Windows.Threading;
using System.Diagnostics;
using OnlineUserPool.Services;
using Newtonsoft.Json;
using System.Collections.Concurrent;
namespace OnlineUserPool.ViewModels
{
public class MainWindowViewModel : BindableBase
{
private static IPEndPoint serverIpEndPoint;
private static UdpClient udpServer;
{
public ObservableCollection<HostModel> Clients { get; private set; } = new ObservableCollection<HostModel>();
private static List<MsgModel> receiveMes = new List<MsgModel>();
private static ConcurrentBag<MsgModel> receiveMes = new ConcurrentBag<MsgModel>();
private static object locker = new object();
public static System.Timers.Timer timer;
private static MapRecordRankingHander mapRecordRankingHander;
private static IHandle mapRecordRankingHander;
private Dispatcher dispatcher;
public ObservableCollection<MsgModel> Customers { get; private set; } =
@ -40,23 +41,36 @@ namespace OnlineUserPool.ViewModels
}
}
private string _SendDataSize = "";
public string SendDataSize
{
get { return _SendDataSize; }
set
{
SetProperty(ref _SendDataSize, value);
}
}
//private IService _udpService;
public MainWindowViewModel()
{
Title = $"{ ConfigHelp.Ip }:{ ConfigHelp.Port }";
Title = $"{ IPAddress.Any }:{ ConfigHelp.Port }";
dispatcher = Dispatcher.CurrentDispatcher;
//Customers.Add("suntao");
WriteLine(DateTime.Now.ToShortDateString());
WriteLine("加载日志组件");
LogHelper.Init();
Log.Information("日志组件加载完成");
Log.Information("加载虚拟人物");
mapRecordRankingHander = new MapRecordRankingHander();
Log.Information("虚拟人物加载完成");
Log.Information($"初始化连接,当前地址:{ConfigHelp.Ip}:{ConfigHelp.Port}");
serverIpEndPoint = new IPEndPoint(IPAddress.Parse(ConfigHelp.Ip), ConfigHelp.Port);
RunServer();
Log.Information($"初始化连接,当前地址:{ IPAddress.Any }:{ConfigHelp.Port}");
new TcpService1().RunServer(ReceivedData);
//var tet = new System.Collections.Concurrent.ConcurrentBag<object>();
var _udpService = new UdpService();
_udpService.RunServer(ReceivedData);
Log.Information("服务启动成功");
timer = new System.Timers.Timer(1000);
timer.Elapsed += Timer_Elapsed;
@ -66,96 +80,67 @@ namespace OnlineUserPool.ViewModels
//Console.ReadKey();
}
private void ReceivedData(IPEndPoint remoteIpEndPoint, MsgModel msg, IService service)
{
dispatcher.Invoke(() =>
{
if (!Clients.Any(c => c.Equals(remoteIpEndPoint)))
{
Clients.Add(new HostModel
{
IPEndPoint = remoteIpEndPoint,
LastActiveTime = DateTime.Now,
RouteId = msg.RouteId,
MemberId = msg.MemberId,
//ShowVirtual = msg.ShowVirtual
V = msg.V,
Service = service
});
}
if (msg.CommandType == 0)
{
var client = Clients.FirstOrDefault(n => n.Equals(remoteIpEndPoint));
client.LastActiveTime = DateTime.Now;
//client.ShowVirtual = msg.ShowVirtual;
}
else if (msg.CommandType == 1)
{
receiveMes.Add(msg);
}
});
}
private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
NotifyClient();
}
private void RunServer()
{
udpServer = new UdpClient(serverIpEndPoint.Port);
uint IOC_IN = 0x80000000;
uint IOC_VENDOR = 0x18000000;
uint SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12;
udpServer.Client.IOControl((int)SIO_UDP_CONNRESET, new byte[] { Convert.ToByte(false) }, null);
var remoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);
Task.Run(() =>
{
while (true)
{
try
{
byte[] receiveBytes = udpServer.Receive(ref remoteIpEndPoint);
var returnData = Encoding.ASCII.GetString(receiveBytes);
var msg = Newtonsoft.Json.JsonConvert.DeserializeObject<MsgModel>(returnData);
lock (locker)
{
#if DEBUG
WriteLine($"本次接收:{ remoteIpEndPoint.Address.ToString() }:{ remoteIpEndPoint.Port }收到消息:{ returnData }");
#endif
dispatcher.Invoke(() =>
{
if (!Clients.Any(c => c.Equals(remoteIpEndPoint)))
{
Clients.Add(new HostModel
{
IPEndPoint = remoteIpEndPoint,
LastActiveTime = DateTime.Now,
RouteId = msg.RouteId,
MemberId = msg.MemberId,
ShowVirtual = msg.ShowVirtual
});
}
else
{
if (msg.CommandType == 0)
{
var client = Clients.FirstOrDefault(n => n.Equals(remoteIpEndPoint));
client.LastActiveTime = DateTime.Now;
client.ShowVirtual = msg.ShowVirtual;
}
else if(msg.CommandType == 1)
{
receiveMes.Add(msg);
}
}
});
//if (timer.AutoReset == false)
//{
// timer.AutoReset = true;
//}
}
}
catch (Exception e)
{
Log.Error("RunServer" + e.Message);
}
}
});
}
private void NotifyClient()
{
try
{
lock (locker)
{
var list = CloneJson(receiveMes).ToList();
receiveMes.Clear();
//加入虚拟人物消息
var virtualData = mapRecordRankingHander.GetVirtualUserData();
WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "-当前在线人数:" + Clients.Count + "-当前虚拟人数:" + virtualData.Count);
#if DEBUG
WriteLine($"在线人:{Newtonsoft.Json.JsonConvert.SerializeObject(receiveMes)}");
WriteLine($"在线人:{Newtonsoft.Json.JsonConvert.SerializeObject(list)}");
//\r\n虚拟人{Newtonsoft.Json.JsonConvert.SerializeObject(virtualData)}
#endif
receiveMes.AddRange(virtualData);
list.AddRange(virtualData);
SendMessage(Clients, receiveMes);
SendMessage(Clients, list);
dispatcher.Invoke(() =>
{
Customers.Clear();
foreach (var item in receiveMes)
foreach (var item in list)
{
if (Customers.Any(c => c.MemberId == item.MemberId))
{
@ -165,20 +150,21 @@ namespace OnlineUserPool.ViewModels
}
//移除下线的客户端
for (int i = 0; i < receiveMes.Count; i++)
for (int i = 0; i < list.Count; i++)
{
if (receiveMes[i].exit && !receiveMes[i].IsVirtual)//客户端退出,并且不是虚拟的人物
if (list[i].exit && list[i].MemberId >0)//客户端退出,并且不是虚拟的人物
{
//这个地方有严重的逻辑错误(虚拟的人物不能和真实的人用同一个名字)
var info = Clients.FirstOrDefault(n => n.MemberId == receiveMes[i].MemberId);
var info = Clients.FirstOrDefault(n => n.MemberId == list[i].MemberId);
if (info != null)
{
Clients.Remove(info);
}
}
}
receiveMes.Clear();//删除已经发送的数据
//clients.RemoveAll(i => i.Expire);//移除5钟内连接不上的客户端
//删除已经发送的数据
//receiveMes.Clear();
//移除5钟内连接不上的客户端
Clients.ToList().ForEach(item =>
{
if (item.Expire)
@ -189,7 +175,7 @@ namespace OnlineUserPool.ViewModels
});
}
//更新虚拟人物信息
mapRecordRankingHander.RemoveEndAndAddNewVirtualUser();
mapRecordRankingHander.RemoveEndAndAddNewVirtualUser(Customers.Count);
}
catch (Exception e)
{
@ -197,9 +183,17 @@ namespace OnlineUserPool.ViewModels
}
}
private static void SendMessage(Collection<HostModel> clients, List<MsgModel> msgModels)
private void SendMessage(Collection<HostModel> clients, List<MsgModel> msgModels)
{
string jsonString = Newtonsoft.Json.JsonConvert.SerializeObject(msgModels.Select(m=> new {
var clients1 = clients.ToList();
var list = CloneJson<List<MsgModel>>(msgModels);
foreach (var item in list)
{
item.PreDistance = Math.Round(item.PreDistance, 5);
item.EndDistance = Math.Round(item.EndDistance, 5);
item.WeightKg = Math.Round(item.WeightKg, 2);
}
string jsonString = Newtonsoft.Json.JsonConvert.SerializeObject(list.Select(m=> new {
m.RouteId,
m.MemberId,
m.Point,
@ -208,21 +202,42 @@ namespace OnlineUserPool.ViewModels
m.Speed,
m.PreDistance,
m.EndDistance,
m.IsVirtual,//后面要把这个字段过滤掉
//m.IsVirtual,//后面要把这个字段过滤掉
m.WeightKg,
m.Competitionid,
m.Saved
m.Saved,
}));
var data = Encoding.ASCII.GetBytes(jsonString);
foreach (var item in clients)
SendDataSize = (data.Length/1000D).ToString() +"KB";
var strV1 = string.Join("|", list.Select(m => m.ToString(1)));
WriteLine(strV1);
WriteLine(strV1.Length.ToString());
var dataV1 = Encoding.ASCII.GetBytes(strV1);
SendDataSize += $"\tV1{ (strV1.Length/1000D) }KB";
foreach (var item in clients1)
{
try
{
udpServer.Send(data, data.Length, item.IPEndPoint);
if(item == null)
{
continue;
}
if (item.V == 1)
{
item.Service.Send(dataV1, dataV1.Length, item.IPEndPoint);
}
else
{
item.Service.Send(data, data.Length, item.IPEndPoint);
}
}
catch (Exception e)
{
Log.Error($"{ item.IPEndPoint.ToString() }:{ e.Message }\r\n{ e.StackTrace }");
Debug.WriteLine(e.Message);
Log.Error($"{ item.IPEndPoint.ToString() }:{ e.Message }\r\n{ e.StackTrace }");
}
}
}
@ -230,7 +245,24 @@ namespace OnlineUserPool.ViewModels
void WriteLine(string str)
{
Debug.WriteLine(str);
//Debug.WriteLine(str);
}
T CloneJson<T>(T source)
{
// Don't serialize a null object, simply return the default for that object
if (Object.ReferenceEquals(source, null))
{
return default(T);
}
// initialize inner objects individually
// for example in default constructor some list property initialized with some values,
// but in 'source' these items are cleaned -
// without ObjectCreationHandling.Replace default constructor values will be added to result
var deserializeSettings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace };
return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source), deserializeSettings);
}
}
}

View File

@ -8,6 +8,7 @@
Title="{ Binding Title }" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20"></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
@ -28,7 +29,13 @@
</DataTemplate>
</ListView.ItemTemplate>
</ListView>-->
<DataGrid ItemsSource="{ Binding Clients }" IsReadOnly="True"></DataGrid>
<DataGrid ItemsSource="{ Binding Customers }" Grid.Row="1" IsReadOnly="True"></DataGrid>
<TextBlock>
<Run Text="在线客户端:"></Run>
<Run Text="{ Binding Clients.Count, Mode=OneWay}"></Run>
<Run Text="发送单个客户端数据包大小:"></Run>
<Run Text="{Binding SendDataSize,Mode=OneWay}"></Run>
</TextBlock>
<DataGrid ItemsSource="{ Binding Clients }" Grid.Row="1" IsReadOnly="True"></DataGrid>
<DataGrid ItemsSource="{ Binding Customers }" Grid.Row="2" IsReadOnly="True"></DataGrid>
</Grid>
</Window>