11.1.1 寻址,协议簇和套接字类型 套接字是程序在本地或者经过互联网来回传递数据所用通讯通道的一个端点。 套接字主要有两个属性来控制如何发送数据:协议簇(address family)控制所用的OSI网络层协议,套接字(socket type)控制传输层协议。 Python支持三个地址簇。最经常使用的是AF_INET,用于IPv4 Internet寻址。AF_INET6应用IPv6 Internet寻址。 AF_UNIX是UNIX域套接字(UNIX Domain Sockets, UDS)的地址簇,这是一种POSIX兼容系统上的进程间通讯协议。UDS的实现一般容许操做系统直接从进程向进程传递数据,而不经过网络栈。 UDS仅限于同一系统上的进程。相比其余IPC,使用UDS的优点在于,它与IP网络应用的编程接口是同样的。 套接字类型每每是SOCK_DGRAM或SOCK_STREAM,SOCK_DGRAM用于UDP,SOCK_stream用于TCP。UDP不须要传输握手过程,可靠性较低,TCP确保每条消息指传输一次,并且按照正确的顺序传送。因为增长了可靠性,可能引入额外的延迟。大多数传送大量数据的应用协议(HTTP)都是创建在TCP基础上,UDP一般用于顺序不过重要的协议,如DNS或者广播。 在网络上查找主机 socket包含一些函数与网络上的域名服务交互,使得程序能够将服务器的主机名转换为其数字网络地址。应用使用地址来链接一个服务器前并不须要显示的转换地址,不过报错是提供数字地址颇有用。 import socket print socket.gethostname() 返回的名字取决于当前系统的网络设置。 >>> ================================ RESTART ================================ >>> ruby 使用gethostbyname()访问操做系统主机名解析API,将服务器名称转换为其数字地址。 import socket for host in ['homer', 'www', 'www.python.org','www.caoqing123.com']: try: print '%s : %s' % (host,socket.gethostbyname(host)) except socket.error, msg: print '%s : %s' % (host, msg) 若是当前系统的DNS配置在搜索中包括一个或多个域,名字参数不要求彻底限定名。若是名字没法找到,会返回socket error类型的异常。 >>> ================================ RESTART ================================ >>> homer : [Errno 11004] getaddrinfo failed www : 121.14.228.36 www.python.org : 82.94.164.162 www.caoqing123.com : [Errno 11004] getaddrinfo failed 要访问服务器的更多信息,可使用函数gethostname_ex(),能够返回服务器的标准主机名,全部别名,以及能够用来到达这个主机的全部可用IP地址。 import socket for host in ['homer', 'www', 'www.python.org','www.caoqing123.com']: print host try: hostname, alias, address = socket.gethostbyname_ex(host) print 'Hostname :', hostname print 'alias :', alias print 'address :', address except socket.error as msg: print 'ERROR:', msg print 若是获得一个服务器的全部已知地址,客户能够实现本身的负载均衡或故障恢复算法。 >>> ================================ RESTART ================================ >>> homer ERROR: [Errno 11004] getaddrinfo failed www Hostname : 1st.dtwscache.glb0.lxdns.com alias : ['www.zte.com.cn', 'www.zte.com.cn.cdn20.com'] address : ['61.146.152.58', '121.14.228.36', '121.14.35.233'] www.python.org Hostname : www.python.org alias : [] address : ['82.94.164.162'] www.caoqing123.com ERROR: [Errno 11004] getaddrinfo failed 使用getfqdn()能够将一个部分名转换为彻底限定域名。 import socket for host in ['homer', 'www', 'www.python.org','www.caoqing123.com']: print '%6s : %s' % (host, socket.getfqdn(host)) 若是输入的是一个别名,返回的名字不必定与输入参数一致。 >>> ================================ RESTART ================================ >>> homer : homer www : www www.python.org : dinsdale.python.org www.caoqing123.com : www.caoqing123.com 若是获得一个服务器地址,可使用gethostbyaddr()完成一个逆向查找来获得主机名。 import socket hostname, alias, address = socket.gethostbyaddr('192.168.27.91') print 'Hostname :', hostname print 'Alias :', alias print 'Address :', address 查找服务信息 除了IP地址外,每一个套接字地址还包括一个端口号,不少应用能够在同一个端口上运行并监听一个IP地址,不过只有一个套接字可使用该地址的端口。 经过结合IP地址,协议和端口号,能够惟一的标识一个通讯通道,确保一个套接字发送的消息达到正确的目标。 网络服务的端口号和标准名可使用getservbyname()查找。 import socket from urlparse import urlparse for url in ['http://www.python.org', 'https://caoqing.com', 'pop3://xiaohuan.com', 'smtp://xiaobao.com', ]: parse_url = urlparse(url) port = socket.getservbyname(parse_url.scheme) print '%6s : %s' % (parse_url.scheme, port) >>> ================================ RESTART ================================ >>> http : 80 https : 443 pop3 : 110 smtp : 25 要逆向完成服务端口查找,可使用getservbyport()。 import socket import urlparse for port in [80, 443, 110, 25]: print urlparse.urlunparse( (socket.getservbyport(port), 'caoqing.com', '/', '', '', '') ) 要从任意地址构造服务URL,这个逆向查找就颇有用。 >>> ================================ RESTART ================================ >>> http://caoqing.com/ https://caoqing.com/ pop3://caoqing.com/ smtp://caoqing.com/ 可使用getprotobyname()获取分配给一个传输协议的端口号。 import socket def get_constants(prefix): return dict( (getattr(socket, n), n) for n in dir(socket) if n.startswith(prefix) ) protocols = get_constants('IPPROTO_') for name in ['icmp', 'udp', 'tcp']: proto_num = socket.getprotobyname(name) const_name = protocols[proto_num] print '%4s -> %2d (socket.%-12s = %2d)' % \ (name, proto_num, const_name, getattr(socket, const_name)) 协议码值是标准化的,做为常量在socket中定义,这些协议码都有前缀IPPROTO_。 >>> ================================ RESTART ================================ >>> icmp -> 1 (socket.IPPROTO_ICMP = 1) udp -> 17 (socket.IPPROTO_UDP = 17) tcp -> 6 (socket.IPPROTO_TCP = 6) 查找服务器地址 getaddrinfo()将一个服务的基本地址转换为一个元组列表,其中包含建议一个链接所需的所有信息。每一个元组的内容会有变化,包含不一样的网络簇或协议。 import socket def get_constants(prefix): return dict( (getattr(socket, n), n) for n in dir(socket) if n.startswith(prefix) ) families = get_constants('AF_') types = get_constants('SOCK_') protocols = get_constants('IPPROTO_') for response in socket.getaddrinfo('www.python.org', 'http'): family, socktype, proto, canonname, sockaddr = response print 'Family :', families[family] print 'Type :', types[socktype] print 'Protocol :', protocols[proto] print 'Canoniocal name :', canonname print 'Socket address :', sockaddr print 展现了如何查找www.python.org的链接信息。 >>> ================================ RESTART ================================ >>> Family : AF_INET Type : SOCK_STREAM Protocol : IPPROTO_IP Canoniocal name : Socket address : ('82.94.164.162', 80) getaddrinfo()有多个参数来过滤结果列表。host和port是必要参数。可选参数是family,socktype,proto和flags。这些可选值能够取0或socket定义的某个常量。 用IP地址表示 用C编写的网络程序使用数据类型struct sockaddr将IP地址表示为二进制值。在C表示和Python表示之间转换IPV4地址,可使用inet_aton()和inet_ntoa()。 import socket import binascii import struct import sys for string_address in ['127.0.0.1']: packed = socket.inet_aton(string_address) print 'Original :', string_address print 'Packed :', binascii.hexlify(packed) print 'Unpacked :', socket.inet_ntoa(packed) print 数据包格式中的四个字节能够传递到C库,经过网络彻底的传输。 >>> ================================ RESTART ================================ >>> Original : 127.0.0.1 Packed : 7f000001 Unpacked : 127.0.0.1 函数inet_aton和inet_ntoa都能处理IPv4和IPv6地址,根据传入的地址簇参数生成合适的格式。