python网络安全的书信_马克的Python学习笔记#网络和Web编程 3

创建一个UDP服务器

假设我们想实现一个采用UDP协议同客户端进行通信的服务器,同TCP一样,我们可以利用socketsever库也很容易创建出UDP服务器。比如下面这个简单的时间服务器程序:

from socketserver import BaseRequestHandler, UDPServer

import time

class TimeHandler(BaseRequestHandler):

def handle(self):

print('Got connection from', self.client_address)

# Get message and client socket

msg, sock = self.request

resp = time.ctime()

sock.sendto(resp.encode('ascii'), self.client_address)

if __name__ == '__main__':

serv = UDPServer(('', 20000), TimeHandler)

serv.serve_forever()

像上一节一样,这里需要定义一个特殊的处理类,其中要实现一个handle()方法来处理客户端的连接,这里的request书信就是一个元组,包含了这个服务器收到的数据报以及代表了底层的socket对象。client_address包含的是客户端的地址

要调试这个服务器程序,先运行上面的脚本,然后另外开一个Python进程并向服务端程序发送消息:

>>> from socket import socket, AF_INET, SOCK_DGRAM

>>> s = socket(AF_INET, SOCK_DGRAM)

>>> s.sendto(b'', ('localhost', 20000))

0

>>> s.recvfrom(8192)

(b'Wed Aug 15 20:35:08 2012', ('127.0.0.1', 20000))

>>>

一个典型的UDP服务器接收到达的数据报(消息)和客户端地址。如果服务器需要做应答, 它要给客户端回发一个数据报。对于数据报的传送, 你应该使用socket的 sendto() 和 recvfrom() 方法。 尽管传统的 send() 和 recv() 也可以达到同样的效果, 但是前面的两个方法对于UDP连接而言更普遍。

由于没有底层的连接,UPD服务器相对于TCP服务器来讲实现起来更加简单。 不过,UDP天生是不可靠的(因为通信没有建立连接,消息可能丢失)。 因此需要由你自己来决定该怎样处理丢失消息的情况。这个已经不在本书讨论范围内了, 不过通常来说,如果可靠性对于你程序很重要,你需要借助于序列号、重试、超时以及一些其他方法来保证。 UDP通常被用在那些对于可靠传输要求不是很高的场合。例如,在实时应用如多媒体流以及游戏领域, 无需返回恢复丢失的数据包(程序只需简单的忽略它并继续向前运行)。

UDPServer 类是单线程的,也就是说一次只能为一个客户端连接服务。 实际使用中,这个无论是对于UDP还是TCP都不是什么大问题。 如果你想要并发操作,可以实例化一个 ForkingUDPServer 或 ThreadingUDPServer 对象:

from socketserver import ThreadingUDPServer

if __name__ == '__main__':

serv = ThreadingUDPServer(('',20000), TimeHandler)

serv.serve_forever()

直接使用socket来实现一个UDP服务器也不难,下面是一个例子:

from socket import socket, AF_INET, SOCK_DGRAM

import time

def time_server(address):

sock = socket(AF_INET, SOCK_DGRAM)

sock.bind(address)

while True:

msg, addr = sock.recvfrom(8192)

print('Got message from', addr)

resp = time.ctime()

sock.sendto(resp.encode('ascii'), addr)

if __name__ == '__main__':

time_server(('', 20000))

从CIDR地址中生成IP地址的范围

假设我们有一个类似于"123.45.67.89/27"这样的CIDR网络地址,我们向生成由该地址可表示的全部IP地址范围。对于这种情况来说,我们可以利用ipaddress模块来处理这样的计算:

>>> import ipaddress

>>> net = ipaddress.ip_network('123.45.67.64/27')

>>> net

IPv4Network('123.45.67.64/27')

>>> for a in net:

... print(a)

...

123.45.67.64

123.45.67.65

123.45.67.66

123.45.67.67

123.45.67.68

...

123.45.67.95

>>>

>>> net6 = ipaddress.ip_network('12:3456:78:90ab:cd:ef01:23:30/125')

>>> net6

IPv6Network('12:3456:78:90ab:cd:ef01:23:30/125')

>>> for a in net6:

... print(a)

...

12:3456:78:90ab:cd:ef01:23:30

12:3456:78:90ab:cd:ef01:23:31

12:3456:78:90ab:cd:ef01:23:32

12:3456:78:90ab:cd:ef01:23:33

12:3456:78:90ab:cd:ef01:23:34

12:3456:78:90ab:cd:ef01:23:35

12:3456:78:90ab:cd:ef01:23:36

12:3456:78:90ab:cd:ef01:23:37

>>>

network对象同样也支持像数组一样的索引操作:

>>> net.num_addresses

32

>>> net[0]

IPv4Address('123.45.67.64')

>>> net[1]

IPv4Address('123.45.67.65')

>>> net[-1]

IPv4Address('123.45.67.95')

>>> net[-2]

IPv4Address('123.45.67.94')

>>>

并且还可以执行检查成员归属的操作:

>>> a = ipaddress.ip_address('123.45.67.69')

>>> a in net

True

>>> b = ipaddress.ip_address('123.45.67.123')

>>> b in net

False

>>>

IP地址加上网络号可以用来指定一个IP接口:

>>> inet = ipaddress.ip_interface('123.45.67.73/27')

>>> inet.network

IPv4Network('123.45.67.64/27')

>>> inet.ip

IPv4Address('123.45.67.73')

>>>

ipaddress 模块有很多类可以表示IP地址、网络和接口。 当你需要操作网络地址(比如解析、打印、验证等)的时候会很有用。

要注意的是,ipaddress 模块跟其他一些和网络相关的模块比如 socket 库交集很少。 所以,你不能使用 IPv4Address 的实例来代替一个地址字符串,你首先得显式的使用 str() 转换它。例如:

>>> a = ipaddress.ip_address('127.0.0.1')

>>> from socket import socket, AF_INET, SOCK_STREAM

>>> s = socket(AF_INET, SOCK_STREAM)

>>> s.connect((a, 8080))

Traceback (most recent call last):

File "", line 1, in

TypeError: Can't convert 'IPv4Address' object to str implicitly

>>> s.connect((str(a), 8080))

>>>

参考书目:

《Python CookBook》作者:【美】 David Beazley, Brian K. Jones

Github地址:yidao620c/python3-cookbook​github.com

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值