上网找了很多关于C# 实现uPnP映射的资料,好用的资料不是很多,很多人都是用系统UPnP.dll封装好的方法,但是我在vs2010下用C#尝试不是很成功。很多时候UPnPNATClass nat=new UPnPNATClass();得到的都是null值.终于找到了一个自己封装SOAP进行uPnP端口映射的方法,我帮作者把类又重新封装了一下。
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Xml;
public sealed class upnpLib
{
public enum InternetConnectTypes { IPv4, IPv6 }
public enum ResultTypes { Success, Faild }
public class RootDevice
{
#region -- Private Fields --
private string _location;
private string _host;
private string _port;
private string _server;
private int _maxAge;
public string _USN;
#endregion
#region -- Properties --
public string Location
{
get { return this._location; }
set { this._location = value; }
}
public string Host
{
get { return this._host; }
set { this._host = value; }
}
public string Port
{
get { return this._port; }
set { this._port = value; }
}
public string Server
{
get { return this._server; }
set { this._server = value; }
}
public int MaxAge
{
get { return this._maxAge; }
set { this._maxAge = value; }
}
public string USN
{
get { return this._USN; }
set { this._USN = value; }
}
#endregion
#region -- Method --
public RootDevice(string location)
{
this.Host = location.Substring(location.IndexOf('/') + 2, location.IndexOf('/', location.IndexOf('/') + 2) - (location.IndexOf('/') + 2));
this.Port = this.Host.Substring(this.Host.IndexOf(':') + 1);
this.Host = this.Host.Substring(0, this.Host.IndexOf(':'));
this.Location = location.Substring(location.IndexOf('/', location.IndexOf('/') + 2));
}
#endregion
}
public class DeviceService
{
#region -- Private Fields --
private string _type;
private string _id;
private string _scpdUrl;
private string _controlUrl;
private string _eventSubUrl;
private string _baseUrl;
#endregion
#region -- Properties --
public string Type
{
get { return this._type; }
set { this._type = value; }
}
public string ID
{
get { return this._id; }
set { this._id = value; }
}
public string SCPDUrl
{
get { return this._scpdUrl; }
set { this._scpdUrl = value; }
}
public string ControlUrl
{
get { return this._controlUrl; }
set { this._controlUrl = value; }
}
public string EventSubUrl
{
get { return this._eventSubUrl; }
set { this._eventSubUrl = value; }
}
public string BaseUrl
{
get { return this._baseUrl; }
set { this._baseUrl = value; }
}
#endregion
}
public class ServicePoint
{
#region -- Private Fields --
private string _name;
private IList _argumentList = new List();
#endregion
#region -- Properties --
public string Name
{
get { return this._name; }
set { this._name = value; }
}
public IList ArgumentList
{
get { return this._argumentList; }
set { this._argumentList = value; }
}
#endregion
}
public class Argument
{
#region -- Private Fields --
private string _name;
private string _direction;
private string _relatedStateVariable;
#endregion
#region -- Properties --
public string Name
{
get { return this._name; }
set { this._name = value; }
}
public string Direction
{
get { return this._direction; }
set { this._direction = value; }
}
public string RelatedStateVariable
{
get { return this._relatedStateVariable; }
set { this._relatedStateVariable = value; }
}
#endregion
}
public class StateVariable
{
#region -- Private Fields --
private string _sendEvents;
private string _name;
private string _dataType;
private IList _allowedValueList = new List();
#endregion
#region -- Properties --
public string SendEvents
{
get { return this._sendEvents; }
set { this._sendEvents = value; }
}
public string Name
{
get { return this._name; }
set { this._name = value; }
}
public string DataType
{
get { return this._dataType; }
set { this._dataType = value; }
}
public IList AllowedValueList
{
get { return this._allowedValueList; }
set { this._allowedValueList = value; }
}
#endregion
}
public class Result
{
#region -- Private Fields--
private ResultTypes _status;
private string _message;
#endregion
#region -- Properties --
public ResultTypes Status
{
get { return this._status; }
set { this._status = value; }
}
public string Message
{
get { return this._message; }
set { this._message = value; }
}
#endregion
}
#region -- Private Fields --
private InternetConnectTypes _ict;
private Result r = new Result();
private DeviceService service = null;
private IList devices = null;
private IList services = null;
private IList _actions = null;
private IList _stateVariables = null;
#endregion
#region -- Properties --
public InternetConnectTypes InternetConnectType
{
get { return this._ict; }
set { this._ict = value; }
}
public IList ActionList
{
get { return this._actions; }
}
public IList StateVariableList
{
get { return this._stateVariables; }
}
#endregion
#region -- Method --
public upnpLib(InternetConnectTypes ict)
{
this.InternetConnectType = ict;
r.Status = ResultTypes.Success;
try
{
this.devices = this.discoverDevices(5);
if (this.devices.Count == 0)
{
throw new Exception("没有发现可用uPnP设备!");
}
this.services = this.getDeviceSvc(this.getDeviceDesc(this.devices[0]));
foreach (DeviceService s in this.services)
{
if (s.Type.Contains("service:WANIPConnection"))
{
this.service = s;
string Xml = this.getServiceDesc(this.devices[0],s);
this._actions = this.getServicePoint(Xml);
this._stateVariables = this.GetStateVariables(Xml);
break;
}
}
}
catch (Exception ex)
{
r.Status = ResultTypes.Faild;
r.Message = ex.Message;
}
}
private IList discoverDevices(int timeOut)
{
#region -- Send UDP data --
string strData = "M-SEARCH * HTTP/1.1\r\n" +
"HOST: 239.255.255.250:1900\r\n" +
"MAN:\"ssdp:discover\"\r\n" +
"MX:" + timeOut.ToString() + "\r\n" +
"ST:upnp:rootdevice\r\n\r\n";
byte[] data = Encoding.UTF8.GetBytes(strData);
UdpClient uc = new UdpClient(this.InternetConnectType == InternetConnectTypes.IPv4 ? AddressFamily.InterNetwork : AddressFamily.InterNetworkV6);
IPEndPoint IPep = new IPEndPoint(IPAddress.Broadcast, 1900);
uc.Send(data, data.Length, IPep);
Timer RecvTimeOut = new Timer(this.RecvTimeOutFunc, uc, timeOut * 1000, timeOut * 1000);
IList hints = new List();
#endregion
#region -- Recive response --
byte[] buffer = null;
int start, end;
string temp = "", find = "";
RootDevice hint;
while (buffer == null || buffer.Length == 0)
{
buffer = uc.Receive(ref IPep);
if (buffer != null && buffer.Length > 0)
{
strData = Encoding.UTF8.GetString(buffer);
if (!strData.Contains("upnp:rootdevice")) continue;
// Location
temp = "";find = "LOCATION:";
start = strData.IndexOf(find);
if (start > -1)
{
end = strData.IndexOf("\r\n", start);
temp = strData.Substring(start + find.Length, end - (start + find.Length)).Trim();
hint = new RootDevice(temp);
}
else
{
continue;
}
// Max age
temp = ""; find = "max-age =";
start = strData.IndexOf(find);
if (start > -1)
{
end = strData.IndexOf("\r\n", start);
temp = strData.Substring(start + find.Length, end - (start + find.Length)).Trim();
hint.MaxAge = int.Parse(temp);
}
else
{
continue;
}
// Server
temp = ""; find = "SERVER:";
start = strData.IndexOf(find);
if (start > -1)
{
end = strData.IndexOf("\r\n", start);
temp = strData.Substring(start + find.Length, end - (start + find.Length)).Trim();
hint.Server = temp;
}
else
{
continue;
}
// USN
temp = ""; find = "USN:";
start = strData.IndexOf(find);
if (start > -1)
{
end = strData.IndexOf("\r\n", start);
temp = strData.Substring(start + find.Length, end - (start + find.Length)).Trim();
hint.USN = temp;
}
else
{
continue;
}
hints.Add(hint);
break;
}
}
#endregion
uc.Close();
return hints;
}
private void RecvTimeOutFunc(object uc)
{
((UdpClient)uc).Close();
}
private string getDeviceDesc(RootDevice rd)
{
string strData = "GET " + rd.Location + " HTTP/1.1\r\n" +
"HOST:" + rd.Host + ":" + rd.Port + "\r\n" +
"ACCEPT-LANGUAGE: \r\n\r\n", result = "";
byte[] data = Encoding.UTF8.GetBytes(strData);
IPAddress ipadr = (Dns.GetHostAddresses(rd.Host))[0];
IPEndPoint IPep = new IPEndPoint(ipadr, int.Parse(rd.Port));
TcpClient tc = new TcpClient(this.InternetConnectType == InternetConnectTypes.IPv4 ? AddressFamily.InterNetwork : AddressFamily.InterNetworkV6);
NetworkStream ns = null;
try
{
tc.Connect(IPep);
ns = tc.GetStream();
ns.Write(data, 0, data.Length);
Thread.Sleep(100);
int ReadSize = 2048;
byte[] buff = new byte[ReadSize], readBuff;
strData = "";
while (ReadSize == 2048)
{
ReadSize = ns.Read(buff, 0, buff.Length);
readBuff = new byte[ReadSize];
Array.Copy(buff, 0, readBuff, 0, ReadSize);
strData += Encoding.UTF8.GetString(readBuff);
}
result = strData.Substring(strData.IndexOf("\r\n\r\n") + 4).Trim();
while (result.Substring(result.Length - 2) == "\r\n" || result.Substring(result.Length - 2) == Encoding.Default.GetString(new byte[2] { 0, 0 }))
{
result = result.Substring(0, result.Length - 2);
}
}
catch { }
finally
{
if (ns != null)
{
ns.Close();
ns.Dispose();
}
if (tc != null)
{
tc.Close();
}
}
return result;
}
private IList getDeviceSvc(string Xml)
{
MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(Xml));
XmlReader xr = XmlReader.Create(ms);
IList hints = new List();
string URLBase = "";
while (xr.Read())
{
if (xr.NodeType == XmlNodeType.Element && xr.Name == "URLBase")
{
do
{
xr.Read();
if (xr.NodeType == XmlNodeType.Text)
{
URLBase = xr.Value;
}
} while (xr.NodeType != XmlNodeType.EndElement || xr.Name != "URLBase");
continue;
}
if (xr.NodeType == XmlNodeType.Element && xr.Name == "service")
{
DeviceService s = new DeviceService();
s.BaseUrl = URLBase;
string curElement = string.Empty;
do
{
xr.Read();
if (xr.NodeType == XmlNodeType.Element)
{
curElement = xr.Name;
continue;
}
if (xr.NodeType == XmlNodeType.EndElement)
{
curElement = null;
continue;
}
if (xr.NodeType == XmlNodeType.Text)
{
switch (curElement)
{
case "serviceType":
s.Type = xr.Value;
break;
case "serviceId":
s.ID = xr.Value;
break;
case "SCPDURL":
s.SCPDUrl = xr.Value;
break;
case "controlURL":
s.ControlUrl = xr.Value;
break;
case "eventSubURL":
s.EventSubUrl = xr.Value;
break;
default:
break;
};
}
} while (xr.NodeType != XmlNodeType.EndElement || xr.Name != "service");
hints.Add(s);
}
}
return hints;
}
private string getServiceDesc(RootDevice rd, DeviceService ds)
{
string strData = "GET " + ds.BaseUrl + ds.SCPDUrl + " HTTP/1.1\r\n" +
"HOST:" + rd.Host + ":" + rd.Port + "\r\n" +
"ACCEPT-LANGUAGE: \r\n\r\n", result = "";
byte[] data = Encoding.UTF8.GetBytes(strData);
IPAddress ipadr = (Dns.GetHostAddresses(rd.Host))[0];
IPEndPoint IPep = new IPEndPoint(ipadr, int.Parse(rd.Port));
TcpClient tc = new TcpClient(this.InternetConnectType == InternetConnectTypes.IPv4 ? AddressFamily.InterNetwork : AddressFamily.InterNetworkV6);
NetworkStream ns = null;
try
{
tc.Connect(IPep);
ns = tc.GetStream();
ns.Write(data, 0, data.Length);
Thread.Sleep(100);
int ReadSize = 2048;
byte[] buff = new byte[ReadSize], readBuff;
strData = "";
while (ReadSize == 2048)
{
ReadSize = ns.Read(buff, 0, buff.Length);
readBuff = new byte[ReadSize];
Array.Copy(buff, 0, readBuff, 0, ReadSize);
strData += Encoding.UTF8.GetString(readBuff);
}
result = strData.Substring(strData.IndexOf("\r\n\r\n") + 4).Trim();
while (result.Substring(result.Length - 2) == "\r\n" || result.Substring(result.Length - 2) == Encoding.Default.GetString(new byte[2] { 0, 0 }))
{
result = result.Substring(0, result.Length - 2);
}
}
catch { }
finally
{
if (ns != null)
{
ns.Close();
ns.Dispose();
}
if (tc != null)
{
tc.Close();
}
}
return result;
}
private IList getServicePoint(string Xml)
{
MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(Xml));
XmlReader xr = XmlReader.Create(ms);
IList hints = new List();
while (xr.Read())
{
if (xr.NodeType == XmlNodeType.Element && xr.Name == "action")
{
ServicePoint sp = new ServicePoint();
string curElement = string.Empty;
do
{
xr.Read();
if (xr.NodeType == XmlNodeType.Element && xr.Name != "argumentList")
{
curElement = xr.Name;
continue;
}
if (xr.NodeType == XmlNodeType.EndElement && xr.Name != "argumentList")
{
curElement = null;
continue;
}
if (xr.NodeType == XmlNodeType.Text)
{
switch (curElement)
{
case "name":
sp.Name = xr.Value;
break;
default:
break;
};
continue;
}
if (xr.NodeType == XmlNodeType.Element && xr.Name == "argumentList")
{
string curElement2 = string.Empty;
do
{
xr.Read();
if (xr.NodeType == XmlNodeType.Element && xr.Name == "argument")
{
Argument arg = new Argument();
do
{
xr.Read();
if (xr.NodeType == XmlNodeType.Element)
{
curElement2 = xr.Name;
continue;
}
if (xr.NodeType == XmlNodeType.EndElement)
{
curElement2 = null;
continue;
}
if (xr.NodeType == XmlNodeType.Text)
{
switch (curElement2)
{
case "name":
arg.Name = xr.Value;
break;
case "direction":
arg.Direction = xr.Value;
break;
case "relatedStateVariable":
arg.RelatedStateVariable = xr.Value;
break;
default:
break;
};
}
} while (xr.NodeType != XmlNodeType.EndElement || xr.Name != "argument");
sp.ArgumentList.Add(arg);
}
} while (xr.NodeType != XmlNodeType.EndElement || xr.Name != "argumentList");
}
} while (xr.NodeType != XmlNodeType.EndElement || xr.Name != "action");
hints.Add(sp);
}
}
return hints;
}
private IList GetStateVariables(string Xml)
{
MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(Xml));
XmlReader xr = XmlReader.Create(ms);
IList hints = new List();
while (xr.Read())
{
if (xr.NodeType == XmlNodeType.Element && xr.Name == "stateVariable")
{
StateVariable hint = new StateVariable();
hint.SendEvents = xr.GetAttribute("sendEvents");
string curElement = string.Empty;
do
{
xr.Read();
if (xr.NodeType == XmlNodeType.Element && xr.Name != "allowedValueList")
{
curElement = xr.Name;
continue;
}
if (xr.NodeType == XmlNodeType.EndElement && xr.Name != "allowedValueList")
{
curElement = null;
continue;
}
if (xr.NodeType == XmlNodeType.Text)
{
switch (curElement)
{
case "name":
hint.Name = xr.Value;
break;
case "dataType":
hint.DataType = xr.Value;
break;
default:
break;
};
continue;
}
if (xr.NodeType == XmlNodeType.Element && xr.Name == "allowedValueList")
{
string curElement2 = string.Empty;
do
{
xr.Read();
if (xr.NodeType == XmlNodeType.Element)
{
curElement2 = xr.Name;
continue;
}
if (xr.NodeType == XmlNodeType.EndElement)
{
curElement2 = null;
continue;
}
if (xr.NodeType == XmlNodeType.Text)
{
switch (curElement2)
{
case "allowedValue":
hint.AllowedValueList.Add(xr.Value);
break;
default:
break;
};
}
} while (xr.NodeType != XmlNodeType.EndElement || xr.Name != "allowedValueList");
}
} while (xr.NodeType != XmlNodeType.EndElement || xr.Name != "stateVariable");
hints.Add(hint);
}
}
return hints;
}
private void makeAddPortMappingSOAP(RootDevice dev, DeviceService srv, string NewRemoteHost, string NewExternalPort, string NewProtocol, string NewInternalPort, string NewPortMappingDescription, string NewLeaseDuration)
{
string InternalAddress = string.Empty;
IPAddress[] addrs = Dns.GetHostAddresses(Dns.GetHostName());
foreach (IPAddress addr in addrs)
{
if (addr.AddressFamily == AddressFamily.InterNetwork)
{
InternalAddress = addr.ToString();
}
}
string strData = "" +
" " +
"" + NewExternalPort + " " +
"" + NewProtocol + " " +
"" + NewInternalPort + " " +
"" + InternalAddress + " " +
"1 " +
"" + NewPortMappingDescription + " " +
"" + NewLeaseDuration + " " +
" ";
string result = PostSOAP(dev, srv, strData, "AddPortMapping");
}
private void makeDeletePortMappingSOAP(RootDevice dev, DeviceService srv, string NewRemoteHost, string NewExternalPort, string NewProtocol)
{
string strData = "" +
"" + NewRemoteHost + " " +
"" + NewExternalPort + " " +
"" + NewProtocol + " " +
" ";
string result = PostSOAP(dev, srv, strData, "DeletePortMapping");
}
private string makeGetExternalIPAddress(RootDevice dev, DeviceService srv)
{
string strData = "" +
" ";
string result = PostSOAP(dev, srv, strData, "GetExternalIPAddress");
if (result.IndexOf("") > -1)
{
result = result.Substring(result.IndexOf("") + 22, result.IndexOf(" ") - (result.IndexOf("") + 22));
}
else
{
result = "";
}
return result;
}
private string PostSOAP(RootDevice dev, DeviceService srv, string soapData, string action)
{
string soap = "" +
"" + soapData + " " +
" ";
string strData = "POST " + srv.BaseUrl + srv.ControlUrl + " HTTP/1.1\r\n" +
"HOST: " + dev.Host + ":" + dev.Port + "\r\n" +
"Content-Length: " + Encoding.UTF8.GetBytes(soap).Length.ToString() + "\r\n" +
"CONTENT-TYPE: text/xml; charset=\"utf-8\"\r\n" +
"SOAPACTION: \"" + srv.Type + "#" + action + "\"\r\n\r\n" + soap;
byte[] data = Encoding.Default.GetBytes(strData);
IPAddress ipaddr = (Dns.GetHostAddresses(dev.Host))[0];
IPEndPoint IPep = new IPEndPoint(ipaddr, int.Parse(dev.Port));
TcpClient tc = new TcpClient(AddressFamily.InterNetwork);
NetworkStream ns = null;
try
{
tc.Connect(IPep);
ns = tc.GetStream();
ns.Write(data, 0, data.Length);
byte[] buffer = new byte[4096], readBuff;
int ReadSize = ns.Read(buffer, 0, buffer.Length);
readBuff = new byte[ReadSize];
Array.Copy(buffer, 0, readBuff, 0, ReadSize);
strData = Encoding.Default.GetString(readBuff);
}
catch { }
finally
{
if (ns != null)
{
ns.Close();
}
tc.Close();
}
return strData;
}
public Result addPortMapping(string NewRemoteHost, string NewExternalPort, string NewProtocol, string NewInternalPort, string NewPortMappingDescription, string NewLeaseDuration)
{
if (this.r.Status == ResultTypes.Success)
{
try
{
this.makeAddPortMappingSOAP(this.devices[0], this.service, NewRemoteHost, NewExternalPort, NewProtocol, NewInternalPort, NewPortMappingDescription, NewLeaseDuration);
}
catch (Exception ex)
{
r.Status = ResultTypes.Faild;
r.Message = ex.Message;
}
return this.r;
}
else
{
return this.r;
}
}
public Result deletePortMapping(string NewRemoteHost, string NewExternalPort, string NewProtocol)
{
if (this.r.Status == ResultTypes.Success)
{
try
{
this.makeDeletePortMappingSOAP(this.devices[0], this.service, NewRemoteHost, NewExternalPort, NewProtocol);
}
catch (Exception ex)
{
r.Status = ResultTypes.Faild;
r.Message = ex.Message;
}
return this.r;
}
else
{
return this.r;
}
}
public Result getExternalIPAddress()
{
if (this.r.Status == ResultTypes.Success)
{
try
{
r.Message = this.makeGetExternalIPAddress(this.devices[0], this.service);
}
catch (Exception ex)
{
r.Status = ResultTypes.Faild;
r.Message = ex.Message;
}
return this.r;
}
else
{
return this.r;
}
}
#endregion
}
这是调用方法
upnpLib upnp = new upnpLib(upnpLib.InternetConnectTypes.IPv4);
upnpLib.Result r = upnp.addPortMapping("200,200,200,200", "8000", "TCP", "11500", "this is uPnP Test.", "0");
MessageBox.Show(r.Status == upnpLib.ResultTypes.Success ? "成功" : "失败");
这里是获取路由器uPnP命令列表
upnpLib upnp = new upnpLib(upnpLib.InternetConnectTypes.IPv4);
IList hints = upnp.ActionList;