2、第七 - 网络编程基础 - Socket编程调用系统命令简单的ssh实现
编写socket调用类似于ssh远程调用系统命令
Socket实现简单的ssh,并实现大于1024字节数据的传输。
1、示例(在MAC上操作的):
服务端:
import socket,os server = socket.socket() server.bind(('localhost',3617)) server.listen() while True: conn,addr = server.accept() print("new conn:",addr) while True: print ("等待新指令") data = conn.recv(1024) if not data: print("客户端已断开") break print("执行命令:",data) cmd_res = os.popen(data.decode()).read() #接受字符串,执行结果也是字符串 print("before send:",len(cmd_res)) if len(cmd_res) == 0: cmd_res = "cmd_res has no output..." conn.send(str(len(cmd_res.encode())).encode("utf-8")) #先发大小给客户端 conn.send(cmd_res.encode("utf-8")) #再发内容大小 print("send done") server.close()
客户端:
import socket client = socket.socket() client.connect(("localhost",3617)) while True: cmd = input(">>:").strip() if len(cmd) == 0: continue client.send(cmd.encode("utf-8")) cmd_res_size = client.recv(1024) #接受命令结果的长度 print("命令结果大小:",cmd_res_size) received_size = 0 received_data = b'' while received_size < int(cmd_res_size.decode()): data = client.recv(1024) received_size += len(data) #每次收到的有可能小于1024,所以必须用len判断 received_data += data else: print("cmd res receive done...",received_size) print(received_data.decode()) client.close()
检查客户端与服务端的命令行输出:
服务端返回: new conn: ('127.0.0.1', 60434) 等待新指令 执行命令: b'pwd' before send: 48 send done 等待新指令 执行命令: b'top -l 2' before send: 177758 send done 等待新指令 客户端返回: /Library/Frameworks/Python.framework/Versions/3.5/bin/python3.5 /Users/mac/PycharmProjects/untitled2/51CTO/6day/socket_client_test.py >>:pwd #输入系统命令 命令结果大小: b'48' cmd res receive done... 48 /Users/mac/PycharmProjects/untitled2/51CTO/6day >>:top -l 2 命令结果大小: b'177758' cmd res receive done... 177758 Processes: 313 total, 2 running, 311 sleeping, 1244 threads 2018/03/27 15:27:17 Load Avg: 2.66, 2.52, 2.62 CPU usage: 6.66% user, 23.33% sys, 70.0% idle SharedLibs: 172M resident, 43M data, 22M linkedit. MemRegions: 60372 total, 2838M resident, 100M private, 935M shared. PhysMem: 8091M used (1810M wired), 100M unused. VM: 845G vsize, 621M framework vsize, 0(0) swapins, 0(0) swapouts. Networks: packets: 874801/908M in, 760986/107M out. Disks: 1097544/23G read, 811488/12G written.
2、Mac调用liunx 服务端上的server...注意: 客户端与服务端的Python版本要一致。
示例如下:
服务端:部署在 liunx 服务器上,注意监听的IP为 全网端,如下
import socket,os server = socket.socket() server.bind(('0.0.0.0',3617)) #监听全网端,同时注意Iptables的开放 server.listen() while True: conn,addr = server.accept() print("new conn:",addr) while True: print ("等待新指令") data = conn.recv(1024) if not data: print("客户端已断开") break print("执行命令:",data) cmd_res = os.popen(data.decode()).read() print("before send:",len(cmd_res)) if len(cmd_res) == 0: cmd_res = "cmd_res has no output..." conn.send(str(len(cmd_res.encode())).encode("utf-8")) conn.send(cmd_res.encode("utf-8")) print("send done") server.close()
客户端:运行在Mac PyCharm 上,如下:
import socket client = socket.socket() client.connect(("201.66.5.250",3617)) #监听服务端IP,注意服务器上的防火墙配置 while True: cmd = input(">>:").strip() if len(cmd) == 0: continue client.send(cmd.encode("utf-8")) cmd_res_size = client.recv(1024) print("命令结果大小:",cmd_res_size) received_size = 0 received_data = b'' while received_size < int(cmd_res_size.decode()): data = client.recv(1024) received_size += len(data) received_data += data else: print("cmd res receive done...",received_size) print(received_data.decode()) client.close()
测试,输出结果如下:
客户端操作; /Library/Frameworks/Python.framework/Versions/3.5/bin/python3.5 /Users/mac/PycharmProjects/untitled2/51CTO/6day/socket_client_test.py >>:pwd #输入命令 命令结果大小: b'5' cmd res receive done... 5 /www >>:ls #输入命令 命令结果大小: b'485' cmd res receive done... 485 167log.csv 94log.csv api167_host.php_20180103.log API167_php-fpm_slow.log20180103.bak 服务端,日志打印输出: new conn: ('99.38.28.116', 1470) 等待新指令 执行命令: b'pwd' before send: 5 send done 等待新指令 执行命令: b'ls' before send: 485 send done 等待新指
参考示例,解决“粘包”的问题,就是客户端接收到服务器端发送的信息时,本来是分开的信息,结果是没有隔行,一起输出打印。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#_*_coding:utf-8_*_ __author__ = 'Alex Li' import socket import os,subprocess server = socket.socket() #获得socket实例 server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server.bind(("localhost",9999)) #绑定ip port server.listen() #开始监听 while True: #第一层loop print("等待客户端的连接...") conn,addr = server.accept() #接受并建立与客户端的连接,程序在此处开始阻塞,只到有客户端连接进来... print("新连接:",addr ) while True: data = conn.recv(1024) if not data: print("客户端断开了...") break #这里断开就会再次回到第一次外层的loop print("收到命令:",data) #res = os.popen(data.decode()).read() #py3 里socket发送的只有bytes,os.popen又只能接受str,所以要decode一下 res = subprocess.Popen(data,shell=True,stdout=subprocess.PIPE).stdout.read() #跟上面那条命令的效果是一样的 if len(res) == 0: res = "cmd exec success,has not output!".encode("utf-8") conn.send(str(len(res)).encode("utf-8")) #发送数据之前,先告诉客户端要发多少数据给它 print("等待客户ack应答...") client_final_ack = conn.recv(1024) #等待客户端响应 print("客户应答:",client_final_ack.decode()) print(type(res)) conn.sendall(res) #发送端也有最大数据量限制,所以这里用sendall,相当于重复循环调用conn.send,直至数据发送完毕 server.close()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#_*_coding:utf-8_*_ __author__ = 'Alex Li' import socket import sys client = socket.socket() client.connect(("localhost",9999)) while True: msg = input(">>:").strip() if len(msg) == 0:continue client.send( msg.encode("utf-8") ) res_return_size = client.recv(1024) #接收这条命令执行结果的大小 print("getting cmd result , ", res_return_size) total_rece_size = int(res_return_size) print("total size:",res_return_size) client.send("准备好接收了,发吧loser".encode("utf-8")) received_size = 0 #已接收到的数据 cmd_res = b'' f = open("test_copy.html","wb")#把接收到的结果存下来,一会看看收到的数据 对不对 while received_size != total_rece_size: #代表还没收完 data = client.recv(1024) received_size += len(data) #为什么不是直接1024,还判断len干嘛,注意,实际收到的data有可能比1024少 cmd_res += data else: print("数据收完了",received_size) #print(cmd_res.decode()) f.write(cmd_res) #把接收到的结果存下来,一会看看收到的数据 对不对 #print(data.decode()) #命令执行结果 client.close()
备注:可添加 time.sleep(0.5) 阻断分开,不过不是最终的办法。
人有傲骨终不贱,脚踏实地见真章;
超出预期为工作,价值体现显能力。