第1章 客户/服务器网络介绍
1. 一个gopher的客户端。
2. socket的makefile。
3. 一个简单的服务器,socket.setsockopt() socket.bind(host,port), socket.listen(1), socket.accept()
第2章 网络客户端
1. 使用socket对象
# -*- coding: cp936 -*-
import socket
#创建socket对象
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#连接
s.connect(('www.cainiao8.com',80))
#获取信息
print 'getsockname(): ' , s.getsockname()
print 'getpeername():' , s.getpeername()
其中socket.AF_INET是IPV4的协议族。TCP对应SOCK_STREAM, UDP对应SOCK_DGRAM。
2. 根据服务名称获取端口号码
>>> socket.getservbyname('http')
80
3. socket的异常处理
socket可能抛出4种异常:errot、gaierror、herror、timeout。在写操作之后最好使用shutdown()来确保真的没有异常发生。第2章的shutdown.py例程介绍了异常的捕获。shutdownfile.py介绍了使用makefile()之后使用文件方式使用socket时候对异常的捕获,异常仍然是socket.error,作者建议尽量不要在使用文件方式的时候用缓冲器。
4. UDP
使用UDP的主要区别:
l socket.socket(socket.AF_INET, socket.SOCK_DGRAM)。
l 可以不用connect,直接 s.sendto(‘’, (host, port))
详见第2章的udp.py和udptime.py。
第3章 网络服务器
1. 建立服务器的步骤:
1) 建立socket对象
2) 设置socket选项
cockopts.py文件可以列出当前系统下Python支持的socket选项。
import socket
solist = [x for x in dir(socket) if x.startswith('SO_')]
solist.sort()
for x in solist:
print x
在调试程序的时候,最常用的是将SO_REUSEADDR设置为True,代表在程序进程结束后立即释放进程占用的端口。
3) 绑定端口
例如绑定80端口,bind的第一个参数是IP地址:
s.bind(‘’,80)
4) 侦听连接
s.listen(5)
表示允许的队列中等待的连接数。
2. 接受连接
basicserver.py是一个简单的服务器,在收到连接的时候简单的print一条信息,并且将连接关闭。主要源代码如下:
host = '' # Bind to all interfaces
port = 51423
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((host, port))
s.listen(1)
while 1:
clientsock, clientaddr = s.accept()
print "Got connection from", clientsock.getpeername()
clientsock.close()
在Windows下使用IDLE运行会造成IDLE假死,直接双击运行没问题。如下图:
3. 异常处理
考虑到服务器的容错能力,errorserver.py使用了各种try将程序包围起来。
4. UDP
不需要listen。大致代码如下:
while 1:
try:
message, address = s.recvfrom(8192)
s.sendto(message, address)
5. inetd或xinetd
像写普通Python程序一样写服务器。
6. syslog
记录信息,Windows没有syslog。
7. 避免死锁
使用一个客户端和服务器端的例子来说明一种死锁的可能情况。客户端试图向服务器发送一个大数据,而服务器每次读取一部分数据后给客户端发送一个回应,但是由于客户端的设计是在发送完之后才进行读取,因此服务器发送的回应被缓存。如此一来,缓存逐渐达到系统极限。服务器端由于缓存不足而无法再发送,卡在sendall一处,客户端试图发送数据,但是服务器端卡了,无法进行recv,所以客户端也卡在这里,形成死锁。
第4章 域名系统
1. 执行基本的DNS查询
使用socket模块的getaddrinfo()函数,它返回一个元组的列表。
>>> import socket
>>> res = socket.getaddrinfo('google.com',None)
>>> print res
[(2, 0, 0, '', ('74.125.127.100', 0)), (2, 0, 0, '', ('74.125.67.100', 0)), (2, 0, 0, '', ('74.125.45.100', 0))]
2. 反向查找
使用socket模块的gethostbyaddr:
>>> import socket
>>> res = socket.getaddrinfo('google.com',None)
>>> res
[(2, 0, 0, '', ('74.125.67.100', 0)), (2, 0, 0, '', ('74.125.45.100', 0)), (2, 0, 0, '', ('74.125.127.100', 0))]
>>> res = socket.gethostbyaddr('74.125.67.100')
>>> res
('gw-in-f100.google.com', [], ['74.125.67.100'])
反向查找的结果可能是对方IP刻意伪造的,gethostbyaddr-paranoid.py在反向查找之后再进行正向的查找,可以确保信息的正确性。
书后面介绍了利用PyDNS进行高级的DNS查找功能,搞了老半天,不过没安装成功。
第5章 高级网络操作
1. 半开放socket
只可以发送或者接收的socket。shutdown(0)表示禁止将来读,shutdown(1)表示禁止将来写,2表示禁止读和写。0和1的效果可以累加。
2. 超时
timeoutserver.py演示了超时的用法,调用settimeout方法,主要代码如下:
#建立socket,绑定到端口
while 1:
try:
clientsock, clientaddr = s.accept()
……
clientsock.settimeout(5)
如果客户端在限定时间之内没有发送内容,连接就会被服务器断开。
3. 广播
接收端需要设置socket广播选项:
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
发送方除了设置那以上选项外,sendTo的参数不再使用IP地址,而是使用'<broadcast>'。
4. poll
避免在socket上的I/O将程序阻塞。pollclient.py介绍了poll的使用方法:
……
#p为poll对象。
p = select.poll()
#注册到感兴趣的socket上。
p.register(s.fileno(), select.POLLIN | select.POLLERR | select.POLLHUP)
while 1:
#每50毫秒返回一次,如果没有结果就返回空列表。
results = p.poll()
if len(results):
if results[0][1] == select.POLLIN:
data = s.recv(4096)
if not len(data):
print("/rRemote end closed connection; exiting.")
break
# Only one item in here -- if there's anything, it's for us.
sys.stdout.write("/rReceived: " + data)
sys.stdout.flush()
else:
print "/rProblem occured; exiting."
sys.exit(0)
#每50毫秒要执行的操作。
spin()