IndexService.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. using IES.ExamServer.Helpers;
  2. using Microsoft.Extensions.Caching.Memory;
  3. using System.Diagnostics;
  4. using System.Net.NetworkInformation;
  5. using System.Net;
  6. using System.Runtime.InteropServices;
  7. using System.Text.Json;
  8. using System.Text.Json.Nodes;
  9. using System.Text.RegularExpressions;
  10. using System.Management;
  11. namespace IES.ExamServer.Services
  12. {
  13. public static class IndexService
  14. {
  15. public static ServerDevice GetServerDevice( string remote,string region, IEnumerable<string>? _url)
  16. {
  17. string hostName = $"{Environment.UserName}-{Dns.GetHostName()}";
  18. string os = RuntimeInformation.OSDescription;
  19. //获取当前客户端的服务端口
  20. string currentUserName = Environment.UserName;
  21. ServerDevice device = new ServerDevice { name =hostName, userName=currentUserName, os= os,region=region,remote=remote };
  22. int CpuCoreCount = 0;
  23. long MenemorySize = 0;
  24. if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
  25. {
  26. // 获取CPU核心数
  27. //int processorCount = Environment.ProcessorCount;
  28. //Console.WriteLine("CPU 核心数: " + processorCount);
  29. using (ManagementClass managementClass = new ManagementClass("Win32_Processor"))
  30. {
  31. using (ManagementObjectCollection managementObjectCollection = managementClass.GetInstances())
  32. {
  33. foreach (ManagementObject managementObject in managementObjectCollection)
  34. {
  35. CpuCoreCount += Convert.ToInt32(managementObject.Properties["NumberOfLogicalProcessors"].Value);
  36. }
  37. }
  38. }
  39. using (ManagementClass mc = new ManagementClass("Win32_ComputerSystem"))
  40. {
  41. using (ManagementObjectCollection moc = mc.GetInstances())
  42. {
  43. foreach (ManagementObject mo in moc)
  44. {
  45. if (mo["TotalPhysicalMemory"]!= null)
  46. {
  47. MenemorySize = Convert.ToInt64(mo["TotalPhysicalMemory"]);
  48. }
  49. }
  50. }
  51. }
  52. if (Environment.Is64BitOperatingSystem)
  53. {
  54. device.bit="64";
  55. }
  56. else
  57. {
  58. device.bit="32";
  59. }
  60. ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT Name, MaxClockSpeed FROM Win32_Processor");
  61. foreach (ManagementObject mo in searcher.Get())
  62. {
  63. string? cpuName = mo["Name"].ToString();
  64. string? clockSpeed = mo["MaxClockSpeed"].ToString();
  65. //Console.WriteLine($"CPU 名称: {cpuName}");
  66. //Console.WriteLine($"CPU 主频: {clockSpeed} MHz");
  67. device.cpuInfos.Add(new CPUInfo { name = cpuName, hz = clockSpeed });
  68. }
  69. }
  70. else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
  71. {
  72. //int processorCount = Environment.ProcessorCount;
  73. // Console.WriteLine("CPU 核心数: " + processorCount);
  74. try {
  75. string cpuInfo = File.ReadAllText("/proc/cpuinfo");
  76. string[] cpu_lines = cpuInfo.Split('\n');
  77. CpuCoreCount= cpu_lines.Count(line => line.StartsWith("processor", StringComparison.OrdinalIgnoreCase));
  78. string? cpuNameLine = cpuInfo.Split('\n').FirstOrDefault(line => line.StartsWith("model name"));
  79. string? clockSpeedLine = cpuInfo.Split('\n').FirstOrDefault(line => line.StartsWith("cpu MHz"));
  80. string cpuName = string.Empty;
  81. string clockSpeed = string.Empty;
  82. if (cpuNameLine!= null)
  83. {
  84. cpuName = cpuNameLine.Split(':').Last().Trim();
  85. }
  86. if (clockSpeedLine!= null)
  87. {
  88. clockSpeed = clockSpeedLine.Split(':').Last().Trim();
  89. }
  90. device.cpuInfos.Add(new CPUInfo { name = cpuName, hz = clockSpeed });
  91. } catch (Exception ex)
  92. {
  93. }
  94. string[] mem_lines = File.ReadAllLines("/proc/meminfo");
  95. var match = mem_lines.FirstOrDefault(line => line.StartsWith("MemTotal:"));
  96. if (match != null)
  97. {
  98. var matchResult = Regex.Match(match, @"\d+");
  99. if (matchResult.Success)
  100. {
  101. MenemorySize= long.Parse(matchResult.Value);
  102. }
  103. }
  104. }
  105. else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
  106. {
  107. try {
  108. using (var process = new Process())
  109. {
  110. process.StartInfo.FileName = "/usr/sbin/sysctl";
  111. process.StartInfo.Arguments = "-n hw.ncpu";
  112. process.StartInfo.RedirectStandardOutput = true;
  113. process.StartInfo.UseShellExecute = false;
  114. process.Start();
  115. string output = process.StandardOutput.ReadToEnd().Trim();
  116. int coreCount;
  117. if (int.TryParse(output, out coreCount))
  118. {
  119. CpuCoreCount= coreCount;
  120. }
  121. }
  122. }
  123. catch (Exception ex) { }
  124. try
  125. {
  126. using (var process = new Process())
  127. {
  128. process.StartInfo.FileName = "/usr/sbin/sysctl";
  129. process.StartInfo.Arguments = "-n hw.memsize";
  130. process.StartInfo.RedirectStandardOutput = true;
  131. process.StartInfo.UseShellExecute = false;
  132. process.Start();
  133. string output = process.StandardOutput.ReadToEnd().Trim();
  134. long memorySize;
  135. if (long.TryParse(output, out memorySize))
  136. {
  137. MenemorySize= memorySize;
  138. }
  139. }
  140. }
  141. catch (Exception ex) { }
  142. try
  143. {
  144. using (var process = new Process())
  145. {
  146. process.StartInfo.FileName = "/usr/sbin/sysctl";
  147. process.StartInfo.Arguments = "-n machdep.cpu.brand_string";
  148. process.StartInfo.RedirectStandardOutput = true;
  149. process.StartInfo.UseShellExecute = false;
  150. process.Start();
  151. string cpuName = process.StandardOutput.ReadToEnd().Trim();
  152. process.StartInfo.FileName = "/usr/sbin/sysctl";
  153. process.StartInfo.Arguments = "-n hw.cpu.frequency";
  154. process.StartInfo.RedirectStandardOutput = true;
  155. process.StartInfo.UseShellExecute = false;
  156. process.Start();
  157. string clockSpeed = process.StandardOutput.ReadToEnd().Trim();
  158. //Console.WriteLine($"CPU 名称: {cpuName}");
  159. //Console.WriteLine($"CPU 主频: {clockSpeed} Hz");
  160. device.cpuInfos.Add(new CPUInfo { name = cpuName, hz = clockSpeed });
  161. }
  162. }
  163. catch (Exception ex)
  164. {
  165. Console.WriteLine($"出现错误: {ex.Message}");
  166. }
  167. if (Environment.Is64BitOperatingSystem)
  168. {
  169. device.bit="64";
  170. }
  171. else
  172. {
  173. device.bit="32";
  174. }
  175. }
  176. if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
  177. {
  178. device.arch="ARM64";
  179. }
  180. else if (RuntimeInformation.ProcessArchitecture == Architecture.Arm)
  181. {
  182. device.arch="ARM32";
  183. }
  184. else if (RuntimeInformation.ProcessArchitecture == Architecture.X64)
  185. {
  186. device.arch="X64";
  187. }
  188. else if (RuntimeInformation.ProcessArchitecture == Architecture.X86)
  189. {
  190. device.arch="X86";
  191. }
  192. else
  193. {
  194. device.arch=$"未知({device.arch})";
  195. }
  196. //Console.WriteLine("CPU 核心数: " + CpuCoreCount+",RAM 大小:"+MenemorySize);
  197. device.cpu=CpuCoreCount;
  198. device.ram=MenemorySize;
  199. var nics = NetworkInterface.GetAllNetworkInterfaces();
  200. foreach (var nic in nics)
  201. {
  202. if (nic.OperationalStatus == OperationalStatus.Up)
  203. {
  204. var name = $"{nic.Name}-{nic.Description}";
  205. var mac = nic.GetPhysicalAddress().ToString();
  206. var properties = nic.GetIPProperties();
  207. var unicastAddresses = properties.UnicastAddresses;
  208. foreach (var unicast in unicastAddresses)
  209. {
  210. if (unicast.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
  211. {
  212. var ip = unicast.Address.ToString();
  213. Network network = new Network() { mac=mac, ip=ip, name= name };
  214. if (!string.IsNullOrWhiteSpace(mac.ToString()) && !mac.Equals("000000000000"))
  215. {
  216. device.networks.Add(network);
  217. }
  218. }
  219. }
  220. }
  221. }
  222. if (_url!.IsNotEmpty())
  223. {
  224. List<int> ports = new List<int>();
  225. foreach (var url in _url)
  226. {
  227. Uri uri = new Uri(url);
  228. ports.Add(uri.Port);
  229. }
  230. device.port= string.Join(",", ports);
  231. }
  232. else
  233. {
  234. throw new Exception("未获取到端口信息!");
  235. }
  236. string hashData = ShaHashHelper.GetSHA1($"{device.name}-{device.remote}-{device.port}-{device.os}-{string.Join(",", device.networks.Select(x => $"{x.mac}-{x.ip}"))}");
  237. device.deviceId=hashData;
  238. return device;
  239. }
  240. /// <summary>
  241. /// 初始化设备
  242. /// </summary>
  243. /// <param name="httpContext"></param>
  244. /// <param name="fingerprint">浏览器指纹</param>
  245. /// <param name="IP"></param>
  246. /// <param name="device_timeSpan"></param>
  247. /// <param name="_azureRedis"></param>
  248. /// <returns></returns>
  249. public static string GetDeviceInit(this HttpContext httpContext, string fingerprint, string IP, IMemoryCache _memoryCache)
  250. {
  251. string device = $"{fingerprint}-{IP}";
  252. List<string> cookieString = new List<string>();
  253. var cookie = httpContext.Request.Cookies;
  254. int status = 1;
  255. if (cookie != null)
  256. {
  257. ///设备是否存在
  258. foreach (var ck in cookie)
  259. {
  260. if (ck.Key.Equals("device"))
  261. {
  262. if (device.Contains("-") && device.Contains("."))
  263. {
  264. //如果匹配的是fingerprint-IP 则是已经存在的。
  265. if (ck.Value.Equals(device))
  266. {
  267. //redis如果存在则
  268. _memoryCache.TryGetValue<JsonNode>($"device:{fingerprint}:{IP}", out JsonNode device_exist);
  269. // 返回的则应该是ck.Value=exist_device_exist的数据
  270. if (device_exist!=null)
  271. {
  272. if (!string.IsNullOrWhiteSpace($"{device_exist["device"]}"))
  273. {
  274. //0是代表指纹和IP匹配,正常返回的
  275. status = 1;
  276. }
  277. else
  278. {
  279. //需要新建 fingerprint-IP
  280. status = 1;
  281. }
  282. }
  283. else
  284. {
  285. status = 1;
  286. }
  287. }
  288. else
  289. {
  290. string ck_ip = ck.Value.Split("-")[1];
  291. if (ck_ip.Equals(IP))
  292. {
  293. //传入的指纹和cookie的不一致,仍然以cookie的为准。
  294. status = 1;
  295. fingerprint = ck.Value.Split("-").First();
  296. device = ck.Value;
  297. }
  298. }
  299. }
  300. else
  301. {
  302. //如果匹配的是fingerprint则是一个新的设备。
  303. if (ck.Value.Equals(fingerprint))
  304. {
  305. //检查设备是否被占用
  306. //var device_exist = _azureRedis.GetRedisClient(8).HashExists($"device:{fingerprint}", IP);
  307. _memoryCache.TryGetValue<JsonNode>($"device:{fingerprint}:{IP}", out JsonNode device_exist);
  308. if (device_exist!=null)
  309. {
  310. //需要新建 sha1(fingerprint+uuid)-IP
  311. status = 2;
  312. }
  313. else
  314. {
  315. //0是代表指纹和IP匹配,正常返回的
  316. status = 1;
  317. }
  318. }
  319. else
  320. {
  321. //匹配的都不是,新设备。
  322. status = 1;
  323. }
  324. }
  325. }
  326. else
  327. {
  328. cookieString.Add($"{ck.Key}{ck.Value}");
  329. }
  330. }
  331. }
  332. /*
  333. httpContext.Request.Headers.TryGetValue("accept-language", out var accept_language);
  334. httpContext.Request.Headers.TryGetValue("sec-ch-ua", out var chua);
  335. httpContext.Request.Headers.TryGetValue("sec-ch-ua-platform", out var platform);
  336. httpContext.Request.Headers.TryGetValue("user-agent", out var useragent);
  337. httpContext.Request.Headers.TryGetValue("accept", out var accept);
  338. httpContext.Request.Headers.TryGetValue("accept-encoding", out var accept_encoding);
  339. device = ShaHashHelper.GetSHA1($"{IP}{accept_language}{chua}{platform}{useragent}{accept}{accept_encoding}{string.Join("", cookieString)}");
  340. */
  341. if (status == 2)
  342. {
  343. fingerprint = ShaHashHelper.GetSHA1(fingerprint + Guid.NewGuid().ToString());
  344. device = $"{fingerprint}-{IP}";
  345. }
  346. //else if (status == 1)
  347. //{
  348. // device = $"{fingerprint}-{IP}";
  349. //}
  350. //await _azureRedis.GetRedisClient(8).HashSetAsync($"device:{fingerprint}", IP, new { device }.ToJsonString());
  351. //await _azureRedis.GetRedisClient(8).KeyExpireAsync($"device:{fingerprint}", device_timeSpan);
  352. _memoryCache.Set($"device:{fingerprint}:{IP}", new { device });
  353. httpContext.Response.Cookies.Append("device", device, new CookieOptions { HttpOnly = true, MaxAge = new TimeSpan(24 * 7, 0, 0) });
  354. return device;
  355. }
  356. }
  357. public class ServerDevice
  358. {
  359. /// <summary>
  360. /// 设备id
  361. /// </summary>
  362. public string? deviceId { get; set; }
  363. public string? userName { get; set; }
  364. /// <summary>
  365. /// 机器名
  366. /// </summary>
  367. public string? name { get; set; }
  368. /// <summary>
  369. /// 操作系统
  370. /// </summary>
  371. public string? os { get; set; }
  372. /// <summary>
  373. /// 操作系统位数 64位/32位
  374. /// </summary>
  375. public string? bit { get; set; }
  376. /// <summary>
  377. /// 操作系统指令架构 x86/x64, arm arm64 其他
  378. /// </summary>
  379. public string? arch { get; set; }
  380. /// <summary>
  381. /// CPU核心数量
  382. /// </summary>
  383. public int cpu { get; set; }
  384. public List<CPUInfo> cpuInfos { get; set; } = new List<CPUInfo>();
  385. /// <summary>
  386. /// 内存大小
  387. /// </summary>
  388. public long ram { get; set;}
  389. /// <summary>
  390. /// 远程ip
  391. /// </summary>
  392. public string? remote { get; set; }
  393. /// <summary>
  394. /// 端口,可能有多个端口
  395. /// </summary>
  396. public string? port { get; set; }
  397. /// <summary>
  398. /// 地区
  399. /// </summary>
  400. public string? region { get; set; }
  401. /// <summary>
  402. /// 网卡 IP信息
  403. /// </summary>
  404. public List<Network> networks { get; set; } = new List<Network>();
  405. }
  406. public class CPUInfo
  407. {
  408. public string? name { get; set; }
  409. public string? hz { get; set; }
  410. }
  411. public class Network
  412. {
  413. public string? name { get; set; }
  414. public string? mac { get; set; }
  415. public string? ip { get; set; }
  416. }
  417. }