一残家、概要
由于偶爾需要重啟zimbra服務(wù)榆俺,出現(xiàn)故障時(shí)運(yùn)維人員可能不在電腦旁,故想要如何來(lái)使用手機(jī)便捷得去重啟服務(wù)器上的服務(wù)。服務(wù)器出于安全考慮茴晋,采用跳板機(jī)的方式ssh登錄陪捷,再進(jìn)行重啟服務(wù)操作
二、難點(diǎn)
zimbra服務(wù)器這里稱(chēng)為A诺擅,跳板機(jī)稱(chēng)為B市袖,運(yùn)維人員終端稱(chēng)為C
- A只允許B進(jìn)行ssh連接,也就是如果C要操作A烁涌,只能先連接上B苍碟,再?gòu)腂使用ssh連接A撮执,進(jìn)行對(duì)A的操作
- 如何遠(yuǎn)程操作A服務(wù)器
- socket服務(wù)端啟用端口監(jiān)聽(tīng)后县忌,由于硬件防火墻限制,監(jiān)聽(tīng)端口外網(wǎng)無(wú)法訪問(wèn)
- socket多用戶(hù)連接導(dǎo)致阻塞
- 多用戶(hù)在操作的時(shí)候,如果同一條命令沒(méi)有執(zhí)行完逼友,只允許一個(gè)用戶(hù)正常執(zhí)行
三、python sshtunnel 模塊
為了解決跳板機(jī)的問(wèn)題,以及監(jiān)聽(tīng)端口外網(wǎng)無(wú)法訪問(wèn)的問(wèn)題照棋,這里我想到了使用ssh的隧道來(lái)進(jìn)行連接(題外話,ssh隧道很強(qiáng)大符隙,有興趣的同學(xué)可以去谷歌學(xué)習(xí)了解下)驻售,那么python如何實(shí)現(xiàn)ssh隧道呢?這里就用到了sshtunnel,附上官方使用說(shuō)明:https://github.com/pahaz/sshtunnel类腮,然后下面先把代碼貼出來(lái):
#!/usr/bin/env python
# coding:utf-8
# Build by Zengfl
from sshtunnel import SSHTunnelForwarder
def connserver(host, port):
server = SSHTunnelForwarder(
ssh_address=('serverIP',serverport),
ssh_username='sshname',
ssh_password='sshpasswd',
local_bind_address=('127.0.0.1',55005),
remote_bind_address=('172.25.4.21',55005)
) #創(chuàng)建ssh隧道
try:
server.start() #ssh隧道連接
except:
print "[SSH CONNECT ERROR]"
exit(1)
- ssh_address: 我這里寫(xiě)的跳板機(jī)的IP地址和端口
- ssh_username:連接跳板機(jī)的ssh用戶(hù)名
- ssh_password: 連接跳板機(jī)的ssh密碼
- local_bind_address:ssh連上跳板機(jī)后綁定本地的哪個(gè)ip和端口厂抽,我這里綁定的是我本機(jī)的55005端口(端口數(shù)字隨便設(shè),只要不和本機(jī)已用端口沖突即可)
- remote_bind_address:將上條綁定的端口和遠(yuǎn)程服務(wù)器的端口進(jìn)行綁定映射藐守,這里我綁定的就是zimbra服務(wù)器的55005端口
以上代碼便創(chuàng)建了ssh隧道,并將遠(yuǎn)端zimbra服務(wù)器上的55005端口映射到我本機(jī)的55005端口,之后我訪問(wèn)本機(jī)的55005端口即是訪問(wèn)zimbra服務(wù)器的55005端口,完美繞過(guò)跳板機(jī)和防火墻的限制(思考:如果是mysql服務(wù)器如何來(lái)進(jìn)行隧道連接)
四、python socket網(wǎng)絡(luò)編程
Ⅰ.方案選擇
在解決了遠(yuǎn)程連接到跳板機(jī)后端的服務(wù)器之后棚品,就要思考如何來(lái)對(duì)后端的服務(wù)器進(jìn)行操作门怪,當(dāng)時(shí)我想到了兩個(gè)方案:
- Web頁(yè)來(lái)進(jìn)行操作
- 通過(guò)socket來(lái)進(jìn)行操作
由于考慮到使用web來(lái)操作的話囤锉,有點(diǎn)重酿傍,并且開(kāi)發(fā)周期會(huì)稍長(zhǎng)一點(diǎn)(可能是我比較菜,大佬應(yīng)該還是挺快的),所以我這里選擇了socket來(lái)進(jìn)行操作癣朗,做個(gè)輕量級(jí)的遠(yuǎn)程控制
Ⅱ.socket編程思路
TCP服務(wù)端: (UDP這里不做介紹扁达,由于本文主要概述一下 Python Socket 來(lái)解決服務(wù)器上服務(wù)的操作炉旷,因此不會(huì)對(duì)相關(guān)函數(shù)參數(shù)、返回值進(jìn)行詳細(xì)介紹罐盔,需要了解的可以查看相關(guān)手冊(cè))
- 創(chuàng)建套接字,綁定套接字到本地IP與端口
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind()
- 開(kāi)始監(jiān)聽(tīng)連接
s.listen()
- 進(jìn)入循環(huán),不斷接受客戶(hù)端的連接請(qǐng)求拆座,并將連接的請(qǐng)求賦值給變量
s.accept()
- 接收/發(fā)送數(shù)據(jù)
s.recv()
s.sendall()
- 傳輸完畢后岖赋,關(guān)閉套接字
s.close()
TCP客戶(hù)端:
- 打開(kāi) socket
socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect()
- 連接后發(fā)送數(shù)據(jù)和接收數(shù)據(jù)
s.sendall()
s.recv()
- 傳輸完畢后杭抠,關(guān)閉套接字
s.close()
Ⅲ. 創(chuàng)建socket客戶(hù)端和服務(wù)端連接
按照以上思路如下編寫(xiě)
服務(wù)端:
#!/usr/bin/env python
# coding:utf-8
# Build by Zengfl
import socket
import sys
import time
# 創(chuàng)建socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print 'Socket created'
# 綁定socket的ip和端口
try:
s.bind(("", 55005))
except socket.error , msg:
print 'Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1]
sys.exit()
# 監(jiān)聽(tīng)端口(用來(lái)控制連接的個(gè)數(shù)。如果設(shè)為 5铆遭,那么有 5 個(gè)連接正在等待處理橄妆,此時(shí)第 6 個(gè)請(qǐng)求過(guò)來(lái)時(shí)將會(huì)被拒絕)
s.listen(5)
print "[+] Server is running on port: 55005 at %s" % (time.strftime("%Y%m%d %H:%M:%S", time.localtime()))
# 接收連接
mainsocket, mainhost = s.accept()
print "[+] Connect success -> %s at %s" % (str(mainhost), time.strftime("%Y%m%d %H:%M:%S", time.localtime()))
# 接收消息
data = mainsocket.recv(1024)
if data:
print "[+] Receive:%s" % data
# 發(fā)送給客戶(hù)端消息接收成功的信息
mainsocket.sendall("[Server]success")
mainsocket.close()
s.close()
客戶(hù)端:
#!/usr/bin/env python
# coding:utf-8
# Build by Zengfl
import time
import socket
# 創(chuàng)建socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, 55005)) #host配置服務(wù)端的ip
# 終端輸入并發(fā)送到服務(wù)端
print "\n[*] Please input command:"
data = raw_input()
s.sendall(data)
# 接收服務(wù)端的返回結(jié)果
recvdata = s.recv(1024)
print "[+] Send %s:55005 -> %s" % (host, data)
if recvdata:
print "[+] Receive :%s" % recvdata
運(yùn)行結(jié)果:
服務(wù)端:
[root@iZwz9esy3n96ermeee5jzkZ ~]# python server.py
Socket created
[+] Server is running on port 55005: at 20171228 13:33:50
[+] Connect success -> ('183.15.179.197', 52653) at 20171228 13:34:23
[+] Receive:hello world
客戶(hù)端:
C:\Users\Zengfl\Desktop>python client.py
[*] Please input command:
hello world
[+] Send xxx.xxx.xx.xxx:55005 -> hello world
[+] Receive :[Server]success
總結(jié):
如上則完成了一次簡(jiǎn)單的會(huì)話通訊另绩,但是還是沒(méi)有達(dá)到我所期望的目的蹦漠。我們期望的是:
- 服務(wù)端能夠無(wú)限制接收客戶(hù)端過(guò)來(lái)的消息侍芝,并不是完成一次會(huì)話后就斷開(kāi)連接
- 服務(wù)端接收到客戶(hù)端的固定指令(exit,quit致板,close)才斷開(kāi)連接
- 客戶(hù)端也要同樣的無(wú)限制得可以向服務(wù)端發(fā)送消息
- 客戶(hù)端發(fā)送固定執(zhí)行(exit斟或,quit平斩,close)自己中斷連接
Ⅳ. 優(yōu)化(1)
針對(duì)上方總結(jié)出的需求揭璃,對(duì)代碼進(jìn)行優(yōu)化,需要將服務(wù)端一直接受請(qǐng)求,最簡(jiǎn)單的辦法就是將服務(wù)端接收消息放到一個(gè)循環(huán)中無(wú)限執(zhí)行蕉陋,這樣就能一直接收,客戶(hù)端同理
至于如何中斷連接患民,對(duì)數(shù)接收到的數(shù)據(jù)進(jìn)行判斷缩举,如果為exit,quit,close,則關(guān)閉連接酒奶,并且推出循環(huán)蚁孔。
優(yōu)化后的代碼如下:
服務(wù)端(這里使用函數(shù)來(lái)封裝,便于使用)
#!/usr/bin/env python
# coding:utf-8
# Build by Zengfl
import socket
import sys
import time
import threading
def recvdata(port):
close_list=["close session","exit","quit","close"] # 斷開(kāi)連接指令列表
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('', port))
s.listen(5)
print "[+] Server is running on port:%s at %s" % (str(port), time.strftime("%Y%m%d %H:%M:%S", time.localtime()))
mainsocket, mainhost = s.accept()
print "[+] Connect success -> %s at %s" % (str(mainhost), time.strftime("%Y%m%d %H:%M:%S", time.localtime()))
if mainhost:
# 增加收發(fā)消息的循環(huán)
while True:
data = mainsocket.recv(1024)
if data:
print "[+] Receive:%s" % data
mainsocket.sendall("[Server]success")
#判斷是否是斷開(kāi)連接的指令
if data in close_list:
mainsocket.close()
print "[+] Quit success"
break
if __name__ == "__main__":
connPort = 55005
onethreads = threading.Thread(target=recvdata, args=(connPort,))
onethreads.start()
客戶(hù)端
#!/usr/bin/env python
# coding:utf-8
# Build by Zengfl
import time
import socket
def connserver(host, port):
close_list=["close session","exit","quit","close"] #監(jiān)聽(tīng)輸入狀態(tài)惋嚎,如果為這些狀態(tài)則斷開(kāi)連接
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
while True:
print "\n[*] Please input command:"
data = raw_input()
if not data:
continue
s.sendall(data)
recvdata = s.recv(1024)
time.sleep(0)
if recvdata:
print "[+] Receive :%s" % recvdata
if data in close_list:
print "[+] Close sucess"
s.close()
break
if __name__ == "__main__":
server_list = ["xxx.xxx.xxx.xxx"]
connPort = 55005
if server_list != []:
for host in server_list:
connserver(host, connPort)
運(yùn)行結(jié)果:
服務(wù)端:
[root@iZwz9esy3n96ermeee5jzkZ ~]# python server.py
[+] Server is running on port:55005 at 20171228 14:55:10
[+] Connect success -> ('183.15.179.197', 53661) at 20171228 14:55:54
[+] Receive:hello world
[+] Receive:my name is kard
[+] Receive:bye bye
[+] Receive:exit
[+] Quit success
[root@iZwz9esy3n96ermeee5jzkZ ~]#
客戶(hù)端:
C:\Users\Zengfl\Desktop>python client.py
[*] Please input command:
hello world
[+] Receive :[Server]success
[*] Please input command:
my name is kard
[+] Receive :[Server]success
[*] Please input command:
bye bye
[+] Receive :[Server]success
[*] Please input command:
exit
[+] Receive :[Server]success
[+] Close sucess
總結(jié):
從上述代碼來(lái)看杠氢,我們已經(jīng)解決了之前遇到的問(wèn)題,實(shí)現(xiàn)了客戶(hù)端可以多次發(fā)送消息另伍,但是又有新的問(wèn)題:
- 服務(wù)端在收到客戶(hù)端退出請(qǐng)求以后鼻百,服務(wù)端也退出了監(jiān)聽(tīng),我想要的是服務(wù)端一直處于監(jiān)聽(tīng)狀態(tài)摆尝,只要有客戶(hù)端連接進(jìn)來(lái)温艇,就能夠正常收發(fā)消息
Ⅴ. 優(yōu)化(2)
針對(duì)此問(wèn)題,我們只需要修改服務(wù)端堕汞,將監(jiān)聽(tīng)會(huì)話連接也加個(gè)循環(huán)即可
服務(wù)端
#!/usr/bin/env python
# coding:utf-8
# Build by Zengfl
import socket
import sys
import time
import threading
def recvdata(port):
close_list=["close session","exit","quit","close"] # 斷開(kāi)連接指令列表
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('', port))
s.listen(5)
print "[+] Server is running on port:%s at %s" % (str(port), time.strftime("%Y%m%d %H:%M:%S", time.localtime()))
# 增加連接建立的循環(huán)
while True:
mainsocket, mainhost = s.accept()
print "[+] Connect success -> %s at %s" % (str(mainhost), time.strftime("%Y%m%d %H:%M:%S", time.localtime()))
if mainhost:
# 增加收發(fā)消息的循環(huán)
while True:
data = mainsocket.recv(1024)
if data:
print "[+] Receive:%s" % data
mainsocket.sendall("[Server]success")
#判斷是否是斷開(kāi)連接的指令
if data in close_list:
mainsocket.close()
print "[+] Quit success"
break
if __name__ == "__main__":
connPort = 55005
onethreads = threading.Thread(target=recvdata, args=(connPort,))
onethreads.start()
運(yùn)行結(jié)果:
服務(wù)端
[root@iZwz9esy3n96ermeee5jzkZ ~]# python server.py
[+] Server is running on port:55005 at 20171228 16:00:28
[+] Connect success -> ('183.15.179.197', 54658) at 20171228 16:01:11
[+] Receive:hello wordl
[+] Receive:ni hao
[+] Receive:bye bye
[+] Receive:exit
[+] Quit success
[+] Connect success -> ('183.15.179.197', 54663) at 20171228 16:01:34
[+] Receive:nihao
[+] Receive:bye
[+] Receive:close
[+] Quit success
客戶(hù)端
C:\Users\Zengfl\Desktop>python client.py
[*] Please input command:
hello wordl
[+] Receive :[Server]success
[*] Please input command:
ni hao
[+] Receive :[Server]success
[*] Please input command:
bye bye
[+] Receive :[Server]success
[*] Please input command:
exit
[+] Receive :[Server]success
[+] Close sucess
----------------------------------------------------------------------------------------------------------------
C:\Users\Zengfl\Desktop>python client.py
[*] Please input command:
nihao
[+] Receive :[Server]success
[*] Please input command:
bye
[+] Receive :[Server]success
[*] Please input command:
close
[+] Receive :[Server]success
[+] Close sucess
總結(jié):
以上方式修改了之后勺爱,服務(wù)端能夠一直監(jiān)聽(tīng)客戶(hù)端,并響應(yīng)消息讯检,客戶(hù)端退出重連都可以正常使用琐鲁。but!新的問(wèn)題和需求來(lái)了:
- 當(dāng)一個(gè)客戶(hù)端連接上了之后人灼,socket處于消息阻塞围段,故新的客戶(hù)端連接上去,服務(wù)端無(wú)法正常響應(yīng)投放,并且新客戶(hù)端會(huì)處于卡死狀態(tài)(具體原因是什么我還沒(méi)有去研究奈泪,忘大佬解答)
- 當(dāng)客戶(hù)端輸入Ctrl+c 退出后,服務(wù)端沒(méi)有接收到退出信息,故沒(méi)有中斷客戶(hù)端連接涝桅,導(dǎo)致服務(wù)端連接被占用
- 并且我們需要判斷客戶(hù)端的消息拜姿,然后進(jìn)行服務(wù)器上的操作
- 已經(jīng)在操作系統(tǒng)命令,不允許重復(fù)執(zhí)行
Ⅵ. 優(yōu)化(3)
對(duì)于消息阻塞冯遂,我這里想到了用多線程來(lái)解決此問(wèn)題
對(duì)于客戶(hù)端輸入Ctrl+c 我采用監(jiān)控KeyboardInterrupt異常來(lái)進(jìn)行判斷砾隅,如果出現(xiàn)此異常則向服務(wù)端發(fā)送中斷消息
然后客戶(hù)端消息判斷,則在服務(wù)端加一個(gè)判斷的函數(shù)债蜜,監(jiān)聽(tīng)cmd.run開(kāi)頭的信息,如果客戶(hù)端以cmd.run來(lái)發(fā)送消息究反,服務(wù)端則執(zhí)行cmd.run 后面的語(yǔ)句寻定。
已經(jīng)在執(zhí)行的命令,通過(guò)ps在服務(wù)上查看進(jìn)程精耐,判斷命令是否正在執(zhí)行
代碼如下:
服務(wù)端 (注釋解釋得挺詳細(xì)了)
#!/usr/bin/env python
# coding:utf-8
# By Zengfl
import time
import socket
import threading
import traceback
import subprocess
from thread import *
# 處理收到消息的方法狼速,判斷消息是否為cmd.run 開(kāi)頭
def parsecmd(strings):
midsplit = str(strings).split(" ")
# 判斷消息是否大于2個(gè)傳參并且為cmd.run開(kāi)頭
if len(midsplit) >= 2 and midsplit[0] == "cmd.run":
# 統(tǒng)計(jì)傳入的命令在服務(wù)器上是否有進(jìn)程在運(yùn)行,再進(jìn)行判斷
res=subprocess.Popen("ps -ef|grep \""+strings[8:]+"\"|grep -v grep|wc -l",stdout=subprocess.PIPE,shell=True)
pronum=int(res.stdout.read())
if pronum < 1:
try:
# 正常執(zhí)行消息傳進(jìn)來(lái)的參數(shù)
command = subprocess.Popen(strings[8:], shell=True)
command.communicate()
print "傳參:%s,執(zhí)行成功...請(qǐng)稍等"%strings
except Exception, e:
# 傳參執(zhí)行失敗
print "傳參:%s,執(zhí)行失斬酝!O蚝!惊完!"%strings
print e.message
traceback.print_exc()
else:
# 如果消息中傳的參數(shù)僵芹,服務(wù)器上有進(jìn)行在運(yùn)行,則不執(zhí)行
print "傳參:%s,正在執(zhí)行小槐。"%strings
else:
# 如果消息不以cmd.run 開(kāi)頭并且少于2個(gè)傳參拇派,則不執(zhí)行
print "收到傳參:%s,不予執(zhí)行!"%strings
# 客戶(hù)端消息收發(fā)的方法
def clientthread(mainsocket):
close_list=["close session","exit","quit","close"]
while True:
data = mainsocket.recv(1024)
if data:
mainsocket.sendall("[Server] success")
# 將收到的消息凿跳,傳到消息處理的方法中件豌,進(jìn)行消息處理
parsecmd(data)
if data in close_list:
print "[+] Client quit success"
break
mainsocket.close()
# 創(chuàng)建socket,并建立連接的方法
def recvdata(port):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 這條是我在網(wǎng)上看到粘包的問(wèn)題控嗜,加上的
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('', port))
s.listen(5)
print "[-] Server is running on port:%s" % str(port)
while True:
mainsocket, mainhost = s.accept()
print "[+] Connect success -> %s" % str(mainhost)
# 判斷連接是否成功茧彤,如果連接成功,將此連接的會(huì)話創(chuàng)建到一個(gè)線程中
if mainhost:
# 創(chuàng)建線程疆栏,線程中調(diào)用客戶(hù)端收發(fā)消息的方法
start_new_thread(clientthread ,(mainsocket,))
if __name__ == "__main__":
connPort = 55005
onethreads = threading.Thread(target=recvdata, args=(connPort,))
onethreads.start()
客戶(hù)端
#!/usr/bin/env python
# coding:utf-8
# Build by Zengfl
#
import time
import socket
def connserver(host, port):
close_list=["close session","exit","quit","close"] # 監(jiān)聽(tīng)輸入狀態(tài)曾掂,如果為這些狀態(tài)則斷開(kāi)連接
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
while True:
try:
print "\n[*] Please input command:"
data = raw_input()
if not data:
continue
s.sendall(data)
recvdata = s.recv(1024)
time.sleep(0)
if recvdata:
print "[+] Receive :%s" % recvdata
if data in close_list:
print "[+] Close sucess"
s.close()
break
except KeyboardInterrupt: #監(jiān)聽(tīng)鍵盤(pán)輸出Ctrl + c,并發(fā)送中斷消息以及中斷連接
s.sendall("close session")
recvdata = s.recv(1024)
if recvdata:
print "[+] Close sucess"
s.close()
break
if __name__ == "__main__":
server_list = ["xxx.xxx.xxx.xxx"]
connPort = 55005
if server_list != []:
for host in server_list:
connserver(host, connPort)
運(yùn)行結(jié)果:
客戶(hù)端1
C:\Users\Zengfl\Desktop>python test.py
[*] Please input command:
hello
[+] Receive :[Server] success
[*] Please input command:
world
[+] Receive :[Server] success
[*] Please input command:
wo shi 1
[+] Receive :[Server] success
[*] Please input command:
wo zai xian
[+] Receive :[Server] success
[*] Please input command:
bye bye
[+] Receive :[Server] success
[*] Please input command:
exit
[+] Receive :[Server] success
[+] Close sucess
C:\Users\Zengfl\Desktop>
客戶(hù)端2
C:\Users\Zengfl\Desktop>python test.py
[*] Please input command:
hello
[+] Receive :[Server] success
[*] Please input command:
wo shi 2
[+] Receive :[Server] success
[*] Please input command:
1 zai xian ma
[+] Receive :[Server] success
[*] Please input command:
1 zou le
[+] Receive :[Server] success
[*] Please input command:
wo ye zou le
[+] Receive :[Server] success
[*] Please input command:
exit
[+] Receive :[Server] success
[+] Close sucess
C:\Users\Zengfl\Desktop>
客戶(hù)端3
C:\Users\Zengfl\Desktop>python test.py
[*] Please input command:
cmd.run echo 111 > /tmp/hello
[+] Receive :[Server] success
[*] Please input command:
byebye
[+] Receive :[Server] success
[*] Please input command:
close
[+] Receive :[Server] success
[*] Please input command:
close session
[+] Receive :[Server] success
[+] Close sucess
C:\Users\Zengfl\Desktop>
服務(wù)端
[root@iZwz9esy3n96ermeee5jzkZ ~]# python server.py
[-] Server is running on port:55005
[+] Connect success -> ('183.15.179.197', 55884)
收到傳參:hello,不予執(zhí)行!
收到傳參:world,不予執(zhí)行承边!
收到傳參:wo shi 1,不予執(zhí)行遭殉!
[+] Connect success -> ('183.15.179.197', 55890)
收到傳參:hello,不予執(zhí)行!
收到傳參:wo shi 2,不予執(zhí)行博助!
收到傳參: 1 zai xian ma,不予執(zhí)行险污!
收到傳參:wo zai xian,不予執(zhí)行!
收到傳參:bye bye,不予執(zhí)行!
收到傳參:exit,不予執(zhí)行蛔糯!
[+] Client quit success
收到傳參:1 zou le,不予執(zhí)行拯腮!
收到傳參:wo ye zou le ,不予執(zhí)行!
收到傳參:exit,不予執(zhí)行蚁飒!
[+] Client quit success
[+] Connect success -> ('183.15.179.197', 57579)
傳參:cmd.run echo 111 > /tmp/hello,執(zhí)行成功...請(qǐng)稍等
收到傳參:byebye,不予執(zhí)行动壤!
收到傳參:close ,不予執(zhí)行!
收到傳參:close session,不予執(zhí)行淮逻!
[+] Client quit success
在服務(wù)端查看 /tmp/hello 文件:
[root@iZwz9esy3n96ermeee5jzkZ ~]# cat /tmp/hello
111
[root@iZwz9esy3n96ermeee5jzkZ ~]#
總結(jié):
綜上琼懊,所有的需求都完成了。解決了下面幾個(gè)功能:
- 服務(wù)端能夠無(wú)限制接收客戶(hù)端過(guò)來(lái)的消息爬早,并且支持多個(gè)終端同時(shí)連接哼丈,同時(shí)進(jìn)行會(huì)話
- 服務(wù)端接收到客戶(hù)端的固定指令(exit,quit筛严,close)斷開(kāi)連接
- 服務(wù)端可執(zhí)行客戶(hù)端發(fā)送過(guò)來(lái)的執(zhí)行命令
- 服務(wù)端不允許執(zhí)行正在運(yùn)行的命令
- 客戶(hù)端也要同樣的無(wú)限制得可以向服務(wù)端發(fā)送消息
- 客戶(hù)端發(fā)送固定執(zhí)行(exit醉旦,quit,close)自己中斷連接桨啃,客戶(hù)端執(zhí)行Ctrl+c也能中斷連接
- s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 這個(gè)解決粘包問(wèn)題:參考文檔
五车胡、結(jié)合ssh隧道,實(shí)現(xiàn)跨跳板機(jī)進(jìn)行服務(wù)端會(huì)話
跳板機(jī)的問(wèn)題解決了
服務(wù)端遠(yuǎn)程執(zhí)行遠(yuǎn)程指令也解決了
現(xiàn)在就使用這兩個(gè)功能實(shí)現(xiàn)我們最初的目的照瘾,并將之前print的輸出記錄到日志匈棘,直接貼代碼吧:
服務(wù)端
#!/usr/bin/env python
# coding:utf-8
# By Zengfl
import time,os,logging
import socket
import threading
import traceback
import subprocess
from thread import *
#日期模塊
def unix_te(day=0):
return time.strftime(
'%Y-%m-%d',
time.localtime(time.time()-86400*day)
)
#定義日志配置
if not os.path.exists('/home/work/script/logs'): os.system('mkdir -p /home/work/script/logs')
logging.basicConfig(filename = '/home/work/script/logs/%s.log' % unix_te(), level = logging.DEBUG, filemode = 'a', format = '%(asctime)s - %(levelname)s: %(message)s')
def parsecmd(strings):
midsplit = str(strings).split(" ")
if len(midsplit) >= 2 and midsplit[0] == "cmd.run":
res=subprocess.Popen("ps -ef|grep \""+strings[8:]+"\"|grep -v grep|wc -l",stdout=subprocess.PIPE,shell=True)
pronum=int(res.stdout.read())
if pronum < 1:
try:
command = subprocess.Popen(strings[8:], shell=True)
command.communicate()
logging.info("傳參:%s,執(zhí)行成功...請(qǐng)稍等"%strings)
except Exception, e:
logging.error("傳參:%s,執(zhí)行失敗N雒8巍!"%strings)
print e.message
traceback.print_exc()
else:
logging.warning("傳參:%s,正在執(zhí)行碳却。"%strings)
else:
logging.info("收到傳參:%s,不予執(zhí)行队秩!"%strings)
def clientthread(mainsocket):
close_list=["close session","exit","quit","close"]
while True:
data = mainsocket.recv(1024)
if data:
mainsocket.sendall("[Server] success")
parsecmd(data)
if data in close_list:
#mainsocket.close()
logging.info("[+] Client quit success")
break
mainsocket.close()
def recvdata(port):
#close_list=["close session","exit","quit","close"]
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('', port))
s.listen(5)
logging.info("[-] Server is running on port:%s" % str(port))
while True:
mainsocket, mainhost = s.accept()
logging.info("[+] Connect success -> %s" % str(mainhost))
if mainhost:
start_new_thread(clientthread ,(mainsocket,))
if __name__ == "__main__":
connPort = 55005
onethreads = threading.Thread(target=recvdata, args=(connPort,))
onethreads.start()
客戶(hù)端
#!/usr/bin/env python
# coding:utf-8
# Build by Zengfl
import time
import socket
from sshtunnel import SSHTunnelForwarder
def connserver(host, port):
server = SSHTunnelForwarder(
ssh_address=('xx.xx.xx.xx',xxx),
ssh_username='xxx',
ssh_password='xxxx',
local_bind_address=('127.0.0.1',55005),
remote_bind_address=('192.168.1.21',55005)
) #創(chuàng)建ssh隧道
try:
server.start() #ssh隧道連接
except:
print "[SSH CONNECT ERROR]"
exit(1)
close_list=["close session","exit","quit","close"] #監(jiān)聽(tīng)輸入狀態(tài),如果為這些狀態(tài)則斷開(kāi)連接
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
while True:
try:
print "\n[*] Please input command:"
data = raw_input()
if not data:
continue
s.sendall(data)
recvdata = s.recv(1024)
time.sleep(0)
if recvdata:
print "[+] Receive :%s" % recvdata
if data in close_list:
print "[+] Close sucess"
s.close()
break
except KeyboardInterrupt: #監(jiān)聽(tīng)鍵盤(pán)輸出Ctrl + c,并中斷連接
s.sendall("close session")
recvdata = s.recv(1024)
if recvdata:
print "[+] Close sucess"
s.close()
break
server.stop() #關(guān)閉ssh隧道
if __name__ == "__main__":
server_list = ["127.0.0.1"] # 這里很重要昼浦,因?yàn)槲襰sh做隧道的時(shí)候監(jiān)聽(tīng)的就是本機(jī)的55005端口
connPort = 55005
if server_list != []:
for host in server_list:
connserver(host, connPort)
重啟zimbra腳本
#!/usr/bin/env python
# coding:utf-8
# Build by Zengfl
import time
import socket
from sshtunnel import SSHTunnelForwarder
def connserver(host, port):
server = SSHTunnelForwarder(
ssh_address=('xxx.xxx.xxx.xxx',xxx),
ssh_username='xxx',
ssh_password='xxx',
local_bind_address=('127.0.0.1',55005),
remote_bind_address=('192.168.1.21',55005)
) #創(chuàng)建ssh隧道
try:
server.start() #ssh隧道連接
except:
print "[SSH CONNECT ERROR]"
exit(1)
close_list=["close session","exit","quit","close"] #監(jiān)聽(tīng)輸入狀態(tài)馍资,如果為這些狀態(tài)則斷開(kāi)連接
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
data = "cmd.run /etc/init.d/zimbra restart"
s.sendall(data)
recvdata = s.recv(1024)
if recvdata:
print "[+] Receive :%s" % recvdata
print u"操作成功,請(qǐng)等待5分鐘后檢測(cè)郵件收發(fā)是否正常"
else:
print "操作失敗"
s.sendall("close session")
recvdata = s.recv(1024)
s.close()
server.stop() #關(guān)閉ssh隧道
if __name__ == "__main__":
server_list = ["127.0.0.1"]
connPort = 55005
if server_list != []:
for host in server_list:
connserver(host, connPort)
測(cè)試這里就不演示了关噪,我這里已經(jīng)在正常使用了鸟蟹,同學(xué)們可以參考此代碼自行進(jìn)行嘗試
六、總結(jié):
目前為止使兔,已經(jīng)解決了我所期望的需求建钥,最后再將程序打個(gè)exe包就行了。具體如何打包虐沥,使用pyinstaller這個(gè)工具熊经,怎么使用不是本文的重點(diǎn)
代碼有寫(xiě)得不好的地方忘大佬們多多包涵并指出缺點(diǎn)泽艘,定改正
本文主要講述的是一個(gè)解決問(wèn)題的思路,一步步將我解決問(wèn)題的流程和思路捋了出來(lái)镐依,說(shuō)實(shí)話寫(xiě)這篇文章比我完成這份代碼花的時(shí)間長(zhǎng)得多匹涮。
當(dāng)碰到需求不要急于去動(dòng)手做,先想好這個(gè)需求需要面臨些什么問(wèn)題槐壳,然后再化整為零然低,把需求細(xì)分成小的功能點(diǎn),逐一解決务唐,最后將各個(gè)功能點(diǎn)結(jié)合起來(lái)雳攘,然后大功告成,撒花