python2.7實現websocket服務器,可以在web實時顯示遠程服務器日志


一、開始的話

  使用python實現websocket服務器,可以在瀏覽器上實時顯示遠程服務器的日志。

  之前寫了一個發布系統,每次發布版本后,為了了解發布情況(進度、是否有錯誤)都會登錄到服務器上查看日志,有點麻煩,如果發布的服務器比較多,難道要登錄到每台服務器去看日志嗎?作為新時代的運維,太不能接收這種重復操作的體力勞動了,於是一個看日志的功能就這么誕生了。下面是效果圖,頁面丑陋不堪,將就着吧。

 

二、行動

  打開頁面時,自動連接websocket服務器,完成握手,並發送ip和type給服務端,所以可以看不同類型,不同機器上的日志,websocket服務器接收到信息后,去數據庫查找對應的日志路徑和主機賬號密碼,然后起一個線程ssh登錄到遠程服務器上tail查看日志,再推送給瀏覽器,代碼如下:

  1 # coding:utf-8
  2 import os
  3 import struct
  4 import base64
  5 import hashlib
  6 import socket
  7 import threading
  8 import paramiko
  9 
 10 
 11 def get_ssh(ip, user, pwd):
 12     try:
 13         ssh = paramiko.SSHClient()
 14         ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
 15         ssh.connect(ip, 22, user, pwd, timeout=15)
 16         return ssh
 17     except Exception, e:
 18         print e
 19         return "False"
 20 
 21 
 22 def recv_data(conn):    # 服務器解析瀏覽器發送的信息
 23     try:
 24         all_data = conn.recv(1024)
 25         if not len(all_data):
 26             return False
 27     except:
 28         pass
 29     else:
 30         code_len = ord(all_data[1]) & 127
 31         if code_len == 126:
 32             masks = all_data[4:8]
 33             data = all_data[8:]
 34         elif code_len == 127:
 35             masks = all_data[10:14]
 36             data = all_data[14:]
 37         else:
 38             masks = all_data[2:6]
 39             data = all_data[6:]
 40         raw_str = ""
 41         i = 0
 42         for d in data:
 43             raw_str += chr(ord(d) ^ ord(masks[i % 4]))
 44             i += 1
 45         return raw_str
 46 
 47 
 48 def send_data(conn, data):   # 服務器處理發送給瀏覽器的信息
 49     if data:
 50         data = str(data)
 51     else:
 52         return False
 53     token = "\x81"
 54     length = len(data)
 55     if length < 126:
 56         token += struct.pack("B", length)    # struct為Python中處理二進制數的模塊,二進制流為C,或網絡流的形式。
 57     elif length <= 0xFFFF:
 58         token += struct.pack("!BH", 126, length)
 59     else:
 60         token += struct.pack("!BQ", 127, length)
 61     data = '%s%s' % (token, data)
 62     conn.send(data)
 63     return True
 64 
 65 
 66 def handshake(conn, address, thread_name):    # 握手建立連接  67     headers = {}
 68     shake = conn.recv(1024)
 69     if not len(shake):
 70         return False
 71 
 72     print ('%s : Socket start handshaken with %s:%s' % (thread_name, address[0], address[1]))
 73     header, data = shake.split('\r\n\r\n', 1)
 74     for line in header.split('\r\n')[1:]:
 75         key, value = line.split(': ', 1)
 76         headers[key] = value
 77 
 78     if 'Sec-WebSocket-Key' not in headers:
 79         print ('%s : This socket is not websocket, client close.' % thread_name)
 80         conn.close()
 81         return False
 82 
 83     MAGIC_STRING = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
 84     HANDSHAKE_STRING = "HTTP/1.1 101 Switching Protocols\r\n" \
 85                        "Upgrade:websocket\r\n" \
 86                        "Connection: Upgrade\r\n" \
 87                        "Sec-WebSocket-Accept: {1}\r\n" \
 88                        "WebSocket-Origin: {2}\r\n" \
 89                        "WebSocket-Location: ws://{3}/\r\n\r\n"
 90 
 91     sec_key = headers['Sec-WebSocket-Key']
 92     res_key = base64.b64encode(hashlib.sha1(sec_key + MAGIC_STRING).digest())
 93     str_handshake = HANDSHAKE_STRING.replace('{1}', res_key).replace('{2}', headers['Origin']).replace('{3}', headers['Host'])
 94     conn.send(str_handshake)                 # 發送建立連接的信息  95     print ('%s : Socket handshaken with %s:%s success' % (thread_name, address[0], address[1]))
 96     print 'Start transmitting data...'
 97     print '- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -'
 98     return True
 99 
