|
|
using Apewer.Network; using System; using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; using System.Runtime.InteropServices; using System.Text;
namespace Apewer {
/// <summary></summary>
public class NetworkUtility {
#region UDP
/// <summary>唤醒局域网中拥有指定 MAC 地址的设备。</summary>
/// <param name="mac">被唤醒设备的 MAC 地址,必须是长度为 6 的字节数组。</param>
public static void WakeOnLan(byte[] mac) { if (mac.Length != 6) return;
var uc = new System.Net.Sockets.UdpClient(); uc.Connect(IPAddress.Broadcast, 65535);
var pack = new List<byte>();
// 前 6 字节为 0xFF。
for (int i = 0; i < 6; i++) pack.Add(255);
// 目标 MAC 地址重复 16 次。
for (int i = 0; i < 16; i++) { for (int j = 0; j < 6; j++) pack.Add(mac[j]); }
// 发送 102 字节数据。
uc.Send(pack.ToArray(), pack.Count);
uc.Close(); }
/// <summary>从 NTP 服务器获取 UTC 时间。</summary>
/// <remarks>
/// 通用 NTP 服务器:<br/>
/// Worldwide: pool.ntp.org<br/>
/// Asia: asia.pool.ntp.org<br/>
/// China: edu.ntp.org.cn<br/>
/// China: us.ntp.org.cn<br/>
/// Europe: europe.pool.ntp.org<br/>
/// North: America north-america.pool.ntp.org<br/>
/// Oceania: oceania.pool.ntp.org<br/>
/// South America: south-america.pool.ntp.org<br/>
/// Windows: time.windows.com<br/>
/// Windows: time.nist.gov<br/>
/// </remarks>
public static Class<DateTime> GetUtcFromNtp(string server = "pool.ntp.org", int port = 123, int timeout = 1000) { try { var ipa = null as IPAddress; var parseIP = IPAddress.TryParse(server, out ipa); if (!parseIP) { var addresses = Dns.GetHostEntry(server).AddressList; if (addresses.Length > 0) ipa = addresses[0]; } if (ipa == null) return null;
var endpoint = new IPEndPoint(ipa, port); return GetUtcFromNtp(endpoint, timeout); } catch { } return null; }
/// <summary>从 NTP 服务器获取 UTC 时间。</summary>
public static Class<DateTime> GetUtcFromNtp(IPEndPoint endpoint, int timeout = 1000) { try { var request = new byte[48]; request[0] = 0x1B;
var response = new byte[48]; response[0] = 0x1B;
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); socket.Connect(endpoint); socket.SendTimeout = timeout; socket.ReceiveTimeout = timeout; socket.Send(request); socket.Receive(response); socket.Close();
const byte replytime = 40; ulong secondspart = BitConverter.ToUInt32(response, replytime); ulong secondsfraction = BitConverter.ToUInt32(response, replytime + 4); secondspart = SwapEndian(secondspart); secondsfraction = SwapEndian(secondsfraction); ulong milliseconds = (secondspart * 1000) + ((secondsfraction * 1000) / 0x100000000UL);
var utc = (new DateTime(1900, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).AddMilliseconds(milliseconds); return new Class<DateTime>(utc); } catch { } return null; }
private static uint SwapEndian(ulong x) { var a = ((x & 0x000000ff) << 24); var b = ((x & 0x0000ff00) << 8); var c = ((x & 0x00ff0000) >> 8); var d = ((x & 0xff000000) >> 24); return (uint)(a + b + c + d); }
#endregion
#region Puny Code
/// <summary></summary>
public static string ToPunyCode(string chinese) { if (string.IsNullOrEmpty(chinese)) return ""; try { return new IdnMapping().GetAscii(chinese); } catch { return chinese; } }
/// <summary></summary>
public static string FromPunyCode(string punycode) { if (string.IsNullOrEmpty(punycode)) return ""; try { return new IdnMapping().GetUnicode(punycode); } catch { return punycode; } }
#endregion
#region IP
/// <summary>获取本地计算机的计算机名。</summary>
public static string LocalHost { get => Dns.GetHostName() ?? ""; }
/// <summary>本地计算机的所有 IP 地址。</summary>
public static IPAddress[] LocalIP { get => Dns.GetHostEntry(Dns.GetHostName()).AddressList; }
/// <summary>判断 IPv4 地址格式是否正确。</summary>
public static bool IsIP(string ipv4) { try { if (string.IsNullOrEmpty(ipv4)) return false;
var split = ipv4.Split('.'); if (split.Length != 4) return false;
for (int i = 0; i < 4; i++) { var n = Convert.ToInt32(split[i]); if (n < 0 || n > 255) return false; if (n.ToString() != split[i]) return false; } return true; } catch { } return false; }
/// <summary>对目标地址进行解析。</summary>
public static string Resolve(string host) { try { if (IsIP(host)) { var ip = IPAddress.Parse(host); var he = Dns.GetHostEntry(ip); return he.HostName; } else { var ip = ""; var dn = host.ToLower(); var he = Dns.GetHostEntry(dn); var ts = ""; var on = he.Aliases; he = Dns.GetHostEntry(dn); for (int i = 0; i < on.Length; i++) ts = ts + on[i].ToString() + ","; ts = ""; var al = he.AddressList; for (int i = 0; i < al.Length; i++) ts = ts + al[i].ToString() + ","; ip = ts; if (ip.Length > 0) { if (ip.Substring(ip.Length - 1, 1) == ",") ip = ip.Substring(0, ip.Length - 1); } return ip; } } catch { } return ""; }
/// <summary>将由字符串表示的 IPv4 地址转换为 32 位整数。</summary>
public static int GetNumber(IPAddress ipv4) { try { var ba = ipv4.GetAddressBytes(); return BitConverter.ToInt32(ba, 0); } catch { return 0; } }
/// <summary>转换 IP 地址格式。</summary>
public static string GetPlainAddress(IPAddress address) { try { return address.ToString(); } catch { return ""; } }
/// <summary>转换 IP 地址格式。</summary>
public static string GetPlainAddress(IPEndPoint endpoint) { try { return GetPlainAddress(endpoint.Address); } catch { return ""; } }
/// <summary>转换 IP 地址格式。</summary>
public static IPAddress GetIPAddress(string address) { try { return IPAddress.Parse(address); } catch { return new IPAddress(0); } }
/// <summary>转换 IP 地址格式。</summary>
public static IPEndPoint GetIPEndPoint(string address, int port) { try { return new IPEndPoint(IPAddress.Parse(address), NumberUtility.Restrict(port, 0, ushort.MaxValue)); } catch { return new IPEndPoint(0, 0); } }
/// <summary>转换 IP 地址格式。</summary>
public static IPEndPoint GetIPEndPoint(IPAddress address, int port) { try { return new IPEndPoint(address, NumberUtility.Restrict(port, 0, ushort.MaxValue)); } catch { return new IPEndPoint(0, 0); } }
/// <summary>转换 IP 地址格式。</summary>
public static IPEndPoint GetIPEndPoint(System.Net.EndPoint endpoint) { try { return (IPEndPoint)endpoint; } catch { return new IPEndPoint(0, 0); } }
/// <summary>判断私有 IP 地址。</summary>
public static bool FromLAN(string ipv4) { if (ipv4.IsEmpty()) return false;
// localhost
if (ipv4 == "::1") return true; if (ipv4 == "127.0.0.1") return true;
// IPv4
var a = ipv4.Split('.'); if (a.Length != 4) return false; switch (a[0]) { case "10": return true; case "172": var a1 = NumberUtility.Int32(a[1]); if (a1 >= 16 && a1 <= 31) return true; break; case "192": if (a[1] == "168") return true; break; }
return false; }
#endregion
#region ICMP
/// <summary>发送 PING 命令,命令中包含 32 位零数据。</summary>
/// <param name="address">目标地址。</param>
/// <param name="timeout">等待响应的超时时间(毫秒)。</param>
/// <param name="ttl">命令的起始 TTL 值(在丢弃数据之前可以转发该数据的路由节点数)。</param>
/// <param name="df">是否分段。</param>
/// <returns>命令的返回结果。</returns>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentOutOfRangeException"></exception>
/// <exception cref="PingException"></exception>
public static PingReply Ping(string address, int timeout = 1000, byte ttl = 255, bool df = true) { if (string.IsNullOrEmpty(address)) throw new ArgumentNullException(nameof(address)); if (timeout < 1) throw new ArgumentOutOfRangeException(nameof(timeout));
var ip = IsIP(address) ? address : Resolve(address); if (ip.Contains(",")) ip = ip.Split(',')[0];
var options = new PingOptions(); options.DontFragment = df; options.Ttl = ttl;
var buffer = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; var ping = new Ping(); var reply = ping.Send(ip, timeout, buffer, options); return reply; }
#endregion
#region HTTP
/// <summary>解析 HTTP 方法。</summary>
public static HttpMethod ParseHttpMethod(string method) { if (!string.IsNullOrEmpty(method)) { var upper = TextUtility.Upper(method); if (upper.Contains("OPTIONS")) return HttpMethod.OPTIONS; else if (upper.Contains("POST")) return HttpMethod.POST; else if (upper.Contains("GET")) return HttpMethod.GET; else if (upper.Contains("CONNECT")) return HttpMethod.CONNECT; else if (upper.Contains("DELETE")) return HttpMethod.DELETE; else if (upper.Contains("HEAD")) return HttpMethod.HEAD; else if (upper.Contains("PATCH")) return HttpMethod.PATCH; else if (upper.Contains("PUT")) return HttpMethod.PUT; else if (upper.Contains("TRACE")) return HttpMethod.TRACE; } return HttpMethod.NULL; }
/// <summary>GET</summary>
public static HttpClient HttpGet(string url, int timeout = 30000) => HttpClient.Get(url, timeout);
/// <summary>POST</summary>
public static HttpClient HttpPost(string url, byte[] data, int timeout = 30000, string type = "application/octet-stream") => HttpClient.Post(url, data, timeout, type);
/// <summary>POST text/plain</summary>
public static HttpClient HttpPost(string url, string text, int timeout = 30000, string type = "text/plain") => HttpClient.Text(url, text, timeout, type);
/// <summary>POST application/x-www-form-urlencoded</summary>
public static HttpClient HttpPost(string url, IDictionary<string, string> form, int timeout = 30000) => HttpClient.Form(url, form, timeout);
/// <summary>POST application/x-www-form-urlencoded</summary>
public static HttpClient HttpPost(string url, Dictionary<string, string> form, int timeout = 30000) => HttpClient.Form(url, form, timeout);
/// <summary>获取 HTTP 状态的文本。</summary>
public static string HttpStatusDescription(int code) { switch (code) { case 100: return "Continue"; case 101: return "Switching Protocols"; case 102: return "Processing"; case 200: return "OK"; case 201: return "Created"; case 202: return "Accepted"; case 203: return "Non-Authoritative Information"; case 204: return "No Content"; case 205: return "Reset Content"; case 206: return "Partial Content"; case 207: return "Multi-Status"; case 300: return "Multiple Choices"; case 301: return "Moved Permanently"; case 302: return "Found"; case 303: return "See Other"; case 304: return "Not Modified"; case 305: return "Use Proxy"; case 307: return "Temporary Redirect"; case 400: return "Bad Request"; case 401: return "Unauthorized"; case 402: return "Payment Required"; case 403: return "Forbidden"; case 404: return "Not Found"; case 405: return "Method Not Allowed"; case 406: return "Not Acceptable"; case 407: return "Proxy Authentication Required"; case 408: return "Request Timeout"; case 409: return "Conflict"; case 410: return "Gone"; case 411: return "Length Required"; case 412: return "Precondition Failed"; case 413: return "Request Entity Too Large"; case 414: return "Request-Uri Too Long"; case 415: return "Unsupported Media Type"; case 416: return "Requested Range Not Satisfiable"; case 417: return "Expectation Failed"; case 422: return "Unprocessable Entity"; case 423: return "Locked"; case 424: return "Failed Dependency"; case 426: return "Upgrade Required"; // RFC 2817
case 500: return "Internal Server Error"; case 501: return "Not Implemented"; case 502: return "Bad Gateway"; case 503: return "Service Unavailable"; case 504: return "Gateway Timeout"; case 505: return "Http Version Not Supported"; case 507: return "Insufficient Storage"; default: return null; } }
/// <summary>按文件扩展名获取 Content-Type 值。</summary>
public static string Mime(string extension) { const string Default = "application/octet-stream"; if (string.IsNullOrEmpty(extension)) return Default;
var split = extension.Split('.'); var lower = split.Length < 1 ? null : split[split.Length - 1].Lower(); switch (lower) { // text/plain; charset=utf-8
case "css": return "text/css"; case "htm": return "text/html"; case "html": return "text/html"; case "ini": return "text/ini"; case "js": return "application/javascript"; case "json": return "text/json"; case "shtml": return "text/html"; case "sh": return "text/plain"; case "txt": return "text/plain"; } switch (lower) { case "jad": return "text/vnd.sun.j2me.app-descriptor"; case "m3u8": return "text/vnd.apple.mpegurl"; // application/vnd.apple.mpegurl
case "xml": return "text/xml"; case "htc": return "text/x-component"; case "mml": return "text/mathml"; case "wml": return "text/vnd.wap.wml"; } switch (lower) { case "3gp": return "video/3gpp"; case "3gpp": return "video/3gpp"; case "7z": return "application/x-7z-compressed"; case "ai": return "application/postscript"; case "asf": return "video/x-ms-asf"; case "asx": return "video/x-ms-asf"; case "atom": return "application/atom+xml"; case "avi": return "video/x-msvideo"; case "bmp": return "image/x-ms-bmp"; case "cco": return "application/x-cocoa"; case "crt": return "application/x-x509-ca-cert"; case "der": return "application/x-x509-ca-cert"; case "doc": return "application/msword"; case "docx": return "application/vnd.openxmlformats-officedocument.wordprocessingml.document"; case "ear": return "application/java-archive"; case "eot": return "application/vnd.ms-fontobject"; case "eps": return "application/postscript"; case "flv": return "video/x-flv"; case "gif": return "image/gif"; case "hqx": return "application/mac-binhex40"; case "ico": return "image/x-icon"; case "jar": return "application/java-archive"; case "jardiff": return "application/x-java-archive-diff"; case "jng": return "image/x-jng"; case "jnlp": return "application/x-java-jnlp-file"; case "jpeg": return "image/jpeg"; case "jpg": return "image/jpeg"; case "kar": return "audio/midi"; case "kml": return "application/vnd.google-earth.kml+xml"; case "kmz": return "application/vnd.google-earth.kmz"; case "m4a": return "audio/x-m4a"; case "m4v": return "video/x-m4v"; case "mid": return "audio/midi"; case "midi": return "audio/midi"; case "mkv": return "video/x-matroska"; case "mng": return "video/x-mng"; case "mov": return "video/quicktime"; case "mp3": return "audio/mpeg"; case "mp4": return "video/mp4"; case "mpeg": return "video/mpeg"; case "mpg": return "video/mpeg"; case "odg": return "application/vnd.oasis.opendocument.graphics"; case "odp": return "application/vnd.oasis.opendocument.presentation"; case "ods": return "application/vnd.oasis.opendocument.spreadsheet"; case "odt": return "application/vnd.oasis.opendocument.text"; case "ogg": return "audio/ogg"; case "pdb": return "application/x-pilot"; case "pdf": return "application/pdf"; case "pem": return "application/x-x509-ca-cert"; case "pl": return "application/x-perl"; case "pm": return "application/x-perl"; case "png": return "image/png"; case "ppt": return "application/vnd.ms-powerpoint"; case "pptx": return "application/vnd.openxmlformats-officedocument.presentationml.presentation"; case "prc": return "application/x-pilot"; case "ps": return "application/postscript"; case "ra": return "audio/x-realaudio"; case "rar": return "application/x-rar-compressed"; case "rpm": return "application/x-redhat-package-manager"; case "rss": return "application/rss+xml"; case "rtf": return "application/rtf"; case "run": return "application/x-makeself"; case "sea": return "application/x-sea"; case "sit": return "application/x-stuffit"; case "svg": return "image/svg+xml"; case "svgz": return "image/svg+xml"; case "swf": return "application/x-shockwave-flash"; case "tcl": return "application/x-tcl"; case "tif": return "image/tiff"; case "tiff": return "image/tiff"; case "tk": return "application/x-tcl"; case "ts": return "video/mp2t"; case "war": return "application/java-archive"; case "wbmp": return "image/vnd.wap.wbmp"; case "webm": return "video/webm"; case "webp": return "image/webp"; case "wmlc": return "application/vnd.wap.wmlc"; case "wmv": return "video/x-ms-wmv"; case "woff": return "font/woff"; case "woff2": return "font/woff2"; case "xhtml": return "application/xhtml+xml"; case "xls": return "application/vnd.ms-excel"; case "xlsx": return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; case "xpi": return "application/x-xpinstall"; case "xspf": return "application/xspf+xml"; case "zip": return "application/zip"; } return Default; }
#endregion
#region Port
private static int[] ActivePorts(IPEndPoint[] endpoints) { var list = new List<int>(endpoints.Length); foreach (var endpoint in endpoints) { var port = endpoint.Port; if (list.Contains(port)) continue; list.Add(port); } list.Sort(); list.Capacity = list.Count; return list.ToArray(); }
/// <summary>列出活动的 TCP 端口。</summary>
public static int[] ActiveTcpPorts() => ActivePorts(IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners());
/// <summary>列出活动的 UDP 端口。</summary>
public static int[] ActiveUdpPorts() => ActivePorts(IPGlobalProperties.GetIPGlobalProperties().GetActiveUdpListeners());
#endregion
}
}
|