如何在C#控制台程序中不依赖第三方网站获取用户公网IP?
不依赖第三方网站获取公网IP的C#实现
首先要明确:本地主机本身并没有直接持有公网IP——公网IP通常分配给你网络出口的NAT设备(比如家用路由器、企业网关)。所以不依赖第三方网站的话,只能通过与这个NAT设备交互来获取公网IP,最常用的方式是利用UPnP(通用即插即用)协议,多数家用路由器默认开启了UPnP支持。
以下是基于UPnP协议的实现代码:
using System; using System.Net; using System.Net.Sockets; using System.Xml.Linq; class PublicIpFetcher { static void Main(string[] args) { string publicIp = GetPublicIpViaUpnp(); if (!string.IsNullOrEmpty(publicIp)) { Console.WriteLine($"公网IP: {publicIp}"); } else { Console.WriteLine("无法获取公网IP,可能是路由器未开启UPnP,或者设备不支持UPnP协议。"); } } static string GetPublicIpViaUpnp() { try { // 发送UPnP服务发现的UDP广播请求 string discoveryRequest = @"M-SEARCH * HTTP/1.1 HOST: 239.255.255.250:1900 MAN: ""ssdp:discover"" MX: 3 ST: urn:schemas-upnp-org:service:WANIPConnection:1"; using UdpClient udpClient = new UdpClient(); udpClient.EnableBroadcast = true; IPEndPoint multicastEndpoint = new IPEndPoint(IPAddress.Parse("239.255.255.250"), 1900); byte[] requestBytes = System.Text.Encoding.ASCII.GetBytes(discoveryRequest); udpClient.Send(requestBytes, requestBytes.Length, multicastEndpoint); // 接收路由器返回的服务信息 IPEndPoint responseEndpoint = new IPEndPoint(IPAddress.Any, 0); byte[] responseBytes = udpClient.Receive(ref responseEndpoint); string response = System.Text.Encoding.ASCII.GetString(responseBytes); // 从响应中提取UPnP服务的控制URL string[] responseLines = response.Split(new[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries); string controlUrl = null; foreach (string line in responseLines) { if (line.StartsWith("LOCATION:")) { string locationUrl = line.Substring(9).Trim(); using WebClient client = new WebClient(); string deviceXml = client.DownloadString(locationUrl); XDocument doc = XDocument.Parse(deviceXml); XNamespace ns = "urn:schemas-upnp-org:device-1-0"; var service = doc.Descendants(ns + "service") .FirstOrDefault(s => s.Element(ns + "serviceType")?.Value == "urn:schemas-upnp-org:service:WANIPConnection:1"); if (service != null) { string relativeControlUrl = service.Element(ns + "controlURL")?.Value; if (!string.IsNullOrEmpty(relativeControlUrl)) { Uri locationUri = new Uri(locationUrl); controlUrl = new Uri(locationUri, relativeControlUrl).ToString(); } break; } } } if (string.IsNullOrEmpty(controlUrl)) return null; // 发送SOAP请求获取公网IP string soapRequest = @"<?xml version=""1.0"" encoding=""utf-8""?> <s:Envelope xmlns:s=""http://schemas.xmlsoap.org/soap/envelope/"" s:encodingStyle=""http://schemas.xmlsoap.org/soap/encoding/""> <s:Body> <u:GetExternalIPAddress xmlns:u=""urn:schemas-upnp-org:service:WANIPConnection:1"" /> </s:Body> </s:Envelope>"; WebRequest webRequest = WebRequest.Create(controlUrl); webRequest.Method = "POST"; webRequest.ContentType = "text/xml; charset=utf-8"; webRequest.Headers.Add("SOAPAction", "\"urn:schemas-upnp-org:service:WANIPConnection:1#GetExternalIPAddress\""); using (var stream = webRequest.GetRequestStream()) { byte[] soapBytes = System.Text.Encoding.UTF8.GetBytes(soapRequest); stream.Write(soapBytes, 0, soapBytes.Length); } using WebResponse webResponse = webRequest.GetResponse(); using var reader = new System.IO.StreamReader(webResponse.GetResponseStream()); string soapResponse = reader.ReadToEnd(); XDocument soapDoc = XDocument.Parse(soapResponse); XNamespace soapNs = "http://schemas.xmlsoap.org/soap/envelope/"; XNamespace uNs = "urn:schemas-upnp-org:service:WANIPConnection:1"; var ipElement = soapDoc.Descendants(soapNs + "Body") .FirstOrDefault()? .Descendants(uNs + "GetExternalIPAddressResponse") .FirstOrDefault()? .Element(uNs + "NewExternalIPAddress"); return ipElement?.Value; } catch (Exception ex) { Console.WriteLine($"获取失败: {ex.Message}"); return null; } } }
注意事项
- 该代码依赖路由器开启UPnP功能,若路由器未开启则无法获取公网IP
- 部分企业网络可能禁用UPnP,这种情况下该方法无效
- 运行程序需要确保有足够的网络权限(比如允许UDP广播、HTTP请求)
内容的提问来源于stack exchange,提问作者ch0ck
相关产品推荐
相关产品推荐

