有時候我們希望知道程序正在使用那個ip地址連接到遠程的服務器(類似pathping命令返回的第一個結果,P2P應用中尤其多見),文末的代碼可以完成這個任務
寫完這段代碼之后,我對.net API的設計有些不滿了。
首先就是Socket類的IOControl方法,該方法脫胎于Winsock2 API的WSAIoctl函數。對于基于C語言的Winsock2 API,設計出WSAIoctl顯得還合情合理,雖然冗長的參數列表很是嚇人,但是對于面向對象的C#,居然還需要使用byte[]這些弱類型的數據結構來做參數,實在是有些匪夷所思了,我覺得Socket類應該對IOControl進行充分的包裝,以便沒有Win32 API經驗的用戶更好的使用。
接著是IPEndPoint的序列化形式SocketAddress類,這個類明顯的與sockaddr structure一樣,不同的是,它比sockaddr structure更加難以使用。它提供了一個象數組一樣的索引器,允許用戶以[]運算符獲取其中的byte元素,但是卻不提供方法簡便的將其中的內容復制到一個byte[]中供Socket.IOControl調用,竟然需要客戶自己使用循環來調用,實在傻的可以。
最后是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];
}
byte[] outBytes = new byte[addrBytes.Length];
sock.IOControl(IOControlCode.RoutingInterfaceQuery,
addrBytes,
outBytes);
for (int i = 0; i < sa.Size; i++)
{
sa[i] = outBytes[i];
}
EndPoint ep = CreateIPEndPoint(sa);//remoteEP.Create(sa);
return (IPEndPoint)ep;
}
/// <summary>
/// 根據SocketAddress創建IPEndPoint
/// </summary>
/// <remarks>該函數從IPEndPoint的Create方法反編譯出來</remarks>
/// <param name="socketAddress"></param>
/// <returns></returns>
public static IPEndPoint CreateIPEndPoint(SocketAddress socketAddress)
{
//if (socketAddress.Family != this.AddressFamily)
//{
// throw new ArgumentException(SR.GetString("net_InvalidAddressFamily",
// new object[] { socketAddress.Family.ToString(),
// base.GetType().FullName, this.AddressFamily.ToString() }),
// "socketAddress");
//}
if (socketAddress.Size < 8)
{
//throw new ArgumentException(SR.GetString("net_InvalidSocketAddressSize",
// new object[] { socketAddress.GetType().FullName,
// 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[2] << 8) & 0xff00) | socketAddress[3];
long num3 = (((socketAddress[0x1b] << 0x18)
+ (socketAddress[0x1a] << 0x10))
+ (socketAddress[0x19] << 8)) +
socketAddress[0x18];
return new IPEndPoint(new IPAddress(buffer1, num3), num2);
}
int num4 = ((socketAddress[2] << 8) & 0xff00) | socketAddress[3];
long num5 = ((((socketAddress[4] & 0xff)
| ((socketAddress[5] << 8) & 0xff00))
| ((socketAddress[6] << 0x10) & 0xff0000))
| (socketAddress[7] << 0x18)) & ((long)0xffffffff);
return new IPEndPoint(num5, num4);
}
static void Main(string[] args)
{
try
{
Socket s = new Socket(AddressFamily.InterNetwork,
SocketType.Dgram,
ProtocolType.Udp);
s.Bind(new IPEndPoint(IPAddress.Any, 10001));
IPEndPoint remoteEP = new IPEndPoint(
Dns.GetHostAddresses("],
1000);
IPEndPoint ep = QueryRoutingInterface(s, remoteEP);
Console.WriteLine(remoteEP);
Console.WriteLine(ep);
}
catch (SocketException e)
{
Console.WriteLine(e);
Console.WriteLine(e.ErrorCode);
}
}
}
程序的核心是QueryRoutingInterface方法,代碼相當的簡單,我就不多做解釋了。
http://m.survivalescaperooms.com/ncindy/archive/2007/01/02/610148.html
新聞熱點
疑難解答