Python网络编程02 socket模块
1、socket模块的socket类
- 要创建套接字,必须使用socket.socket()类:
- socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)
- 地址簇(family)应为AF_INET(默认)、AF_INET6、AF_UNIX、AF_CAN、AF_PACKET或AF_RDS其中之一。
- 套接字类型(type)应为SOCK_STREAM(默认)、SOCK_DGRAM、SOCK_RAW或其他SOCK_常量之一。
- 协议号(proto)通常为0,可以省略,或者在地址簇为AF_CAN的情况下,协议号应为CAN_RAW、CAN_BCM或CAN_ISOTP之一。
- 如果指定了 fileno,那么将从这一指定的文件描述符中自动检测 family、type 和 proto 的值。如果调用本函数时显式指定了 family、type 或 proto 参数,可以覆盖自动检测的值。
- socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)
- 创建TCP/IP套接字
- tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- 创建UDP/IP套接字
- udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
-
socket类的方法和属性
2、创建TCP服务器及客户端
- python3中数据的传输必须是byte类型
- bytes(data,'utf8') #将字符串转换为byte类型
- str(data,'utf8') #将其他类型转换为字符串
1、创建TCP服务器
- 创建通用TCP服务器的一般伪代码:
ss = socket(family,type) #创建服务器套接字 #ss.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) #因为前一次运行使套接字处于 TIME_WAIT 状态,无法立即重用。SO_REUSEADDR 标志告诉内核将处于 TIME_WAIT 状态的本地套接字重新使用,而不必等到固有的超时到期。 ss.bind(address) #套接字与地址绑定 ss.listen(backlog) #监听连接 inf_loop: #服务器无限循环 connection, address = ss.accept() #接受客户端连接 comm_loop: #通信循环 cs.recv() / cs.send() #对话(接收/发送) cs.close() #关闭客户端套接字 ss.close() #关闭服务器套接字#(可选)
- 创建套接字:socket.socket(family, type)
- family参数代表地址家族,可为AF_INET或AF_UNIX。AF_INET家族包括Internet地址,AF_UNIX家族用于同一台机器上的进程间通信。
- type参数代表套接字类型,可为SOCK_STREAM(流套接字,就是TCP套接字)和SOCK_DGRAM(数据报套接字,就是UDP套接字)。
- 默认为family=AF_INET,type=SOCK_STREM
- 返回一个整数描述符,用这个描述符来标识这个套接字
- 绑定套接字:bind(address) #因为服务器需要占用一个端口并等待客户端的请求,所以它们必须绑定到一个本地地址。
- 由AF_INET所创建的套接字,address地址必须是一个双元素元组,格式是(host,port)。host代表主机,port代表端口号。
- 如果端口号正在使用、主机名不正确或端口是已被保留的,bind方法将引发socket.error异常。
- 监听套接字:listen(backlog) #TCP服务器必须监听(传入)的连接
- backlog指定最多允许多少个客户连接到服务器。它的值至少为1。收到连接请求后,这些请求需要排队,如果队列满,就拒绝请求。
- 等待接受连接:connection, address = ss.accept()
- 调用accept()函数之后,就开启了一个简单的(单线程)服务器,它会等待客户端的连接。默认情况下,accept()是阻塞的,这意味着执行将被暂停,直到一个连接到达。
- 一旦服务器接受了一个连接,就会返回(利用accept())一个独立的客户端套接字,用来与即将到来的消息进行交换。
- accept方法返回一个含有两个元素的元组(connection, address)。
- 第一个元素connection是所连接的客户端的socket对象(实际上是该对象的内存地址),服务器必须通过它与客户端通信;
- 第二个元素address是客户端的Internet地址。
- 处理阶段:
- connection.recv(bufsize[, flag])
- 接收套接字的数据。数据以字符串形式返回,bufsize指定最多可以接收的数量。flag提供有关消息的其他信息,通常可以忽略
- connection.send(string[, flag])
- 将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。即:可能未将指定内容全部发送。
- connection.recv(bufsize[, flag])
- 传输结束,关闭连接:ss.close()
- 关闭套接字
- 每当一个传入的请求到达时,服务器会创建一个新的通信端口来直接与客户端进行通信,再次空出主要的端口,以使其能够接受新的客户端连接。
- 当一方关闭连接或者向对方发送一个空字符串时,通常就会关闭连接。
示例1:
###python3.8 #!/usr/bin/env python3 from socket import * from time import ctime, sleep HOST = '' #HOST变量是空白的,这是对bind()方法的标识,表示它可以使用任何可用的地址。 PORT = 21567 BUFSIZ = 1024 #缓冲区大小设置为1KB ADDR = (HOST, PORT) tcpSerSock = socket(AF_INET, SOCK_STREAM) #print('******tcpSerSock:', tcpSerSock) #tcpSerSock: <socket.socket fd=448, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0> tcpSerSock.bind(ADDR) tcpSerSock.listen(5) #listen()方法的参数是在连接被转接或拒绝之前,传入连接请求的最大数。 while True: print('waiting for connection...') tcpCliSock, addr = tcpSerSock.accept() #print('******tcpCliSock:', tcpCliSock) #tcpCliSock: <socket.socket fd=496, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 21567), raddr=('127.0.0.1', 51383)> #print('******addr:', addr) #addr: ('127.0.0.1', 51383) while True: data = tcpCliSock.recv(BUFSIZ) if not data: break #sleep(5) tcpCliSock.send(bytes('[%s] %s' % (ctime(), str(data, 'utf-8')), 'utf8')) tcpCliSock.close() tcpSerSock.close() #最后一行永远不会执行,它只是用来提醒读者,有这种优雅的退出方式
示例2:
###python2.7 #!/usr/bin/env python from socket import * from time import ctime HOST = '' PORT = 21567 BUFSIZ = 1024 ADDR = (HOST, PORT) tcpSerSock = socket(AF_INET, SOCK_STREAM) tcpSerSock.bind(ADDR) tcpSerSock.listen(5) while True: print 'waiting for connection...' tcpCliSock, addr = tcpSerSock.accept() print '...connected from:', addr while True: data = tcpCliSock.recv(BUFSIZ) if not data: break tcpCliSock.send('[%s] %s' % (ctime(), data)) tcpCliSock.close() tcpSerSock.close()
2、创建TCP客户端
- 创建客户的伪代码:
cs = socket() # 创建客户端套接字 cs.connect() # 尝试连接服务器 comm_loop: # 通信循环 cs.send() / cs.recv() # 对话(发送/接收) cs.close() # 关闭客户端套接字
- 创建socket对象:cs = socket()
- 连接至服务器端:cs.connect(address)
- 连接到服务器address处的套接字。一般,address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。
- 处理阶段
- cs.recv(bufsize[,flag])
- 接受套接字的数据。数据以字符串形式返回,bufsize指定最多可以接收的数量。flag提供有关消息的其他信息,通常可以忽略。
- cs.send(string[,flag])
- 将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。即:可能未将指定内容全部发送。
- cs.recv(bufsize[,flag])
- 连接结束,关闭套接字
- cs.close()
示例1:
###python3.8 #!/usr/bin/env python from socket import * HOST = '127.0.0.1' #HOST和PORT变量指服务器的主机名与端口号 PORT = 21567 BUFSIZ = 1024 ADDR = (HOST, PORT) tcpClisock = socket(AF_INET, SOCK_STREAM) #print('******tcpClisock', tcpClisock) #tcpClisock <socket.socket fd=452, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0> tcpClisock.connect(ADDR) while True: #两种条件下将会跳出:用户没有输入,或者服务器终止且对recv()方法的调用失败。 data = input('> ') if not data: break tcpClisock.send(bytes(data, 'utf-8')) data = tcpClisock.recv(BUFSIZ) if not data: break print(str(data, 'utf-8')) tcpClisock.close()
示例2:
###python2.7 #!/usr/bin/env python from socket import * HOST = '192.168.248.128' PORT = 21567 BUFSIZ = 1024 ADDR = (HOST, PORT) tcpClisock = socket(AF_INET, SOCK_STREAM) tcpClisock.connect(ADDR) while True: data = raw_input('> ') if not data: break tcpClisock.send(data) data = tcpClisock.recv(BUFSIZ) if not data: break print data tcpClisock.close()
3、创建UDP服务器和客户端
1、创建UDP服务器
- 创建UDP服务器伪代码
ss=socket() #创建服务器套接字 ss.bind() #绑定服务器套接字 inf_loop: #服务器无限循环 cs=ss.recvfrom()/ss.sendto() #关闭(接收/发送) ss.close() #关闭服务器套接字
示例1:
###python3.8 #!/usr/bin/env python from socket import * from time import ctime HOST = '' PORT = 21567 BUFSIZ = 1024 ADDR = (HOST, PORT) udpSerSock = socket(AF_INET, SOCK_DGRAM) udpSerSock.bind(ADDR) while True: print('waiting for message...') data, addr = udpSerSock.recvfrom(BUFSIZ) udpSerSock.sendto(bytes('[%s] %s' % (ctime(), str(data, 'utf8')), 'utf8'), addr) print('...received from and returned to :', addr) udpSerSock.close()
示例2:
###python2.7 #!/usr/bin/env python from socket import * from time import ctime HOST = '' PORT = 21567 BUFSIZ = 1024 ADDR = (HOST, PORT) udpSerSock = socket(AF_INET, SOCK_DGRAM) udpSerSock.bind(ADDR) while True: print 'waiting for message...' data, addr = udpSerSock.recvfrom(BUFSIZ) udpSerSock.sendto('[%s] %s' % (ctime(), data), addr) print '...received from and returned to :', addr udpSerSock.close()
2、创建UDP客户端
- 创建UDP客户端伪代码
cs = socket() #创建客户端套接字 comm_loop: #通信循环 cs.sendto()/cs.recvfrom() #对话(发送/接收) cs.close() #关闭客户端套接字
示例1:
###python3.8 #!/usr/bin/env python from socket import * from time import ctime HOST = 'localhost' PORT = 21567 BUFSIZ = 1024 ADDR = (HOST, PORT) udpCliSock = socket(AF_INET, SOCK_DGRAM) while True: data = input('> ') if not data: break udpCliSock.sendto(bytes(data, 'utf8'), ADDR) data, ADDR = udpCliSock.recvfrom(BUFSIZ) if not data: break print(str(data, 'utf8')) udpCliSock.close()
示例2:
###python2.7 #!/usr/bin/env python from socket import * from time import ctime HOST = 'localhost' PORT = 21567 BUFSIZ = 1024 ADDR = (HOST, PORT) udpCliSock = socket(AF_INET, SOCK_DGRAM) while True: data = raw_input('> ') if not data: break udpCliSock.sendto(data, ADDR) data, ADDR = udpCliSock.recvfrom(BUFSIZ) if not data: break print data udpCliSock.close()
4、socket模块内容
- 除了socket.socket()函数之外,socket模块还提供了更多用于网络应用开发的属性。
5、超时与非阻塞
- 一个套接字对象可以处于以下三种模式之一:阻塞、非阻塞或超时。套接字默认以阻塞模式创建,但是可以调用setdefaulttimeout()来更改
- 在blocking mode(阻塞模式)中,操作将阻塞,直到操作完成或系统返回错误(如连接超时)。
- 在non-blocking mode(非阻塞模式)中,如果操作无法立即完成,则操作将失败(不幸的是,不同系统返回的错误不同):位于select中的函数可用于了解套接字何时以及是否可以读取或写入。
- 在timeout mode(超时模式)下,如果无法在指定的超时内完成操作(抛出timeout异常),或如果系统返回错误,则操作将失败。
- 注解:在操作系统层面上,超时模式下的套接字在内部都设置为非阻塞模式。同时,阻塞和超时模式在文件描述符和套接字对象之间共享,这些描述符和对象均应指向同一个网络端点。
1、超时与connect方法
- connect()操作也受超时设置的约束,通常建议在调用connect()之前调用settimeout(),或将超时参数直接传递给create_connection()。但是,无论Python套接字超时设置如何,系统网络栈都有可能返回自带的连接超时错误。
2、超时与accept方法
- 如果getdefaulttimeout()的值不是None,则accept()方法返回的套接字将继承该超时值。若是None,返回的套接字行为取决于侦听套接字的设置:
- 如果侦听套接字处于阻塞模式或超时模式,则accept()返回的套接字处于阻塞模式;
- 如果侦听套接字处于非阻塞模式,那么accept()返回的套接字是阻塞还是非阻塞取决于操作系统。如果要确保跨平台时的正确行为,建议手动覆盖此设置。
3、非阻塞
- accept()和recv()默认是阻塞的。
- 当ss.setblocking(False)将服务连接设置为非阻塞时,accept()和recv()也都是非阻塞的了。
示例:非阻塞服务端
#!/usr/bin/env python3 from socket import * from time import ctime, sleep ss = socket(AF_INET, SOCK_STREAM) ss.bind(('127.0.0.1', 10000)) ss.listen(5) while True: print('waiting for connection...') try: ss.setblocking(False) #设置非阻塞,将accept、revc都设置为非阻塞啦 cs, addr = ss.accept() print(cs, addr) except Exception as e: #异常时,执行的代码 print(e) sleep(3) else: #无异常时,执行的代码 while True: cs.setblocking(True) #有客户端连接时,将该连接修改为阻塞的(即将recv改为阻塞的) data = cs.recv(1024) if not data: break cs.send(bytes('[%s] %s' % (ctime(), str(data, 'utf-8')), 'utf8')) cs.close() tcpSerSock.close() # 最后一行永远不会执行,它只是用来提醒读者,有这种优雅的退出方式
示例:客户端
#!/usr/bin/env python from socket import * cs = socket(AF_INET, SOCK_STREAM) cs.connect(('127.0.0.1',10000)) while True: data = input('> ') if not data: break cs.send(bytes(data, 'utf-8')) data = cs.recv(1024) if not data: break print(str(data, 'utf-8')) cs.close()
4、注意
- socket.socket类
- 设置套接字超时时间:s.settimeout()
- 设置套接字是否阻塞:s.setblocking()
- socket模块
- 设置套接字默认超时时间:setdefaulttimeout()