[《Python2.1宝典》笔记] 15、19、25章

本文详细介绍了Python的网络编程,包括套接字的基础概念、处理地址和主机名的方法、创建与撤销套接字、发送和接收数据、套接字选项的设置与使用。还探讨了TCP和UDP套接字的连接与通信,以及SocketServer模块的应用,展示了如何处理Web浏览器请求。此外,还涵盖了图像处理的基础知识,如颜色和字体的处理,以及使用Python图像库PIL进行图像操作的方法。
摘要由CSDN通过智能技术生成

第十五章 联网

15.1联网背景

套接字是一个网络连接的端点。域名是为了方便记忆。

略,还有很多很基本的概念。


15.2处理地址和主机名

socket模块提供了几个函数处理主机名和地址。socket模块包装了C套接字库,并且与C版本一样,也支持所有种类的选项。模块内定义了直接映射到C等价的变量。

gethostname()将返回当前计算机的主机名。gethostbyname(name)将试图把指定主机名分解成IP地址。失败将产生异常。我测试的机器没有连接,抛出错误为:

socket.gaierror: (11001, 'getaddrinfo failed')

扩展格式gethostbyname_ex(name)将返回一个三元组,包含特定地址的主要主机名,相同IP地址的可选择机器名列表,以及一个主机界面上的其他IP地址列表:

>>> socket.gethostbyname('www.yahoo.com')

'64.58.76.178'

>>> socket.gethostbyname_ex('www.yahoo.com')

('www.yahoo.akadns.net',['www.yahoo.com'],

['64.58.76.178','64.58.76.176','216.32.74.52',

'216.32.74.50','64.58.76.179','216.32.74.53',

'64.58.76.177','216.32.74.51','216.32.74.55'])

gethostbyaddr(address)完成同样的任务,只不过参数是IP

getservbyname(service,protocol)获取一个服务名,如telnetftp,和一个协议,如tcpudp。并返回服务端口号。

>>> socket.getservbyname('http','tcp')

80

通常,非Python程序以32位的打包格式存储和使用IP地址:

inet_aton(ip_addr)

inet_ntoa(packed)

这两个函数在32位打包格式和IP地址字符串之间进行转换。如:

>>> socket.inet_aton('177.20.1.201')

'/261/024/001/311' # 一个4字节的字符串

>>> socket.inet_ntoa('/x7F/x00/x00/x01')

'127.0.0.1'

socket定义了几个保留IP地址:

INADDR_ANYINADDR_BROADCAST是保留的IP地址,用于任意IP和广播。INADDR_LOOPBACK引用127.0.0.1这个地址的环回设备。几个保留IP地址都是保留的32位格式。

getfqdn([name])函数返回主机名的合格域名,省略name则返回本机的。是Python2.0中的新特性:

>>> socket.getfqdn('')

'dialup84.lasal.net'


15.3与低层套接字通信

15.3.1创建和撤消套接字

socket模块的socket(family,type[,protocol])创建一个新的套接字对象。family常取AF_INET,偶尔也用其他值如AF_IPX,取决于平台。type最常用的是SOCK_STREAM(就是TCP)SOCK_DGRAM(就是UDP)

familytype的组合就可以指定一个协议了,但是仍然可以指定protocol参数,如IPPROTO_TCPIPPROTO_RAW。可用函数getprotobyname(proto)获取协议的代码:

>>> getprotobyname('tcp')

6

>>> IPPROTO_TCP

6

fromfd(fd,family,type[,proto])用于从打开的文件描述符(文件的fileno()方法获得)创建套接字对象。套接字对象的方法fileno()将返回这个套接字的文件描述符(一个整数)

用完套接字之后应该用close()方法关闭。显式的关闭较好,尽管可以自动关闭,使用弱引用?。也可用shutdown(how)方法关闭一端或两端,参数为0时停止接受,1停止发送,2双方停止。

15.3.2连接套接字

一个TCP连接,监听端先要bind(address)一个地址和端口的元组,之后调用listen([backlog])侦听传入连接,之后用accept()接受新的传入连接。

>>> s=socket(AF_INET,SOCK_STREAM)

>>> s.bind(('127.0.0.1',4444))

>>> s.listen(1)

>>> q,v=s.accept() #返回套接字q和地址v

上面的代码在未接到连接时会挂起。只要另外启动一个解释器连接即可,连接函数connect(address)

发送数据q.send('hello'),返回已发送的字节数