100 
101 def dojob(conn, address, thread_name):
102     handshake(conn, address, thread_name)     # 握手
103     conn.setblocking(0)                       # 設置socket為非阻塞
104 
105     ssh = get_ssh('192.168.1.1', 'root', '123456')   # 連接遠程服務器(日志所在的服務器)
106     ssh_t = ssh.get_transport()
107     chan = ssh_t.open_session()
108     chan.setblocking(0)          # 設置非阻塞
109     chan.exec_command('tail -f /var/log/messages')   # 執行查看日志命令 110 
111     while True:            # 下面這個邏輯是在看日志時能停止或繼續,寫的有點混亂,還沒想到優化的方案 112         clientdata = recv_data(conn)
113         if clientdata is not None and 'quit' in clientdata:    # 當瀏覽器點擊stop按鈕或close按鈕時,斷開連接
114             print ('%s : Socket close with %s:%s' % (thread_name, address[0], address[1]))
115             send_data(conn, 'close connect')
116             conn.close()
117             break
118         while True:
119             while chan.recv_ready():
120                 clientdata1 = recv_data(conn)
121                 if clientdata1 is not None and 'quit' in clientdata1:
122                     print ('%s : Socket close with %s:%s' % (thread_name, address[0], address[1]))
123                     send_data(conn, 'close connect')
124                     conn.close()
125                     break
126                 log_msg = chan.recv(10000).strip()    # 接收日志信息
127                 print log_msg
128                 send_data(conn, log_msg)
129             if chan.exit_status_ready():
130                 break
131             clientdata2 = recv_data(conn)
132             if clientdata2 is not None and 'quit' in clientdata2:
133                 print ('%s : Socket close with %s:%s' % (thread_name, address[0], address[1]))
134                 send_data(conn, 'close connect')
135                 conn.close()
136                 break
137         break
138 
139 
140 def ws_service():
141 
142     index = 1
143     sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
144     sock.bind(("127.0.0.1", 12345))
145     sock.listen(100)
146 
147     print ('\r\n\r\nWebsocket server start, wait for connect!')
148     print '- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -'
149     while True:
150         connection, address = sock.accept()
151         thread_name = 'thread_%s' % index
152         print ('%s : Connection from %s:%s' % (thread_name, address[0], address[1]))
153         t = threading.Thread(target=dojob, args=(connection, address, thread_name))
154         t.start()
155         index += 1
156 
157 
158 ws_service()

 

頁面代碼如下:

<!DOCTYPE html>
<html>
<head>
<title>WebSocket</title>

<style>
#log {
width: 440px;
height: 200px;
border: 1px solid #7F9DB9;
overflow: auto;
}
pre {
margin: 0 0 0;
padding: 0;
border: hidden;
background-color: #0c0c0c;
color: #00ff00;
}
#btns {
text-align: right;
}
</style>

<script>
var socket;
function init() {
var host = "ws://127.0.0.1:12345/";

try {
socket = new WebSocket(host);
socket.onopen = function () {
log('Connected');
};
socket.onmessage = function (msg) {
log(msg.data);
var obje = document.getElementById("log"); //日志過多時清屏
var textlength = obje.scrollHeight;
if (textlength > 10000) {
obje.innerHTML = '';
}
};
socket.onclose = function () {
log("Lose Connection!");
$("#start").attr('disabled', false);
$("#stop").attr('disabled', true);
};
$("#start").attr('disabled', true);
$("#stop").attr('disabled', false);
}
catch (ex) {
log(ex);
}
}
window.onbeforeunload = function () {
try {
socket.send('quit');
socket.close();
socket = null;
}
catch (ex) {
log(ex);
}
};
function log(msg) {
var obje = document.getElementById("log");
obje.innerHTML += '<pre><code>' + msg + '</code></pre>';
obje.scrollTop = obje.scrollHeight; //滾動條顯示最新數據
}
function stop() {
try {
log('Close connection!');
socket.send('quit');
socket.close();
socket = null;
$("#start").attr('disabled', false);
$("#stop").attr('disabled', true);
}
catch (ex) {
log(ex);
}
}
function closelayer() {
try {
log('Close connection!');
socket.send('quit');
socket.close();
socket = null;
}
catch (ex) {
log(ex);
}
var index = parent.layer.getFrameIndex(window.name); //先得到當前iframe層的索引
parent.layer.close(index); //再執行關閉
}
</script>

</head>


<body onload="init()">
<div class="row">
<div class="col-lg-12">
<div id="log" style="width: 100%;height:440px;background-color: #0c0c0c;overflow:scroll;overflow-x: auto;"></div>
<br>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<div id="btns">
<input disabled="disabled" type="button" class="btn btn-primary btn-sm" value="start" id="start" onclick="init()">
<input disabled="disabled" type="button" class="btn btn-primary btn-sm" value="stop" id="stop" onclick="stop()" >
<input type="button" class="btn btn-primary btn-sm" value="close" id="close" onclick="closelayer()" >
</div>
</div>
</div>
</body>

</html>

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM