有時候我們希望知道程序正在使用那個IP地址連接到遠程的服務器(類似pathping命令返回的第一個結果,P2P應用中尤其多見),文末的代碼可以完成這個任務
寫完這段代碼之後,我對.Net(.Net培訓 ) API的設計有些不滿了。
首先就是Socket類的IOControl方法,該方法脫胎於Winsock2 API的WSAIoctl函數。對於基於C語言的Winsock2 API,設計出WSAIoctl顯得還合情合理,雖然冗長的參數列表很是嚇人,但是對於面向對象的C#(C#培訓 ),居然還需要使用byte[]這些弱類型的數據結構來做參數,實在是有些匪夷所思了,我覺得Socket類應該對IOControl進行充分的包裝,以便沒有Win32 API經驗的用戶更好的使用。
接著是IPEndPoint的序列化形式SocketAddress類,這個類明顯的與sockaddr structure一樣,不同的是,它比sockaddr structure更加難以使用。它提供了一個象數組一樣的索引器,允許用戶以[]運算符獲取其中的byte元素,但是卻不提供方法簡便的將其中的內容復制到一個byte[]中供Socket.IOControl調用,竟然需要客戶自己使用循環來調用,實在傻的可以。
http://www.mscto.com
最後是IPEndPoint,它居然需要實例化之後才能調用Create成員函數把一個SocketAddress實例反序列化成一個IPEndPoint對象,我暈,為啥不是靜態的呢?看了這個Create方法的代碼之後,我發現完全沒有必要將其做成成員函數(為了證明靜態方法的可行,我在文中創建了一個CreateIPEndPoint靜態方法,並用它替換了IPEndPoint.Create成員方法),不知道為了使這個方法看起來更像是成員方法還是其他什麼原因,SocketAddress的AddressFamily居然必須和IPEndPoint實例的AddressFamily一致,否則就拋出異常,狂暈,人家反序列化還得看你一個不知所謂的對象的臉色,真是慘。
以上是我的觀點,歡迎大家一起議議。
using System;
using System.Net.Sockets;
using System.Net;
class Program
{
static IPEndPoint QueryRoutingInterface(Socket sock,
IPEndPoint remoteEP)
{
SocketAddress sa = remoteEP.Serialize();
byte[] addrBytes = new byte[sa.Size];
for (int i = 0; i < sa.Size; i )
{
addrBytes[i] = sa[i]; http://www.mscto.com
}
http://www.mscto.com
byte[] outBytes = new byte[addrBytes.Length];
http://www.mscto.com
sock.IOControl(IOControlCode.RoutingInterfaceQuery,
addrBytes, http://www.mscto.com
outBytes);
for (int i = 0; i < sa.Size; i )
{
sa[i] = outBytes[i];
}
EndPoint ep = CreateIPEndPoint(sa);//remoteEP.Create(sa);
return (IPEndPoint)ep;
}
///
/// 根據SocketAddress創建IPEndPoint
/// http://www.mscto.com
///
http://www.mscto.com
///
///
public static IPEndPoint CreateIPEndPoint(SocketAddress socketAddress)
{
//if (socketAddress.Family != this.AddressFamily)
//{
// throw new ArgumentException(SR.GetString(.Net(.Net培訓 )_InvalidAddressFamily",
// new object[] { socketAddress.Family.ToString(),
// base.GetType().FullName, this.AddressFamily.ToString() }),
// "socketAddress"); http://www.mscto.com
//}
http://www.mscto.com
if (socketAddress.Size < 8)
{
//throw new ArgumentException(SR.GetString("net_InvalidSocketAddressSize",
// new object[] { socketAddress.GetType().FullName,
http://www.mscto.com
// base.GetType().FullName }),
// "socketAddress");
throw new ArgumentException();
}
//if (this.AddressFamily == AddressFamily.InterNetworkV6)
if (socketAddress.Family == AddressFamily.InterNetworkV6)
{
byte[] buffer1 = new byte[0x10];
for (int num1 = 0; num1 < buffer1.Length; num1 )
{
buffer1[num1] = socketAddress[num1 8];
}
int num2 = ((socketAddress << 8) & 0xff00) | socketAddress; http://www.mscto.com
long num3 = (((socketAddress[0x1b] << 0x18)
(socketAddress[0x1a] << 0x10))
(socketAddress[0x19] << 8))
socketAddress[0x18];
return new IPEndPoint(new IPAddress(buffer1, num3), num2);
}
http://www.mscto.com
int num4 = ((socketAddress << 8) & 0xff00) | socketAddress;
http://www.mscto.com
long num5 = ((((socketAddress & 0xff)
| ((socketAddress << 8) & 0xff00))
| ((socketAddress << 0x10) & 0xff0000))
| (socketAddress << 0x18)) & ((long)0xffffffff);
return new IPEndPoint(num5, num4);
}
static void Main(string[] args)
{
try
{
Socket s = new Socket(AddressFamily.InterNetwork, http://www.mscto.com
SocketType.Dgram,
ProtocolType.Udp);
s.Bind(new IPEndPoint(IPAddress.Any, 10001));
IPEndPoint remoteEP = new IPEndPoint(
Dns.GetHostAddresses("www.google.com")[0],
1000);
IPEndPoint ep = QueryRoutingInterface(s, remoteEP);
Console.WriteLine(remoteEP);
http://www.mscto.com
Console.WriteLine(ep);
}
catch (SocketException e)
{
Console.WriteLine(e);
Console.WriteLine(e.ErrorCode);
}
}
}
程序的核心是QueryRoutingInterface方法,代碼相當的簡單,我就不多做解釋了。