socket
网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket(套接字)。
建立网络通信连接至少要一对socket。socket是对TCP/IP的封装
步骤:
服务器端:
- 实例化类
- bind 绑定端口
- listen 监听端口
- accept 等待连接(阻塞)
sock, addr = server.accept()
sock 是为客户端实例化的socket类, addr 是客户端的地址 - 与客户端交互:recv 接收(阻塞)、send 发送数据
- close 断开连接
客户端:
- 实例化类
- connect 连接端口
- 与服务器端交互:recv 接收(阻塞)、send 发送数据
- close 断开连接
server:
import socket server = socket.socket() server.bind(('localhost', 8001)) server.listen(3) conn, addr = server.accept() data = conn.recv(1024) conn.send(b'Got') print('recv:', data) server.close()
client:
import socket client = socket.socket() client.connect(('localhost', 8001)) client.send(b'Hello') data = client.recv(1024) print('recv:', data) client.close()
server:
import socket server = socket.socket() server.bind(('localhost', 8001)) server.listen(3) while 1: conn, addr = server.accept() print('new connection:', addr) while 1: data = conn.recv(1024) conn.send(b'Got') if not data: break else: print('recv:', data)
client:
import socket client = socket.socket() client.connect(('localhost', 8001)) while 1: data = input('>>>').strip() if len(data) == 0: # 注意不能发送内容为空,否则客户端会卡住 continue if data == 'e': break else: client.send(data.encode('utf-8')) data = client.recv(1024) print('recv:', data.decode()) client.close()
server:
import socket import os server = socket.socket() server.bind(('localhost', 8001)) server.listen(3) while 1: try: conn, addr = server.accept() print('new connection:', addr) while 1: data = conn.recv(1024) if not data: break else: res = os.popen(data.decode()).read() conn.send(res.encode()) except ConnectionResetError: continue
client:
import socket client = socket.socket() client.connect(('localhost', 8001)) while 1: data = input('>>>').strip() if len(data) == 0: # 注意不能发送内容为空,否则客户端会卡住 continue if data == 'e': break else: client.send(data.encode('utf-8')) data = client.recv(1024) print(data.decode()) client.close()
server:
import socket import os import hashlib server = socket.socket() server.bind(('localhost', 8001)) server.listen(3) while 1: try: conn, addr = server.accept() print('new connection:', addr) while 1: cmd = conn.recv(1024).decode() if not cmd: print('lost client') break else: file_name = cmd.split()[1] if not os.path.isfile(file_name): res = 'no such file' else: # 存在可以传输的文件 m = hashlib.md5() file_size = os.stat(file_name).st_size print(file_size) conn.send(str(file_size).encode()) conn.recv(1024) with open(file_name, 'rb')as f: for line in f: conn.send(line) m.update(line) print('file md5:', m.hexdigest()) conn.send(m.hexdigest().encode()) except ConnectionResetError: print('lost client') continue
client:
import socket import hashlib client = socket.socket() client.connect(('localhost', 8001)) while 1: cmd = input('get filename: >>>').strip() if len(cmd) == 0: # 注意不能发送内容为空,否则客户端会卡住 continue else: client.send(cmd.encode('utf-8')) file_name = cmd.split()[1] received_size = 0 m = hashlib.md5() total_size = client.recv(1024) client.send(b'Get size') f = open(file_name + '.new', 'wb') while received_size < int(total_size.decode()): size = int(total_size.decode()) - received_size if size < 1024: receive_size = size else: receive_size = 1024 data = client.recv(receive_size) received_size += len(data) f.write(data) m.update(data) else: f.close() print(m.hexdigest()) server_md5 = client.recv(1024).decode() if m.hexdigest() == server_md5: print('file checked') client.close()
有时结束连接之后再连接会显示端口占用,可以通过重用端口省去等待的时间:server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
(bind 端口之前进行设置)
socket server
创建socketserver的基本步骤:
- 创建一个请求处理类,继承 BaseRequestHandler 并且重写父类中的 handle()
- 实例化 TCPServer ,将 server IP 和请求处理类传给 TCPServer
- 调用
server.handle_request() #只处理一个请求
server.serve_forever() #处理多个一个请求,永远执行
- server_close()
class BaseServer: class TCPServer(BaseServer): class UDPServer(TCPServer): class UnixStreamServer(TCPServer): class UnixDatagramServer(UDPServer): class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass
import socketserver class MyTCPHandler(socketserver.BaseRequestHandler): def handle(self): # 处理和客户端所有的交互 pass if __name__ == '__main__': HOST, PORT = 'localhost', 8001 server = socketserver.TCPServer((HOST, PORT), MyTCPHandler) server.serve_forever()
如果要设置允许地址重用:allow_reuse_address
是 TCPServer 和 UDPServer 的类变量,之后又被实例化的类继承了,由于实例化的类继承父类的同时已经开始 bind,所以只能在他继承父类之前修改父类的类变量
HOST, PORT = 'localhost', 8001 socketserver.TCPServer.allow_reuse_address = True server = socketserver.TCPServer((HOST, PORT), MyTCPHandler) # 实例化 bind listen server.serve_forever()
多线程:ThreadingTCPServer
多进程:ForkingTCPServer
-only in Linux
使用时只需在实例化类时继承其中一个:
HOST, PORT = 'localhost', 8001 socketserver.TCPServer.allow_reuse_address = True server = socketserver.ThreadingTCPServer((HOST, PORT), MyTCPHandler) # 实例化 bind listen server.serve_forever()