Python中Socket编程(TCP、UDP)
1. TCP协议下的如何解决粘包问题
TCP(transport control protocol 传输控制协议) 使用Nagle算法,将多次间隔较小且数据量小的数据,合并成大的数据块;接受端无法识别每条数据的边界,因此产生粘包现象。
""" Server """ from socket import * back_log = 5 bufsize = 1024 ip_port = ('127.0.0.1', 8080) tcp_server = socket(AF_INET, SOCK_STREAM) # 数据流 tcp_server.bind(ip_port) tcp_server.listen(back_log) while True: print('服务端开始处理连接。。。') conn, addr = tcp_server.accept() print('客户端连接', conn) print('客户端地址', addr) while True: try: data = conn.recv(bufsize) if not data: break print('接收到的数据', data) conn.send(data.upper()) except Exception as e: print(e) break
""" Client """ from socket import * bufsize = 1024 ip_port = ('127.0.0.1', 8080) tcp_client = socket(AF_INET, SOCK_STREAM) tcp_client.connect(ip_port) tcp_client.send('hello'.encode('utf-8')) tcp_client.send('world'.encode('utf-8')) response = tcp_client.recv(bufsize) print('response is ==> ', response) # response is ==> b'HELLOWORLD'
解决思路:告知接收端数据长度,导入struct模块,将字节长度封装成4个字节发送给接收方;
服务端 =====》TCP实现远程操作命令
from socket import * import subprocess,struct bufsize = 1024 back_log = 5 ip_port = ('127.0.0.1', 8080) tcp_server = socket(AF_INET, SOCK_STREAM) tcp_server.bind(ip_port) tcp_server.listen(back_log) while True: conn, addr = tcp_server.accept() print('current connection', conn) while True: try: cmd = conn.recv(bufsize) if not cmd: break print('data from client', cmd) res = subprocess.Popen(cmd.decode('utf-8'), shell=True, stderr=subprocess.PIPE, stdin=subprocess.PIPE, stdout=subprocess.PIPE) cmd_res = res.stderr.read() if not cmd_res: cmd_res = res.stdout.read() print('response is:', cmd_res) data_length = struct.pack('i', len(cmd_res)) conn.send(data_length) conn.send(cmd_res) except Exception as e: print(e) break # conn.close()
客户端 ====》
from socket import * import struct bufsize = 100 ip_port = ('127.0.0.1', 8080) tcp_client = socket(AF_INET, SOCK_STREAM) tcp_client.connect(ip_port) while True: cmd = input('>>>> ').strip() if not cmd: continue if cmd == 'quit': break tcp_client.send(cmd.encode('utf-8')) length_data = tcp_client.recv(4) length = struct.unpack('i', length_data)[0] print(length) response = b'' recsize = 0 while recsize < length: response += tcp_client.recv(bufsize) recsize = len(response) # print('execute result is:', response) # 接受字节长度小于发送数据长度,产生粘包 print('execute result is:', response.decode('gbk')) tcp_client.close()
2. TCP协议如何实现多个客户端连接(并发处理)
导入socketserver模块,实现ocketserver.BaseRequestHandler,重写 handle 方法
使用
ThreadingTCPServer,线程实现并发
import socketserver class MyServer(socketserver.BaseRequestHandler): def handle(self): print('conn is', self.request) # conn print('addr is', self.client_address) # addr while True: try: data = self.request.recv(1024) if not data: break self.request.sendall(data.upper()) except Exception as e: print(e) break if __name__ == '__main__': ip_port = ('127.0.0.1', 8080) s = socketserver.ThreadingTCPServer(ip_port, MyServer) s.serve_forever()
3. 基于UDP套接字
UDP 与TCP 不同,使用socket时用的时 SOCK_DGRAM 数据报
sendto()发送的是元组数据类型,包含数据以及连接
recvfrom()接收的也是数据以及连接
服务端 ====》
from socket import * ip_port = ('127.0.0.1', 8080) buf_size = 1024 udp_server = socket(AF_INET, SOCK_DGRAM) # datagram 数据报 udp_server.bind(ip_port) while True: data, addr = udp_server.recvfrom(buf_size) print(data) udp_server.sendto(data.upper(), addr)
客户端====》
from socket import * ip_port = ('127.0.0.1', 8080) buf_size = 1024 udp_client = socket(AF_INET, SOCK_DGRAM) # datagram 数据报 while True: msg = input('==>: ').strip() udp_client.sendto(msg.encode('utf-8'), ip_port) response, addr = udp_client.recvfrom(buf_size) print(response.decode('utf-8'))