接受数据s.recv(1024),最多一次接受1024字节的数据,返回数据

"地址"是由IP地址和端口构成的二元组。

扩展函数connect_ex(address)如果遇到调用的错误则返回错误代码,成功返回0,而不会产生异常,更接近于C socket

调用listen时需要的参数是暂时无法响应时保存连接的队列长度,一般设为5socket.SOMAXCONN变量指明了最大允许的数量。

accept()方法返回一个地址,包含IP和端口。

UDP套接字不是面向连接的,但仍可用connect()

15.3.3发送和接收数据

send(string[,flags])发送指定数据到远程套接字。sendto(string [,flags],address)把指定字符串发送到特殊地址。通常send用于面向连接的,sendto用于无连接的。但是如果UDP套接字调用了connect(),就可以用send而不必是sendto了。

都返回实际发送的字节数。如下函数用于确保发送整个消息:

def safeSend(sock,msg):

sent=0

while msg:

i=sock.send(msg)

if i==-1: # Error

return -1

sent+=i

msg=msg[i:]

time.sleep(25) #等待队列为空

return sent

这样当一次发送不完时也可以持续的发送数据,并最终返回发送成功的字节数。

recv(bufsize[,flags])方法接收数据,如果有很多数据则只返回前bufsize字节的数据。recvfrom(bufsize[,flags])完成同样的任务,只是带有返回信息(data,(ipAddress,port))AF_INET套接字,可以看到信息的发源地。

sendsendtorecvrecvfrom都接受一个flags参数,默认值为0。可以把一些socket.MSG_*变量按位OR操作之后创建flags值。可用值的平台相关的。

MSG_OOB 处理超区段数据

MSG_DONTROUTE 不使用路由表,直接发送到界面

MSG_PEEK 在不从队列删除的情况下,返回等待数据

例如使用MSG_PEEK就可以偷偷的查看消息而不必从数据队列中删除。

makefile([mode[,bufsize]])方法返回一个类似文件的对象,该对象包装了这个套接字。这样可以使用文件的操作方法操作套接字。可选的modebufsize参数获取的值与内置的open函数相同。

经测试在Windows2000SP4+Python2.4.2下的UDP编程中,最大允许发送的UDP数据报为65527字节,而这个数据报是收不到的。最大能够接收的数据报大小为65507字节。在6550765527字节之间的数据报发送会丢失,发送端不会出错,接收端却不会收到。对于超过65527字节的数据报会发生socket.error错误,winsock错误号为10040,信息"Message too long" 。这个问题同样存在于Linux中。经测试linux-2.6.9内核中这个数据长度区间是6250165507字节。

Windows2000SP4+Python2.4.2下尝试在recv中使用MSG_OOB标志,但是返回10045winsock错误,提示"Operation not supported"。另外,如果还没有bind()就使用recv()接收数据会发生10022winsock错误,提示"Invalid argument"

15.3.4使用套接字选项

套接字getpeername()getsockname()方法将返回一个2元组,包含IP地址和端口。getpeername()返回远程套接字信息,getsockname()返回本地套接字信息。

默认情况下的套接字是阻塞的,例如发送缓冲区已满的情况下,send函数也会阻塞,直到可以把更多的数据写入到缓冲区。通过采用0值调用setblocking(flag)方法,就可以改变这种行为。

在非阻塞时,如果发现recv()方法没有返回数据则会发生错误socket.errorWinsock错误号为10035。在套接字运行中也可以随时改变阻塞状态。在非阻塞状态中,一般放入一个带有延时的循环中。在延时发生的时候,其他线程可以改变循环的进入条件,控制程序合法退出。

在非阻塞时,也可以在监听套接字上用selectpoll来检测是否有新的连接到达。

setsockopt(level,name,value)getsockopt(level,name[,buflen ])方法用于设置和返回套接字选项。level参数指定引用选项的层,如套接字层、TCP层、IP层。level的值以SOL_开头,如SOL_SOCKETSOL_TCP等。选项的名字指定了正在交换的选项,socket模块定义了读者的平台上可以使用的选项。

C版本的setsockopt要求用缓冲区传递value参数,但在Python中,这个选项输入数字,可直接用数字或用字符串(代表缓冲区)

getsockopt函数不指定buflen时会返回数字值,而提供了buflen时返回一个字符串(代表缓冲区),并且最大长度为buflen个字节。

各个平台的选项不同,各个选项的值类型也不尽相同。如设置发送缓冲区为65KB

>>> s.setsockopt(SOL_SOCKET,SO_SNDBUF,65535)

SOL_SOCKET的选项(选项名、值、说明)

SO_TYPE (只读) 套接字类型(SOCK_STREAM)

SO_ERROR (只读) 套接字的最后一个错误

SO_LINGER Boolean 如果数据存在则拖延close

SO_RCVBUF Number 接收缓冲区大小

SO_SNDBUF Number 发送缓冲区大小

SO_RCVTIMEO Time struct 接收超时

SO_SNDTIMEO Time struct 发送超时

SO_REUSEADDR Boolean 启用本地地址/端口的多个用户

SOL_TCP选项:

TCP_NODELAY Boolean 立即发送数据,不等待最小发送量

SOL_IP选项:

IP_TTL 0-255 包能够传送的最大转发器数

IP_MULTICAST_TTL 0-255 包能够广播的最大转发器数

IP_MULTICAST_IF inct_aton(ip) 选择界面,以便通过它传输

IP_MULTICAST_LOOP Boolean 启用发送器,以接收它发送的多

点传送包的副本

IP_ADD_MENBERSHIP ip_mreq 联接一个多点传送组

IP_DROP_MENBERSHIP ip_mreq 去掉多点传送组

15.3.5转换数字

平台字节序和网络字节序多半不同,所以通过网络传输之前需要先转换。nthol(x)ntohs(x)函数从网络数字转换到本地字节序。htonl(x)htons(x)函数从本地字节序转换到网络字节序。


15.4示例:多点传送的聊天应用程序

一个聊天程序,允许在共享的白板上绘画。

使用IP多播实现,代码太长见page224-228

在互联网上运行会有问题,因为有些路由器不允许IP多播的转发。而且设置TTL最好足够大。SO_REUSEADDR选项允许一个地址(IP,port)允许被绑定到多个socketIP_ADD_MEMBERSHIP连接一个IP多播组,退出多播组用IP_DROP_MENBERSHIP


15.5使用SocketServers

15.5.1SocketServer

TCPServerUDPServer都是SocketServer的子类。

SocketServer模块同时提供UnixStreamServer(TCPServer的子类)UnixDatagramServer(UDPServer的子类)。他们的父类相同,只是监听套接字是AF_UNIX族,而不是AF_INET

默认的套接字服务器一次处理一个连接,但可以使用ThreadingMixForkingMixIn类创建SocketServer的线程版本或分支版本。还有一些已经实现好的类:ForkingUDPServerForkingTCPServerThreadingUDPServerThreadingTCPServerThreadingUnixStreamServerThreadingUnixDatagramServer。线程版本只能在支持线程的操作系统上运行。分支版本也只可以在支持os.fork的平台上运行。

关于混合类见第7章;分支的解释见11章;线程见26章。

SocketServer以常规方式处理传入的连接,可以自己定义请求处理程序类,只要继承BaseRequestHandler类即可。然后将此类传递给构造函数即可。

import SocketServer

...#创建请求处理程序类

addr=('IP',port)

server=SocketServer.ThreadingTCPServer(addr,请求处理程序类)

server.serve_forever()

这样每次有新的连接时就创建一个"请求处理类"的实例对象,并调用他的handle()方法。代替用server_forever,也可用handle_request() ,可用于等待、接收单个连接。server_forever()仅限在无限循环中调用handle_request()

如果需要创建自己的套接字服务器类,需要实现如下方法:

__init__()函数中调用server_bind()方法,把self.socket绑定到正确的地址self.server_address。然后调用server_activate()激活服务器,默认情况下还会调用套接字的listen()方法。

在调用handle_request()serve_forever()之前,套接字服务器不会进行任何操作。handle_request()调用get_request(),并等待接受一个新的套接字连接,然后调用verify_request(request,client_address) ,以便查看服务器是否应该处理该连接(可以把这种方法用于访问控制-默认情况下,verify_request()总是返回真)。如果处理请求成功,则handle_request()会调用process_request(request,client_address),如果process_request()产生了异常,则调用handle_error(request, client_address)。默认时,process_request()简单的调用finish_request(request,client_address)

分支和线程混合类将会启动一个分支或线程来调用finish_requestfinish_request()是一个新的请求处理程序,接下来会调用他的handle()方法。

SocketServer创建一个新的请求处理程序时,会把处理程序的__init__函数传递给self变量。

SocketServer

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值