在Window Embedded CE(Wince)下使用OpenNETCF进行路由表的开发
背景
在开发3G项目的是时候,发现尽管3G网络连接已经建立成功了,但是数据不能发送成功,查明原因,由于路由表的问题,导致数据往ActiveSync连接的对端,也就是PC发送,而不是发送到3G网络的拨号服务器去。本文讲述如何使用OpenNETCF来修改路由表。
什么是路由表(Routing Table)
先看一个Window Embedded CE的路由表
路由表是存储在路由器或者联网计算机上的一个电子表格或者数据库。本来路由表不仅仅使用在TCP/IP网络,IPX也使用路由表。但是目前流行使用路由表的网络只有TCP/IP。在TCP/IP网络,这表格指定IP包的流向,如上图可见,Destination和Netmask表示需要发送的目的地和及其掩码。GatewayAddress表示该Destination的包需要发送到的网关。Interface表示本机上网卡的地址,这个地址和Destination可以直接通信。Metric表示发送包的成本,是一个从0到9999的整数,数值越低表示成本越低,这条路径越可靠。
问题
当我拨通3G网络的时候,还是ping不通google,如下图:
原因出在路由表上,如下图:
google的地址是66.102.11.99,不在路由表内,会走默认路由,也就是0.0.0.0的路由配置.但是默认路由确有两条,而且Metric都是一样的。通过老王(王坚)的指导,那样的情况下,Windows 会自动选择速率快的网卡来传输数据,由于192.168.55.100是USB的ActiveSync连接,而10.250.47.212是3G网络连接,由于USB速度比3G网络快,所以发送到google.com(66.102.11.99)的包被默认发送到192.168.55.100去了。而192.168.55.100没有帮这台Wince机器做往internet的路由,所以wince机器没办法ping通google。
方案
解决这个问题可以有几个方案。
方案一,为目标地址加上路由,例如为google.com(66.102.11.99)加上路由,让它走10.250.47.212出,但是这方案不好,因为我的应用配置都是DNS名字,以后不知道到底IP是怎样,所以没办法为特定IP加路由。
方案二,删除指向192.168.55.100的默认路由。这个方案能满足我的需求,但是有时候需要用回ActiveSync的话需要增加原有的路由。
方案三,修改Metric的值,使得指向192.168.55.100的模拟路由优先选择。我的做法就是使用这个方案的。
实现
OpenNETCF的IPRoutingTable类大大简化了路由表开发的难度。
IPRoutingTable是一个容器类,封装了一个路由表格,每一条单独记录由IPForwardEntry类表达。IPForwardEntry类如下图所示。
IPForwardEntry类表示路由表中单独的一条记录。IPRoutingTable可以查看,新增,删除和修改路由表里面的项。其实增删改查路由表的操作都是通过P/Invoke iphlpapi.dll的API来完成的,关于P/Invoke 可以查看我之前的文章。关键API如下:
internal static unsafe extern int DeleteIpForwardEntry(byte[] pRoute);
[DllImport("iphlpapi.dll", SetLastError = true)]
internal static unsafe extern int CreateIpForwardEntry(byte[] pRoute);
[DllImport("iphlpapi.dll", SetLastError = true)]
internal static unsafe extern int SetIpForwardEntry(byte[] pRoute);
[DllImport("iphlpapi.dll", SetLastError = true)]
internal static unsafe extern int GetIpForwardTable(IntPtr pIpForwardTable, ref int pdwSize, int bOrder);
[DllImport("iphlpapi.dll", SetLastError = true)]
internal static unsafe extern int GetIpForwardTable(byte[] pIpForwardTable, ref int pdwSize, int bOrder);
[DllImport("iphlpapi.dll", SetLastError = true)]
internal static unsafe extern int FlushIpNetTable(int dwIfIndex);
下面是我的实现代码,选择的是方案三。
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using OpenNETCF.Net.NetworkInformation;
using System.Net;
namespace IpRouting
{
class Program
{
private static readonly IPAddress DefaultRoute = IPAddress.Parse("0.0.0.0");
private static readonly IPAddress ActiveSyncIP = IPAddress.Parse("192.168.55.100");
private static uint activeSyncRouteMetric = 50;
private static List<IPForwardEntry> routeEntries = new List<IPForwardEntry>();
static void Main(string[] args)
{
RemoveAndAddRoute();
}
static void RemoveAndAddRoute()
{
IPRoutingTable table = IPRoutingTable.GetRoutingTable();
Display(table);
table.Remove(DefaultRoute);
Display(table);
foreach (IPForwardEntry entry in routeEntries)
{
if (!entry.NextHop.Equals(ActiveSyncIP))
{
if (activeSyncRouteMetric > 0)
{
entry.Metric = activeSyncRouteMetric - 1;
}
else
{
entry.Metric = 0;
}
}
table.Add(entry);
}
Display(table);
}
static void Display(IPRoutingTable table)
{
Console.WriteLine("\r\n*IP Routing Table\r\n====================");
Console.WriteLine(string.Format("{0}{1}{2}{3}{4}",
"[Destination]".PadLeft(15),
"[NetMask]".PadLeft(15),
"[Next Hop]".PadLeft(15),
"[Interface]".PadLeft(15),
"[Metric]".PadLeft(12)));
foreach (IPForwardEntry entry in table)
{
string interfaceName;
if(entry.NetworkInterface == null)
{
interfaceName = "127.0.0.1";
}
else
{
interfaceName = entry.NetworkInterface.CurrentIpAddress.ToString();
}
Console.WriteLine(string.Format("{0}{1}{2}{3}{4}",
entry.Destination.ToString().PadLeft(15),
entry.SubnetMask.ToString().PadLeft(15),
entry.NextHop.ToString().PadLeft(15),
interfaceName.PadLeft(15),
entry.Metric.ToString().PadLeft(12)));
if (entry.Destination.Equals(DefaultRoute))
{
if (entry.NextHop.Equals(ActiveSyncIP))
{
activeSyncRouteMetric = entry.Metric;
}
IPForwardEntry tempEntry = entry.Clone();
routeEntries.Add(tempEntry);
}
}
}
}
}
上面是整个程序的代码。下面分开讲一下。通过Singleton可以取出当前机器上的路由表信息。
显示
IPRoutingTable table = IPRoutingTable.GetRoutingTable();
取出IPRoutingTable下的IPForwardEntry 就可以显示整个路由表信息。
{
Console.WriteLine("\r\n*IP Routing Table\r\n====================");
Console.WriteLine(string.Format("{0}{1}{2}{3}{4}",
"[Destination]".PadLeft(15),
"[NetMask]".PadLeft(15),
"[Next Hop]".PadLeft(15),
"[Interface]".PadLeft(15),
"[Metric]".PadLeft(12)));
foreach (IPForwardEntry entry in table)
{
string interfaceName;
if(entry.NetworkInterface == null)
{
interfaceName = "127.0.0.1";
}
else
{
interfaceName = entry.NetworkInterface.CurrentIpAddress.ToString();
}
Console.WriteLine(string.Format("{0}{1}{2}{3}{4}",
entry.Destination.ToString().PadLeft(15),
entry.SubnetMask.ToString().PadLeft(15),
entry.NextHop.ToString().PadLeft(15),
interfaceName.PadLeft(15),
entry.Metric.ToString().PadLeft(12)));
if (entry.Destination.Equals(DefaultRoute))
{
if (entry.NextHop.Equals(ActiveSyncIP))
{
activeSyncRouteMetric = entry.Metric;
}
IPForwardEntry tempEntry = entry.Clone();
routeEntries.Add(tempEntry);
}
}
}
删除
Windows Embedded CE和PC对删除的操作有区别,在Wince下只能删除一个Destination,而PC可以删除指向这一Destination的网关。所以在Wince下删除指向这一Destination的网关不方便,需要先删除所有的,然后增加不想删除的。下面的语句是删除操作。
table.Remove(DefaultRoute);
删除默认路由后的路由信息。
修改
可以直接修改路由表项(IPForwardEntry)信息的属性。
entry.Metric = activeSyncRouteMetric - 1;
新增
新增就是把IPForwardEntry增加到IPRoutingTable里面
table.Add(entry);
新增默认路由后的路由表信息。
我把3G链接的路由项的Metric修改成49,这样会优先走这条路由信息。这样就能ping通google了。
最近不是很顺利,放个转运风车,点一下转转运。Cheer up.
出处:http://procoder.cnblogs.com
本作品由Jake Lin创作,采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。 任何转载必须保留完整文章,在显要地方显示署名以及原文链接。如您有任何疑问或者授权方面的协商,请给我留言。