python的socket编程
socket是基于(TCP、UDP、IP)的通讯、也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求,通讯过程由服务端的socket处理信息发送,由客户端的socket处理信息接收。
socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,对于文件用【打开】【读写】【关闭】模式来操作。socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是针对服务器端和客户端Socket进行的操作(读/写IO、打开、关闭)
python中的内置模块socket实现了python的socket编程:
python中的socket模块的内置方法:
socket.getaddrinfo(host,port,family = 0,type = 0,proto = 0,flags = 0)
将host/port参数转换为5元组序列,其中包含用于创建连接到该服务的套接字的所有必要参数。 host是域名。port是数字端口号。
可以选择指定family、type和proto参数,以缩小返回的地址列表。将零作为每个参数的值传递,将选择完整的结果范围。flags参数可以是一个或多个AI_x常量,并且会影响计算和返回结果的方式。例如,AI_NUMERICHOST将禁用域名解析,如果主机是域名,则会引发错误。
函数返回具有5个元组的列表:(family, type, proto, canonname, sockaddr)
其中,family、type和proto都是整数,如果AI_CANONNAME是flags参数的一部分,canonname将是一个表示主机规范名称的字符串;否则为空。sockaddr是一个描述套接字地址的元组(将host转换为IPv4或IPv6地址格式)。其格式取决于返回的系列,如果为AF_INET返回(address,port)2元祖,如果为AF_INET6返回(address,port,flow info,scope id)4元祖
socket.getfqdn([name]):返回一个完全合格的域名名称。如果省略name或为空返回本地主机名
socket.gethostbyname(hostname):返回将主机名解析转换为IPv4格式的字符串。不支持IPv6名称解析
socket.gethostbyname_ex(hostname):返回(hostname,aliaslist,ipaddrlist)三元组,hostname是响应给定hostname的主要主机名,aliaslist是同一地址的备用主机名的列表(可能为空),ipaddrlist是同一接口上的IPv4 / v6地址列表主机(通常但不总是一个地址)。
socket.gethostbyaddr(ip_address):返回(hostname,aliaslist,ipaddrlist)三元组,ip_address是响应给定ip_address的主要主机名,aliaslist是同一地址的备用主机名的列表(可能为空),ipaddrlist是同一接口上的IPv4 / v6地址列表主机(最有可能只包含一个地址)。该函数支持IPv4和IPv6。
socket.gethostname():返回当前机器的主机名。注意并不总是返回完全限定的域名,使用getfqdn(socket.gethostname())返回合格的域名名称
socket.getnameinfo(sockaddr,flags):将套接字地址sockaddr转换为(host,port)2元组。根据flags的设置,结果可以包含主机中的完全限定域名或数字地址表示。
socket.getprotobyname(protocolname):将Internet协议名称(例如‘icmp‘)转换为适合作为socket.socket()第三个参数的常量。这通常仅在以“原始”模式(SOCK_RAW)打开的套接字时需要; 对于正常的套接字模式,如果省略协议或为零,则自动选择正确的协议。
socket.getservbyname(servicename[,protocolname]):将Internet服务名称和协议名称转换为该服务的端口号。可选的协议名称(如果给定)应为‘tcp‘或者‘udp‘,否则任何协议都将匹配。
socket.getservbyport(port[,protocolname]):将Internet端口号和协议名称转换为该服务的服务名称。可选的协议名称(如果给定)应为‘tcp‘或者‘udp‘,否则任何协议都将匹配。
socket.inet_aton(ip_string):将IPv4地址转换为32位打包二进制格式的字节对象,长度为四个字符。如果IPv4地址字符串无效会引发OSError,该函数不支持IPv6
socket.inet_ntoa(packed_ip):将32位打包的IPv4地址转换为标准的字符串。如果传递的字节长度不是4个字节,则会引发OSError,该函数不支持IPv6
socket.inet_pton(address_family,ip_string):将IP地址ip_string从其address_family(AF_INET或AF_INET6)系列特定的格式转换为压缩的二进制格式。如果IP地址字符串ip_string无效,将引发OSError。
socket.inet_ntop(address_family,packed_ip):将打包的IP地址转换为其特定于address_family(AF_INET或AF_INET6)系列的字符串表示形式。字节对象压缩的IP不是指定地址系列的正确长度,将引发ValueError。调用inet_ntop()出错将引发OSError。
socket.getdefaulttimeout():返回新建套接字对象的默认超时(以秒为单位)。值为None表示新套接字对象没有超时。首次导入套接字模块时,默认为None。
socket.setdefaulttimeout(timeout):设置新套接字对象的默认超时(以秒为单位)。首次导入套接字模块时,默认为None。
下面4个是仅Unix可用的方法:
socket.sethostname(name):将计算机的主机名设置为name。如果没有足够的权限将引发OSError。
socket.if_nameindex():返回网络接口信息列表(索引int,名称字符串)元组。 如果系统调用失败印发OSError。
socket.if_nametoindex(if_name):返回与接口名称对应的网络接口索引号。 如果不存在具有给定名称的接口引发OSError。
socket.if_indextoname(if_index):返回与接口索引号对应的网络接口名称。 如果没有与给定索引的接口存在引发OSError。
python中socket模块的Socket对象:下面三个类都可以创建Socket对象
socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0, fileno=None)
family: 套接字家族,可以使用AF_INET(默认值), AF_INET6,AF_UNIX,AF_CAN,AF_PACKET和AF_RDS
type: 套接字类型是面向连接的SOCK_STREAM(TCP)还是非连接SOCK_DGRAM(UDP),或者其他的SOCK_*类型的常量
proto: 协议编号,一般不填使用默认值0.参考socket模块的getprotobyname()方法
fileno:套接字的文件描述符
socket.socketpair([family [,type [,proto ] ] ] )
使用给定的地址系列,套接字类型和协议编号构建一对连接的套接字对象。地址族,套接字类型和协议号与上述socket()功能相同。如果在平台上定义的话,默认的系列是AF_UNIX;否则,默认的系列是AF_INET。新创建的套接字是不可继承的。
socket.create_connection(address [,timeout [,source_address ] ] )
连接到地址(host,port)的TCP服务并返回Socket对象。这是一个比socket.connect()更高级别的函数:如果主机是非数字主机名,它将尝试用AF_INET和AF_INET6解析它,然后依次尝试连接到所有可能的地址,直到连接成功。这使得编写既兼容IPv4又兼容IPv6的客户端变得容易。
如果提供了source_address(一个(host, port)2元祖),则在连接之前,要将套接字绑定到source_address
Socket对象(内建)方法
服务器端套接字
s.bind(address) 绑定地址(host,port)到套接字,用于指定服务的监听端口,host可以使用socket.gethostname()来获取本地主机名, address地址的格式取决于地址族。在AF_INET下,以元组(host,port)的形式表示。
s.listen(backlog) 开始TCP监听。backlog指定在拒绝连接之前,服务器可以挂起的最大连接数量。该值至少为1,大部分应用程序设为5就可以了。
s.accept() 被动接受TCP客户端连接,(阻塞式)等待连接的到来,接受连接并返回(conn,address,其中conn是新的套接字对象,可以用来接收和发送数据。address是连接客户端的地址。
客户端套接字
s.connect(address)主动TCP连接到address。一般address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。
s.connect_ex()同connect(),不同的是该函数有返回值(int类型),连接成功时返回0,失败时候返回对应编码,而不是抛出异常
公共用途的套接字函数
s.close() 关闭套接字
s.recv(bufsize[.flag]) 接收TCP数据,数据以字节字符串形式返回,bufsize指定每次接收的最大数据量。flag提供有关消息的其他信息,通常可以忽略。
s.send(bytes[,flag]) 发送TCP数据,将bytes中的数据发送到连接的套接字。返回值是发送的字节数量,可能小于string的字节大小(可能未将指定内容全部发送)。
s.sendall(bytes[,flag]) 完整发送TCP数据。将bytes中的数据发送到连接的套接字,尝试发送所有数据,成功返回None,失败则抛出异常。内部通过递归调用send,尝试将所有内容发送出去。
s.sendto(bytes,address)发送TCP数据到一个新的套接字address
s.recvfrom(bufsize[.flag]) 接收UDP数据,与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。
s.sendto(string[,flag],address) 发送UDP数据,将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。
s.getpeername() 返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。
s.getsockname() 返回套接字自己的地址。通常是一个元组(ipaddr,port)
s.settimeout(timeout) 设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如connect())
s.gettimeout() 返回当前超时期的值,单位是秒,如果没有设置超时期,则返回None。
s.fileno() 返回套接字的文件描述符。失败时返回-1。
s.setblocking(flag) 是否阻塞(默认True),如果设置False,调用recv()没有任何数据,或send()调用无法立即发送数据,那么将引起socket.error异常。
s.getblocking():返回套接字是否处于阻塞模式
s.shutdown(how)关闭连接的一个或两个部分。如果how为SHUT_RD,则不允许进一步接收。如果how为SHUT_WR,则不允许进一步发送。如果how为SHUT_RDWR,则不允许进一步发送和接收。
s.dup()设置套接字为不可继承
s.get_inheritable()获取套接字的文件描述符或套接字句柄的可继承标志:如果套接字可以在子进程中继承,则为true;如果无法继承,则为false。
使用socket访问http示例:
import socket import ssl def blocking(url,port=80): sock = socket.socket() if port==443: #如果访问的是https网页 sock = ssl.wrap_socket(sock) sock.connect((url, port)) request_head = ‘POST /login.php HTTP/1.1\rHost: %s\rUser-Agent: diy agent\rAccept: */*\rConnection: close\rCookie: k=122;df=345\rContent-Type: application/x-www-form-urlencoded\rContent-Length: 14\r\rasdf=11&sdf=22‘ % (url) sock.send(request_head.encode(‘utf-8‘)) response = b‘‘ chunk = sock.recv(1024) while chunk: # 每次接受1024字节,循环接收数据,因为有可能一次接收不完整 response += chunk chunk = sock.recv(1024) return response.decode(‘utf-8‘) print(blocking(‘www.a.com‘)) #访问并打印http://www.a.com页面 print(blocking(‘www.b.com‘,443)) #访问并打印https://www.b.com页面
访问https
使用socket访问https页面时需要引入ssl模块并使用ssl.wrap_socket(socket)方法返回ssl套接字(sslscoket)对象,ssl套接字对象可以使用socket对象的所有方法。
请求头connection
HTTP 1.1协议中,请求头中默认connection: keep-alive。
Connection: keep-alive :一个请求完成后,用于传输HTTP数据的TCP连接不会关闭,再次访问这个服务器上的网页,会继续使用这一条已经建立的TCP连接
Connection: close:一个请求完成后,服务器主动断开用于传输HTTP数据的TCP连接, 当客户端再次发送请求,需要重新建立TCP连接。
python中的socket.recv如果接收不到数据,会等待直到(超过keep-alive的最大保持连接时间)服务器主动断开TCP连接才会返回NULL,一般是30s~60s,所以使用socket模块访问http数据的时候需要在请求头中添加connection: close选项