本文通过一些简单的攻略介绍python的核心网络库。 Python的socket模块提供了类方法和实例方法,二者的区别在于使用类方法时不需要创建套接字对象实例。 这是一种很直观的方法。例如,打印设备的IP地址不需要创建套接字对象,而只需调用套接字的类方法。但是,如果要把数据发送给服务器程序,那么创建一个套接字对象来处理具体的操作则更加自然。
1、打印设备名和 IPv4 地址首先,使用下面的命令导入Python中的socket库:
import socket调用socket库提供的gethostname()方法与gethostbyname()方法分别是获取主机名,通过主机名获取对应的IP地址
import socketdef print_machine_info():
host_name = socket.gethostname()
ip_address = socket.gethostbyname(host_name)
print "Host name: %s" % host_name
print "IP address: %s" % ip_address
if __name__ == '__main__':
print_machine_info()
运行结果:
Host name: WinXP-178
IP address: 172.16.6.9
2、获取远程设备的 IP 地址如果想知道远程设备的IP地址,可以使用内置的库函数gethostbyname(),其参数是远程设备的主机名。
import socketdef get_remote_machine_info():
remote_host = 'www.python.org'
try:
print "IP address: %s" %socket.gethostbyname(remote_host)
except socket.error, err_msg:
print "%s: %s" %(remote_host, err_msg)
if __name__ == '__main__':
get_remote_machine_info()
运行结果:
IP address: 151.101.72.223
3、将 IPv4 地址转换成不同的格式如果要使用低层网络函数,有时普通的字符串形式的IP地址并不是很有用,需要把它们转换成打包后的32位二进制格式。
Python的socket库提供了很多用来处理不同IP地址格式的函数,这里我们使用其中的两个:inet_aton()和inet_ntoa()。
import socketfrom binascii import hexlify
def convert_ip4_address():
for ip_addr in ['127.0.0.1', '192.168.0.1']:
packed_ip_addr = socket.inet_aton(ip_addr)
unpacked_ip_addr = socket.inet_ntoa(packed_ip_addr)
print "IP Address: %s => Packed: %s, Unpacked: %s"\
%(ip_addr, hexlify(packed_ip_addr), unpacked_ip_addr)
if __name__ == "__main__":
convert_ip4_address()
上面的代码使用for-in语句把两个字符串形式的IP地址转换成打包后的32位二进制格式,而且还调用了binascii模块中的hexlify函数,以十六进制形式表示二进制数据。
4、通过指定的端口和协议找到服务名如果知道网络服务使用的端口,可以调用socket库中的getservbyport()函数来获取服务的名字。调用这个函数时可以根据情况决定是否提供协议名。
实战一下:
import socketdef find_service_name():
protocolname = 'tcp'
for port in [80, 25]:
print "port: %s => service name: %s" %(port, socket.getservbyport(port, protocolname))
print "port: %s => service name: %s" %(53, socket.getservbyport(53, 'udp'))
if __name__ == '__main__':
find_service_name()
运行结果:
port: 80 => service name: http
port: 25 => service name: smtp
port: 53 => service name: domain
5、主机字节序和网络字节序之间相互转换编写低层网络应用时,或许需要处理通过电缆在两台设备之间传送的低层数据。在这种操作中,需要把主机操作系统发出的数据转换成网络格式,或者做逆向转换,因为这两种数据的表示方式不一样。 我们调用ntohl()和htonl()类函数来转换不同格式的数据。
实战一下
import socketdef convert_integer():
data = 1234
#32-bit
print "Original: %s => Long host byte order: %s, Network byte order: %s"\
%(data, socket.ntohl(data), socket.htonl(data))
#16-bit
print "Original: %s => Short host byte order: %s, Network byte order: %s"\
%(data, socket.ntohs(data), socket.htons(data))
if __name__ == '__main__':
convert_integer()
运行结果
Original: 1234 => Long host byte order: 3523477504, Network byte order: 3523477504
Original: 1234 => Short host byte order: 53764, Network byte order: 53764
在这个实战中,我们以整数为例,演示了如何把它转换成网络字节序和主机字节序。 socket库中的类函数ntohl()把网络字节序转换成了长整形主机字节序。函数名中的n表示网络; h表示主机; l表示长整形; s表示短整形,即16位。
6、设定并获取默认的套接字超时时间有时,你需要处理socket库某些属性的默认值,例如套接字超时时间。
你可以创建一个套接字对象实例,调用gettimeout()方法获取默认的超时时间,调用settimeout()方法设定一个超时时间。这种操作在开发服务器应用时很有用。
import socketdef test_socket_timeout():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print "Default socket timeout: %s" %s.gettimeout()
s.settimeout(100)
print "Current socket timeout: %s" %s.gettimeout()
if __name__ == '__main__':
test_socket_timeout()
运行结果:
Default socket timeout: None
Current socket timeout: 100.0
在这段代码片段中,首先创建了一个套接字对象。套接字构造方法的第一个参数是地址族,第二个参数是套接字类型。然后,调用gettimeout()方法获取套接字超时时间,再调用settimeout()方法修改超时时间。传给settimeout()方法的参数可以是秒数(非负浮点数)也可以是None。这个方法在处理阻塞式套接字操作时使用。如果把超时时间设为None,则禁用了套接字操作的超时检测。
7、优雅地处理套接字错误在网络应用中,经常会遇到这种情况:一方尝试连接,但另一方由于网络媒介失效或者其他原因无法响应。 Python的socket库提供了一个方法,能通过socket.error异常优雅地处理套接字错误。
我们来编写几个try-except代码块,每个块对应一种可能发生的错误。为了获取用户输入,可以使用argparse模块。这个模块的功能很强大,而不仅是可以使用sys.argv解析命令行参数。这些try-except代码块分别演示了常见的套接字操作,例如创建套接字对象、连接服务器、发送数据和等待应答。
#coding=utf-8import sys
import socket
import argparse
def main():
parse = argparse.ArgumentParser(description='Socket Error Examples')
parse.add_argument('--host', action="store", dest="host", required=False)
parse.add_argument('--port', action="store", dest="port", type=int, required=False)
parse.add_argument('--file', action="store", dest="file", required=False)
given_args = parse.parse_args()
host = given_args.host
port = given_args.port
filename = given_args.file
# 创建 socket
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
except socket.error, e:
print "Error creating socket: %s" % e
sys.exit(1)
# 连接给定的 host/port
try:
s.connect((host, port))
except socket.gaierror, e:
print "Address-related error connecting to server: %s" % e
sys.exit(1)
except socket.error, e:
print "Connection error: %s" % e
sys.exit(1)
# 发送数据
try:
s.sendall("GET %s HTTP/1.0\r\n\r\n" % filename)
except socket.error, e:
print "Error sending data: %s" % e
sys.exit(1)
while 1:
# 等待从远程主机上接收数据
try:
buf = s.recv(2048)
except socket.error, e:
print "Error receiving data: %s" %e
sys.exit(1)
if not len(buf):
break
#打印接收数据
sys.stdout.write(buf)
if __name__ == '__main__':
main()
在Python中,可以使用argparse模块把命令行参数传入脚本以及在脚本中解析命令行参数。这个模块在Python 2.7中可用。如果使用较旧版本的Python,这个模块可以到“ Python包索引”(Python Package Index, 简称PyPI)中获取,使用easy_install或pip安装。
在linux中可以使用 $ python 1_7_socket_errors.py host=<HOST> --port=<PORT> --file=<FILE>的格式运行,例如:$ python 1_7_socket_errors.py --host=www.pytgo.org --port=8080 --file=1_7_socket_errors.py
而如果在pycharm中,则需要手动指定参数,
Run->Edit Configurations...->ScriptParames
和vs类似,都不用输入程序名字,直接输入参数即可。
这里输入:--host=www.python.org --port=80 --file=1_7_socket_errors.py
8、修改套接字发送和接收的缓冲区大小很多情况下,默认的套接字缓冲区大小可能不够用。此时,可以将默认的套接字缓冲区大小改成一个更合适的值。
我们要使用套接字对象的setsockopt()方法修改默认的套接字缓冲区大小。首先,定义两个常量: SEND_BUF_SIZE和RECV_BUF_SIZE。然后在一个函数中调用套接字实例的setsockopt()方法。修改之前,最好先检查缓冲区大小是多少。注意,发送和接收的缓冲区大小要分开设定
#coding=utf-8import socket
SEND_BUF_SIZE = 4096
RECV_BUF_SIZE = 4096
def modify_buff_size():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
bufsize = sock.getsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF)
print "Buffer size [Before]:%d" %bufsize
sock.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)
sock.setsockopt(
socket.SOL_SOCKET,
socket.SO_SNDBUF,
SEND_BUF_SIZE)
sock.setsockopt(
socket.SOL_SOCKET,
socket.SO_RCVBUF,
RECV_BUF_SIZE)
bufsize = sock.getsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF)
print "Buffer size [After]:%d" %bufsize
if __name__ == '__main__':
modify_buff_size()
运行结果
Buffer size [Before]:8192 Buffer size [After]:4096在套接字对象上可调用方法getsockopt()和setsockopt()分别获取和修改套接字对象的属性。 setsockopt()方法接收三个参数: level、 optname和value。其中, optname是选项名, value是该选项的值。第一个参数所用的符号常量(SO_*等)可在socket模块中查看。
9、把套接字改成阻塞或非阻塞模式 默认情况下,TCP套接字处于阻塞模式中。也就是说,除非完成了某项操作,否则不会把控制权交还给程序。例如,调用connect() API后,连接操作会阻止程序继续往下执行,直到连接成功