python 30 基于TCP协议的socket通信
发布时间:2020-12-20 10:56:12 所属栏目:Python 来源:网络整理
导读:目录 1. 单对单循环通信 2. 循环连接通信:可连接多个客户端 3. 执行远程命令 4. 粘包现象 4.1 socket缓冲区 4.2 出现粘包的情况: 4.3 解决粘包现象 bytes 1. 单对单循环通信 ? send() 和recv()不是一一对应的。 # 服务端 server.py import socketphone = s
目录
1. 单对单循环通信? send() 和recv()不是一一对应的。 # 服务端 server.py import socket phone = socket.socket() phone.bind = (('127.0.0.1',8888)) # 绑定本地回环地址/端口号 phone.listen() # 开始监听 conn,addr = phone.accept() # 等待连接 print(f"连接{addr}成功!") while 1: try: from_client_data = conn.recv(1024) if from_client_data.upper() == b'Q': # 字节bytes Q print('客户端已正常退出!') break print(f"来自客户端{addr}的消息:{from_client_data.decode('utf-8')}") to_server_data = input('>>>').strip().encode('utf-8') phone.send(to_server_data) except ConnectionResetError: print('客户端连接中断!') break conn.close() phone.close() # 客户端 client.py import socket phone = socket.socket() phone.connect(('127.0.0.1',8888)) while 1: to_server_data = input('>>>').strip().encode('utf-8') if not to_server_data: print('输入内容不能为空!') continue phone.send(to_server_data) if to_server_data.upper() == b'Q': break from_server_data = phone.recv(1024) print(f"来自服务端的消息:{from_server_data.decode('utf-8')}") phone.close() 2. 循环连接通信:可连接多个客户端# 服务端循环接收连接 import socket phone = socket.socket() phone.bind(('127.0.0.1',8848)) phone.listen() while 1: conn,addr = phone.accept() # 循环可接收客户端 print(f'与客户端{addr}连接成功!') while 1: try: from_client_data = conn.recv(1024) if from_client_data.upper() == b'Q': print('客户端已退出!') break print(f"来自客户端的消息:{from_client_data.decode('utf-8')}") to_client_data = input('>>>').strip().encode('utf-8') conn.send(to_client_data) except ConnectionResetError: print('客户端连接中断!') break conn.close() phone.close() # 客户端 client import socket phone = socket.socket() phone.connect(('127.0.0.1',8848)) while 1: to_server_data = input('>>>').strip().encode('utf-8') if not to_server_data: print('输入内容不能为空!') continue phine.send(to_server_data) if to_server_data.upper() == b'Q': break from_server_data = phone.recv(1024) print(f"来自服务端的消息:{from_server_data.decode('utf-8')}") phone.close 3. 执行远程命令# 使用subproess模块 可像操作系统cmd一样执行命令 import subprocess obj = subprocess.Popen('dir',shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE,) print(obj.stdout.read().decode('gbk')) # 正确命令 print(obj.stderr.read().decode('gbk')) # 错误命令 # 如果是正确的命令,那么错误命令会输出空字符。 # shell: 命令解释器,相当于调用cmd 执行指定的命令。 # stdout:正确结果丢到管道中。 # stderr:错了丢到另一个管道中。 # windows操作系统的默认编码是gbk编码。 # 在服务端 增加subprocess模块, import subprocess import socket phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) phone.bind(('127.0.0.1',8888)) phone.listen() while 1: conn,addr = phone.accept() print(f'连接{addr}成功!') while 1: try: from_client_data = conn.recv(1024) if from_client_data.upper() == b'Q': print('客户端正常退出!') break # print(f"来自客户端{addr}的消息:{from_client_data.decode('utf-8')}") obj = subprocess.Popen(from_client_data.decode("utf-8"),) to_client_data = obj.stdout.read() + obj.stderr.read() # subprocess使用当前系统默认编码,得到结果为bytes类型,在windows下需要用gbk解码 conn.send(to_client_data) # 此时是gbk的bytes的类型 except ConnectionResetError: print('客户端连接中断!') break conn.close() phone.close() # 客户端 client import socket phone = socket.socket() phone.connect(('127.0.0.1',8888)) while 1: to_server_data = input('>>>').strip().encode('utf-8') if not to_server_data: print('输入内容不能为空!') continue phone.send(to_server_data) if to_server_data.upper() == b'Q': break from_server_data = phone.recv(1024) print(f"{from_server_data.decode('gbk')}") # 需gbk解码 phone.close() 4. 粘包现象4.1 socket缓冲区? 每个socket,都会有两个缓冲区,输入/输出缓冲区。过程如上图,send()结束后,会马上进入recv()状态(有阻塞)。 ? 作用:1. 展示存储一些数据;2. 如果网络有波动,缓冲区会保护数据的收发稳定、匀速。 ? 缺点:是会造成粘包的现象之一。 4.2 出现粘包的情况:? 1. 如果客户端send() 数据超过recv() 设定的字节数,会先接收最大限制的字节数,当第二次send() 数据时,recv() 会将上次遗留的数据接收,产生粘包现象。
4.3 解决粘包现象#思路: 服务端发出send() 数据有10000字节,客户端接收数据时,循环recv()接收,每次(至多)接收1024字节,直至将所有的字节全部接收完毕,将接收的数据拼接在一起,编码打印出。 #遇到的问题: 1. recv() 次数无法确定。 客户端先接收总数据的一个长度,然后再循环recv 控制循环次数,只有接收的数据长度 < 总数据长度,就会一直接收。(先发总数据长度,再发总数据) 2.获取总数据的长度是 int 类型,先要转换成 bytes 类型,再发出,但是总数据的长度转化成的 bytes类型的字节数是不固定的。 int:376 ——> str(376):'376' ——> bytes:b'376' (长度为3) int:4558 ——> str(4558):'4558' ——> bytes:b'4558' (长度为4) 解决:将不固定长度的int类型转换成固定长度的bytes,并且还可以反解回来。(用来制定固定长度的报头) # 将不固定长度的int类型转换成固定长度的bytes import struct ret = struct.pack('i',376) # 将int 376转换成固定4个长度的bytes字节 # i 默认长度为 4; q 默认为8 print(ret,type(ret),len(ret)) ret1 = struct.unpack('i',ret)[0] # 是元组形式, 反解成int型 print(ret1,type(ret1)) # 但是通过struct 处理不能处理太大数据 ret = struct.pack('l',4323241232132324) print(ret,len(ret)) # 报错 # low版解决粘包现象 # 服务端server import socket import subprocess import struct phone = socket.socket() phone.bind(('127.0.0.1',8888)) phone.listen() while 1: conn,addr = phone.accept() print(f'与客户端{addr}连接') while 1: try: from_client_data = conn.recv(1024) if from_client_data.upper() == b'Q': print('客户端已退出!') break obj = subprocess.Popen(from_client_data.decode("utf-8"),) total_data = obj.stdout.read() + obj.stderr.read() conn.send(struct.pack('i',len(total_data))) # 发出 总数据长度的bytes类型 print(len(total_data)) conn.send(total_data) # 发出总数据 gbk编码的字节 except ConnectionResetError: print('与客户端连接中断!') break conn.close() phone.close() # 客户端client import socket import struct phone = socket.socket() phone.connect(('127.0.0.1',8888)) while 1: to_server_data = input('>>>').strip().encode('utf-8') if not to_server_data: print('内容不能为空!') continue phone.send(to_server_data) if to_server_data.upper() == b'Q': break head_bytes = phone.recv(4) # 接收总数据长度的bytes head_int = struct.unpack('i',head_bytes)[0] # 反解成int print(head_int) data = b'' # 定义一个空字节 while len(data) < head_int: data = data + phone.recv(1024) print(len(data)) print(f"{data.decode('gbk')}") # 终端是 gbk 编码的 phone.close() bytes? 用于网络传输、文件存储时。 能够保持数据原本类型。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |