遠(yuǎn)程重啟zimbra服務(wù),python之sshtunnel+socket+thread編程

一残家、概要

由于偶爾需要重啟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è))

  1. 創(chuàng)建套接字,綁定套接字到本地IP與端口
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind()
  1. 開(kāi)始監(jiān)聽(tīng)連接
s.listen()
  1. 進(jìn)入循環(huán),不斷接受客戶(hù)端的連接請(qǐng)求拆座,并將連接的請(qǐng)求賦值給變量
s.accept()
  1. 接收/發(fā)送數(shù)據(jù)
s.recv()
s.sendall()
  1. 傳輸完畢后岖赋,關(guān)閉套接字
s.close()

TCP客戶(hù)端:

  1. 打開(kāi) socket
socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect()
  1. 連接后發(fā)送數(shù)據(jù)和接收數(shù)據(jù)
s.sendall()
s.recv()
  1. 傳輸完畢后杭抠,關(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è)功能:

  1. 服務(wù)端能夠無(wú)限制接收客戶(hù)端過(guò)來(lái)的消息爬早,并且支持多個(gè)終端同時(shí)連接哼丈,同時(shí)進(jìn)行會(huì)話
  2. 服務(wù)端接收到客戶(hù)端的固定指令(exit,quit筛严,close)斷開(kāi)連接
  3. 服務(wù)端可執(zhí)行客戶(hù)端發(fā)送過(guò)來(lái)的執(zhí)行命令
  4. 服務(wù)端不允許執(zhí)行正在運(yùn)行的命令
  5. 客戶(hù)端也要同樣的無(wú)限制得可以向服務(wù)端發(fā)送消息
  6. 客戶(hù)端發(fā)送固定執(zhí)行(exit醉旦,quit,close)自己中斷連接桨啃,客戶(hù)端執(zhí)行Ctrl+c也能中斷連接
  7. 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)雳攘,然后大功告成,撒花

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末枫笛,一起剝皮案震驚了整個(gè)濱河市来农,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌崇堰,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,744評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件涩咖,死亡現(xiàn)場(chǎng)離奇詭異海诲,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)檩互,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)特幔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人闸昨,你說(shuō)我怎么就攤上這事蚯斯。” “怎么了饵较?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,105評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵拍嵌,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我循诉,道長(zhǎng)横辆,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,242評(píng)論 1 292
  • 正文 為了忘掉前任茄猫,我火速辦了婚禮狈蚤,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘划纽。我一直安慰自己脆侮,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,269評(píng)論 6 389
  • 文/花漫 我一把揭開(kāi)白布勇劣。 她就那樣靜靜地躺著靖避,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上筋蓖,一...
    開(kāi)封第一講書(shū)人閱讀 51,215評(píng)論 1 299
  • 那天卸耘,我揣著相機(jī)與錄音,去河邊找鬼粘咖。 笑死蚣抗,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的瓮下。 我是一名探鬼主播翰铡,決...
    沈念sama閱讀 40,096評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼讽坏!你這毒婦竟也來(lái)了锭魔?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,939評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤路呜,失蹤者是張志新(化名)和其女友劉穎迷捧,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體胀葱,經(jīng)...
    沈念sama閱讀 45,354評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡漠秋,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,573評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了抵屿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片庆锦。...
    茶點(diǎn)故事閱讀 39,745評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖轧葛,靈堂內(nèi)的尸體忽然破棺而出搂抒,到底是詐尸還是另有隱情,我是刑警寧澤尿扯,帶...
    沈念sama閱讀 35,448評(píng)論 5 344
  • 正文 年R本政府宣布求晶,位于F島的核電站,受9級(jí)特大地震影響衷笋,放射性物質(zhì)發(fā)生泄漏誉帅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,048評(píng)論 3 327
  • 文/蒙蒙 一右莱、第九天 我趴在偏房一處隱蔽的房頂上張望蚜锨。 院中可真熱鬧,春花似錦慢蜓、人聲如沸亚再。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,683評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)氛悬。三九已至则剃,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間如捅,已是汗流浹背棍现。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,838評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留镜遣,地道東北人己肮。 一個(gè)月前我還...
    沈念sama閱讀 47,776評(píng)論 2 369
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像悲关,于是被迫代替她去往敵國(guó)和親谎僻。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,652評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理寓辱,服務(wù)發(fā)現(xiàn)艘绍,斷路器,智...
    卡卡羅2017閱讀 134,652評(píng)論 18 139
  • 最近在學(xué)習(xí)Python看了一篇文章寫(xiě)得不錯(cuò)秫筏,是在腳本之家里的诱鞠,原文如下,很有幫助: 一这敬、網(wǎng)絡(luò)知識(shí)的一些介紹 soc...
    qtruip閱讀 2,708評(píng)論 0 6
  • 7點(diǎn)30起床 8點(diǎn)洗漱完畢 8點(diǎn)30之前把桌子收拾完畢 9點(diǎn)之前把知米單詞背了 敷面膜休息一會(huì)兒 9點(diǎn)30開(kāi)始 整...
    你很完美閱讀 181評(píng)論 0 0
  • 做媽媽后航夺,我沒(méi)有遭遇太多的問(wèn)題,不會(huì)抑郁鹅颊、不會(huì)脫發(fā)、不會(huì)畏寒墓造,除了因?yàn)槟溉闊o(wú)法減肥之外堪伍,唯一困擾我的就是失眠了。 ...
    不太笨的懶女人閱讀 198評(píng)論 0 0
  • 活到大學(xué)了觅闽,我讀過(guò)了很多的書(shū)帝雇,也確實(shí)忘記了很多。 就像孩子時(shí)吃過(guò)的食物蛉拙,雖然絕大多數(shù)已經(jīng)忘記了味道尸闸。但不可否認(rèn)的是...
    一碗湯的距離閱讀 830評(píng)論 0 0