一.基于tcp的套接字
tcp是基于链接的,必须先启动服务端,然后再启动客户端去链接服务端
tcp服务端
from socket import * # 服务端必须满足至少三点: # 1.绑定一个固定的ip和port # 2.一直对外提供服务,稳定运行 # 3.能够支持并发 server = socket(AF_INET, SOCK_STREAM) server.bind(('127.0.0.1', 9555)) server.listen(5) client, addr = server.accept() data = client.recv(1024) print(data.decode('utf-8')) client.send(data.upper()) client.close() server.close()
tcp客户端
from socket import * client = socket(AF_INET,SOCK_STREAM) client.connect(('127.0.0.1',9555)) msg = input('>>>:').strip() client.send(msg.encode('utf-8')) data = client.recv(1024) print(data) client.close()
二.修改bug+通信循环+链接循环
上述存在客户端退出时服务端即会崩溃的bug,而且无法实现用户的多次输入,以及服务端无法一直对外服务的问题,对此进行修改
服务端
from socket import * server = socket(AF_INET, SOCK_STREAM) server.bind(('127.0.0.1', 8081)) server.listen(5) # 链接循环 while True: conn, client_addr = server.accept() print(client_addr) # 通信循环 while True: try: data = conn.recv(1024) if len(data) == 0: break # 针对linux系统 print('-->收到客户端的消息: ', data) conn.send(data.upper()) except ConnectionResetError: break conn.close() server.close()
客户端
from socket import * client = socket(AF_INET, SOCK_STREAM) client.connect(('127.0.0.1', 8081)) # 通信循环 while True: msg=input('>>: ').strip() #msg='' if len(msg) == 0:continue client.send(msg.encode('utf-8')) #client.send(b'') # print('has send') data=client.recv(1024) # print('has recv') print(data) client.close()
三.模拟ssh实现远程执行命令
from socket import * import subprocess server = socket(AF_INET, SOCK_STREAM) server.bind(('127.0.0.1', 8081)) server.listen(5) # 链接循环 while True: conn, client_addr = server.accept() print(client_addr) # 通信循环 while True: try: cmd = conn.recv(1024) #cmd=b'dir' if len(cmd) == 0: break # 针对linux系统 obj=subprocess.Popen(cmd.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) stdout=obj.stdout.read() stderr=obj.stderr.read() print(len(stdout) + len(stderr)) conn.send(stdout+stderr) except ConnectionResetError: break conn.close() server.close()
from socket import * client = socket(AF_INET, SOCK_STREAM) client.connect(('127.0.0.1', 8081)) # 通信循环 while True: cmd=input('>>: ').strip() if len(cmd) == 0:continue client.send(cmd.encode('utf-8')) cmd_res=client.recv(1024000) print(cmd_res.decode('gbk')) client.close()
四.粘包问题以及解决方法
粘包问题:
只有TCP有粘包现象,UDP不存在粘包,粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的.
简单粘包问题案例
from socket import * import subprocess server = socket(AF_INET, SOCK_STREAM) server.bind(('127.0.0.1', 8081)) server.listen(5) conn,_=server.accept() data1=conn.recv(5) print('第一次收: ',data1) data2=conn.recv(5) print('第二次收: ',data2) data3=conn.recv(4) print('第三次收: ',data3) # 粘包问题是tcp协议流式传输数据的方式导致的 # 如何解决粘包问题:接收端能够精确地收干净每个数据包没有任何残留
from socket import * client = socket(AF_INET, SOCK_STREAM) client.connect(('127.0.0.1', 8081)) # tcp协议会将数据量较小且发送时间间隔较短的数据合并成一个数据报发送 client.send(b'hello') client.send(b'world') client.send(b'egon')
五.解决粘包问题
from socket import * import subprocess import struct import json server = socket(AF_INET, SOCK_STREAM) server.bind(('127.0.0.1', 8081)) server.listen(5) # 链接循环 while True: conn, client_addr = server.accept() print(client_addr) # 通信循环 while True: try: cmd = conn.recv(1024) # cmd=b'dir' if len(cmd) == 0: break # 针对linux系统 obj = subprocess.Popen(cmd.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) stdout = obj.stdout.read() stderr = obj.stderr.read() # 1. 先制作报头 header_dic = { 'filename': 'a.txt', 'md5': 'asdfasdf123123x1', 'total_size': len(stdout) + len(stderr) } header_json = json.dumps(header_dic) header_bytes = header_json.encode('utf-8') # 2. 先发送4个bytes(包含报头的长度) conn.send(struct.pack('i', len(header_bytes))) # 3 再发送报头 conn.send(header_bytes) # 4. 最后发送真实的数据 conn.send(stdout) conn.send(stderr) except ConnectionResetError: break conn.close() server.close()
from socket import * import struct import json client = socket(AF_INET, SOCK_STREAM) client.connect(('127.0.0.1', 8081)) # 通信循环 while True: cmd = input('>>: ').strip() if len(cmd) == 0: continue client.send(cmd.encode('utf-8')) # 1. 先收4bytes,解出报头的长度 header_size = struct.unpack('i', client.recv(4))[0] # 2. 再接收报头,拿到header_dic header_bytes = client.recv(header_size) header_json = header_bytes.decode('utf-8') header_dic = json.loads(header_json) print(header_dic) total_size = header_dic['total_size'] # 3. 接收真正的数据 cmd_res = b'' recv_size = 0 while recv_size < total_size: data = client.recv(1024) recv_size += len(data) cmd_res += data print(cmd_res.decode('gbk')) client.close()