python制作websocket服务器实例分享


Posted in Python onNovember 20, 2016

一、开始的话

使用python简单的实现websocket服务器,可以在浏览器上实时显示远程服务器的日志信息。

之前做了一个web版的发布系统,但没实现在线看日志,每次发布版本后,都需要登录到服务器上查看日志,非常麻烦,为了偷懒,能在页面点几下按钮完成工作,这几天查找了这方面的资料,实现了这个功能,瞬间觉的看日志什么的,太方便了,以后也可以给开发们查日志,再也不用麻烦运维了,废话少说,先看效果吧。

python制作websocket服务器实例分享

二、代码

需求:在web上弹出iframe层来实时显示远程服务器的日志,点击stop按钮,停止日志输出,以便查看相关日志,点start按钮,继续输出日志,点close按钮,关闭iframe层。

在实现这功能前,google了一些资料,发现很多只能在web上显示本地的日志,不能看远程服务器的日志,能看远程日志的是引用了其他框架(例如bottle,tornado)来实现的,而且所有这些都是要重写thread的run方法来实现的,由于本人技术太菜,不知道怎么改成自己需要的样子,而且我是用django这个web框架的,不想引入其他框架,搞的太复杂,所以用python简单的实现websocket服务器。recv_data方法和send_data是直接引用别人的代码。由于技术问题,代码有点粗糙,不过能实现功能就行,先将就着用吧。

执行下面命令启动django和websocketserver

nohup python manage.py runserver 10.1.12.110 &
nohup python websocketserver.py &

启动websocket后,接收到请求,起一个线程和客户端握手,然后根据客户端发送的ip和type,去数据库查找对应的日志路径,用paramiko模块ssh登录到远程服务器上tail查看日志,再推送给浏览器,服务端完整代码如下:

# coding:utf-8
import os
import struct
import base64
import hashlib
import socket
import threading
import paramiko


def get_ssh(ip, user, pwd):
  try:
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect(ip, 22, user, pwd, timeout=15)
    return ssh
  except Exception, e:
    print e
    return "False"


def recv_data(conn):  # 服务器解析浏览器发送的信息
  try:
    all_data = conn.recv(1024)
    if not len(all_data):
      return False
  except:
    pass
  else:
    code_len = ord(all_data[1]) & 127
    if code_len == 126:
      masks = all_data[4:8]
      data = all_data[8:]
    elif code_len == 127:
      masks = all_data[10:14]
      data = all_data[14:]
    else:
      masks = all_data[2:6]
      data = all_data[6:]
    raw_str = ""
    i = 0
    for d in data:
      raw_str += chr(ord(d) ^ ord(masks[i % 4]))
      i += 1
    return raw_str


def send_data(conn, data):  # 服务器处理发送给浏览器的信息
  if data:
    data = str(data)
  else:
    return False
  token = "\x81"
  length = len(data)
  if length < 126:
    token += struct.pack("B", length)  # struct为Python中处理二进制数的模块,二进制流为C,或网络流的形式。
  elif length <= 0xFFFF:
    token += struct.pack("!BH", 126, length)
  else:
    token += struct.pack("!BQ", 127, length)
  data = '%s%s' % (token, data)
  conn.send(data)
  return True


def handshake(conn, address, thread_name):
  headers = {}
  shake = conn.recv(1024)
  if not len(shake):
    return False

  print ('%s : Socket start handshaken with %s:%s' % (thread_name, address[0], address[1]))
  header, data = shake.split('\r\n\r\n', 1)
  for line in header.split('\r\n')[1:]:
    key, value = line.split(': ', 1)
    headers[key] = value

  if 'Sec-WebSocket-Key' not in headers:
    print ('%s : This socket is not websocket, client close.' % thread_name)
    conn.close()
    return False

  MAGIC_STRING = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
  HANDSHAKE_STRING = "HTTP/1.1 101 Switching Protocols\r\n" \
            "Upgrade:websocket\r\n" \
            "Connection: Upgrade\r\n" \
            "Sec-WebSocket-Accept: {1}\r\n" \
            "WebSocket-Origin: {2}\r\n" \
            "WebSocket-Location: ws://{3}/\r\n\r\n"

  sec_key = headers['Sec-WebSocket-Key']
  res_key = base64.b64encode(hashlib.sha1(sec_key + MAGIC_STRING).digest())
  str_handshake = HANDSHAKE_STRING.replace('{1}', res_key).replace('{2}', headers['Origin']).replace('{3}', headers['Host'])
  conn.send(str_handshake)
  print ('%s : Socket handshaken with %s:%s success' % (thread_name, address[0], address[1]))
  print 'Start transmitting data...'
  print '- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -'
  return True


def dojob(conn, address, thread_name):
  handshake(conn, address, thread_name)   # 握手
  conn.setblocking(0)            # 设置socket为非阻塞

  ssh = get_ssh('192.168.1.1', 'root', '123456')  # 连接远程服务器
  ssh_t = ssh.get_transport()
  chan = ssh_t.open_session()
  chan.setblocking(0)  # 设置非阻塞
  chan.exec_command('tail -f /var/log/messages')

  while True:
    clientdata = recv_data(conn)
    if clientdata is not None and 'quit' in clientdata:  # 但浏览器点击stop按钮或close按钮时,断开连接
      print ('%s : Socket close with %s:%s' % (thread_name, address[0], address[1]))
      send_data(conn, 'close connect')
      conn.close()
      break
    while True:
      while chan.recv_ready():
        clientdata1 = recv_data(conn)
        if clientdata1 is not None and 'quit' in clientdata1:
          print ('%s : Socket close with %s:%s' % (thread_name, address[0], address[1]))
          send_data(conn, 'close connect')
          conn.close()
          break
        log_msg = chan.recv(10000).strip()  # 接收日志信息
        print log_msg
        send_data(conn, log_msg)
      if chan.exit_status_ready():
        break
      clientdata2 = recv_data(conn)
      if clientdata2 is not None and 'quit' in clientdata2:
        print ('%s : Socket close with %s:%s' % (thread_name, address[0], address[1]))
        send_data(conn, 'close connect')
        conn.close()
        break
    break


def ws_service():

  index = 1
  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  sock.bind(("127.0.0.1", 12345))
  sock.listen(100)

  print ('\r\n\r\nWebsocket server start, wait for connect!')
  print '- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -'
  while True:
    connection, address = sock.accept()
    thread_name = 'thread_%s' % index
    print ('%s : Connection from %s:%s' % (thread_name, address[0], address[1]))
    t = threading.Thread(target=dojob, args=(connection, address, thread_name))
    t.start()
    index += 1


ws_service()

get_ssh的代码如下:

import paramiko
def get_ssh(ip, user, pwd):
  try:
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect(ip, 22, user, pwd, timeout=15)
    return ssh
  except Exception, e:
    print e
    return "False"

打开页面时,自动连接websocket服务器,完成握手,并发送ip和type给服务端,所以可以看不同类型,不同机器上的日志,

python制作websocket服务器实例分享

 页面代码如下:

<!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 >
    <div >
      <div id="log" ></div>
      <br>
    </div>
  </div>
  <div >
    <div >
      <div id="btns">
        <input disabled="disabled" type="button" value="start" id="start" onclick="init()">
        <input disabled="disabled" type="button" value="stop" id="stop" onclick="stop()" >
        <input type="button" value="close" id="close" onclick="closelayer()" >
      </div>
    </div>
  </div>
</body>
</html>

以上就是本文的全部内容了,希望大家能够喜欢

Python 相关文章推荐
python不带重复的全排列代码
Aug 13 Python
python计算方程式根的方法
May 07 Python
python函数局部变量用法实例分析
Aug 04 Python
python实现井字棋游戏
Mar 30 Python
Python+PIL实现支付宝AR红包
Feb 09 Python
Python实现监控键盘鼠标操作示例【基于pyHook与pythoncom模块】
Sep 04 Python
python实现汽车管理系统
Nov 30 Python
python运行时强制刷新缓冲区的方法
Jan 14 Python
python爬虫爬取笔趣网小说网站过程图解
Nov 18 Python
python中JWT用户认证的实现
May 18 Python
Python中Selenium库使用教程详解
Jul 23 Python
解决TensorFlow训练模型及保存数量限制的问题
Mar 03 Python
Flask框架的学习指南之用户登录管理
Nov 20 #Python
Flask框架的学习指南之制作简单blog系统
Nov 20 #Python
Flask框架的学习指南之开发环境搭建
Nov 20 #Python
Python 描述符(Descriptor)入门
Nov 20 #Python
独特的python循环语句
Nov 20 #Python
【Python】Python的urllib模块、urllib2模块批量进行网页下载文件
Nov 19 #Python
Python基础中所出现的异常报错总结
Nov 19 #Python
You might like
php 获取客户端的真实ip
2009/11/30 PHP
PHP捕获Fatal error错误的方法
2014/06/11 PHP
php+jQuery.uploadify实现文件上传教程
2014/12/26 PHP
PHP实现的多文件上传类及用法示例
2016/05/06 PHP
js left,right,mid函数
2008/06/10 Javascript
javascript 面向对象编程基础:封装
2009/08/21 Javascript
js+xml生成级联下拉框代码
2012/07/24 Javascript
一个字符串反转函数可实现字符串倒序
2014/09/15 Javascript
Nodejs学习笔记之Stream模块
2015/01/13 NodeJs
JavaScript检查数字是否为整数或浮点数的方法
2015/06/09 Javascript
jQuery中prepend()方法使用详解
2015/08/11 Javascript
JS常见问题之为什么点击弹出的i总是最后一个
2016/01/05 Javascript
原生JS实现图片轮播与淡入效果的简单实例
2016/08/21 Javascript
基于jQuery实现的查看全文功能【实用】
2016/12/11 Javascript
Javascript中的async awai的用法
2017/05/17 Javascript
JS实现给json数组动态赋值的方法示例
2020/03/19 Javascript
详解从零搭建 vue2 vue-router2 webpack3 工程
2017/11/22 Javascript
vue解决弹出蒙层滑动穿透问题的方法
2018/09/22 Javascript
vue全局自定义指令-元素拖拽的实现代码
2019/04/14 Javascript
更强大的vue ssr实现预取数据的方式
2019/07/19 Javascript
微信小程序官方动态自定义底部tabBar的例子
2019/09/04 Javascript
基于js实现数组相邻元素上移下移
2020/05/19 Javascript
Nodejs 数组的队列以及forEach的应用详解
2021/02/25 NodeJs
Python 学习笔记
2008/12/27 Python
Django重置migrations文件的方法步骤
2019/05/01 Python
python中pytest收集用例规则与运行指定用例详解
2019/06/27 Python
python实现日志按天分割
2019/07/22 Python
详解Django CAS 解决方案
2019/10/30 Python
JINS眼镜官方网站:日本最大的眼镜邮购
2016/10/14 全球购物
中国跨境电子商务网站:NewFrog
2018/03/10 全球购物
Super-Pharm波兰:药房和香水在一个地方
2020/08/18 全球购物
高等教育学自荐书范文
2014/02/10 职场文书
优秀共产党员先进事迹材料
2014/05/06 职场文书
局火灾防控工作方案
2014/05/25 职场文书
Vue实现下拉加载更多
2021/05/09 Vue.js
SQL语法CONSTRAINT约束操作详情
2022/01/18 MySQL