"IP地址的长度为32位,分为4段,每段8位,用十进制数字表示,每段数字范围为0~255,段与段之间用句点隔开。"
由此我们了解到,IP地址实际上是一个32位正整数,在C#中可以使用uint类型来表示,但SQLServer数据库里好像没有对应的类型;转而使用数据库支持的int类型的话,则会出现溢出的情况;因此我们做出妥协:使用long(bigint)类型。
TIP:
int取值范围:-2,147,483,648
到 2,147,483,647
uint取值范围:0 到
4,294,967,295
long取值范围:-9,223,372,036,854,775,808 到
9,223,372,036,854,775,807
那么如何将IP地址转为整数呢?我们看到IPAddress类中有一个"[否决的]"实例属性Address,这个属性的确可以返回一个long值,但是测试一下,得到的数据确实这样的:
"127.0.0.1"
-> 16777343
"127.0.0.2" –>
33554559
的确该让它"否决",这样的整数对我们来说毫无意义,我们是无法通过这样的方法比较传入的IP是否介于两个IP值之间的。
那么只有自己动手了,我们将通过IPAddress类的GetAddressBytes()实例方法获取IP的4个段的值,然后将它们组合为一个整数,下面将提供这个扩展方法:
///
///
将IP地址转为整数形式
///
/// 整数
public static long 转换为整数(this
IPAddress ip)
{
int x = 3;
long o = 0;
foreach (byte f
in ip.GetAddressBytes())
{
o += (long)f 《 8 *
x--;
}
return
o;
}
你可以这样使用这个扩展方法:
IPAddress.Parse("127.0.0.1")。转换为整数()
这里还有一个用于逆转换的扩展方法,用于将long转回IPAddress:
///
www.tygj123.com
/// 将整数转为IP地址
///
/// IP地址
public static IPAddress
转换为IP地址(this long l)
{
var b = new byte[4];
for (int i = 0;
i < 4; i++)
{
b[3 - i] = (byte)(l 》 8 * i &
255);
}
return new
IPAddress(b);
}
这样我们就可以通过计算得到正确并有意义的整数了:
"127.0.0.1" ->
2130706433
"127.0.0.2" –>
2130706434
OK,确立了方案核心,下面开始设计SQLServer数据表:
这样设计后,在添加时将起始和终止IP地址转为long类型并存入,并指定一个过期时间。
在验证时只需要获取所有未过期的条目,比较传入的IP地址是否介于起始值和终止值之间即可。
以往通过字符串存储和验证的方案中,屏蔽时要么屏蔽一个精确的IP地址,要么就屏蔽一段或两段IP,如"192.168.*.*",要想屏蔽"192.168.1.200"到"192.168.4.64"之间的IP的话,将会非常麻烦;
而我们这样设计就可以轻松实现:"192.168.1.200"在数据库里存储的是"3232235976","192.168.4.64"在数据库中是"3232236608",即使使用肉眼也能极快地判断传入的地址是否介于它们之间,更不要说计算机查询了。
下面为数据表生成EDM模型:
添加IP屏蔽记录的代码:
///
///
添加一个新的IP屏蔽区段
///
/// 起始IP,如61.51.200.0
///
终止IP,如61.51.255.255
/// 屏蔽截止时间
/// ID号
public static Guid
添加(string IP区段起始值, string IP区段终止值, DateTime 过期时间)
{
var id =
Guid.NewGuid();
var sip = IPAddress.Parse(IP区段起始值)。转换为整数();
var
eip = IPAddress.Parse(IP区段终止值)。转换为整数();
using (var c = new
SiteMainEntities())
{
//检测是否已存在相同的IP屏蔽记录
var a =
c.IP地址屏蔽。Where(f => f.区段起始值 == sip && f.区段终止值 ==
eip);
//如果存在则更新其过期时间
if (a.Count()>0)
{
var l =
a.First();
if (l.过期时间 < 过期时间) l.过期时间 =
过期时间;
}
//不存在则正常添加一个新的屏蔽记录 www.qcwy123.com
else c.AddToIP地址屏蔽(new IP地址屏蔽 { ID = id, 过期时间 = 过期时间, 区段起始值 = sip,
区段终止值 = eip });
c.SaveChanges();
}
return
id;
}
检测指定IP地址是否被屏蔽的代码:
///
///
检测指定IP地址是否已受到屏蔽
///
/// 要检测的IP地址
/// 是否属于已屏蔽的IP
public
static bool 检测是否被屏蔽(string IP地址)
{
var ip =
IPAddress.Parse(IP地址)。转换为整数();
using (var c = new
SiteMainEntities())
{
return c.IP地址屏蔽。Count(f => f.过期时间 >
DateTime.Now && ip >= f.区段起始值 && ip <= f.区段终止值) >
0;
}
}
这种方案比起以往的字符串验证方案来说优雅了许多,并可以提高数据库查询的效率,建议各位在日后的网站开发中都采用此方案。
相关文章
- 09-06架构师的成长之路初片~网络之网络地址转换(NAT) (Easy IP)
- 09-06006、 【实战】 在宿主机的浏览器,输入宿主机的 ip地址+映射端口,访问Jenkins容器
- 09-06织梦调用来访者的IP地址
- 09-06查看某个ip地址接在交换机的哪个接口
- 09-06CodeGo.net>关闭System.Net.Sockets.TcpClient杀死其他TCPClient在相同的IP地址的连接
- 09-06python(28)获得网卡的IP地址
- 09-06IP地址和MAC地址绑定的必要性
- 09-06SNAT,是源地址转换,其作用是将ip数据包的源地址转换成另外一个地址
- 09-06delphi中判断IP地址输入的合法性
- 09-06怎样获取本机的ip地址