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数据结构之Array用法实例
Oct 09 Python
python xml.etree.ElementTree遍历xml所有节点实例详解
Dec 04 Python
python线程池threadpool实现篇
Apr 27 Python
django中forms组件的使用与注意
Jul 08 Python
基于Python实现大文件分割和命名脚本过程解析
Sep 29 Python
Django框架创建项目的方法入门教程
Nov 04 Python
python中seaborn包常用图形使用详解
Nov 25 Python
Python 多线程共享变量的实现示例
Apr 17 Python
使用Keras 实现查看model weights .h5 文件的内容
Jun 09 Python
使用SQLAlchemy操作数据库表过程解析
Jun 10 Python
python上selenium的弹框操作实现
Jul 13 Python
详解python日志输出使用配置文件格式
Feb 10 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实现的简单日志写入函数
2015/03/31 PHP
php实现json编码的方法
2015/07/30 PHP
谈谈 PHP7新增功能
2015/12/16 PHP
php抛出异常与捕捉特定类型的异常详解
2016/10/26 PHP
php操作redis常见方法示例【key与value操作】
2020/04/14 PHP
js 判断浏览器类型 去全角、半角空格 自动关闭当前窗口
2009/04/10 Javascript
如何获取JQUERY AJAX返回的JSON结果集实现代码
2012/12/10 Javascript
js 编码转换 gb2312 和 utf8 互转的2种方法
2013/08/07 Javascript
js实现class样式的修改、添加及删除的方法
2015/01/20 Javascript
JS实现单行文字不间断向上滚动的方法
2015/01/29 Javascript
vue.js开发环境安装教程
2017/03/17 Javascript
vue动态路由实现多级嵌套面包屑的思路与方法
2017/08/16 Javascript
JsChart组件使用详解
2018/03/04 Javascript
跨域请求两种方法 jsonp和cors的实现
2018/11/11 Javascript
JavaScript的垃圾回收机制与内存管理
2020/08/06 Javascript
微信小程序实现可长按移动控件
2020/11/01 Javascript
Vue $attrs &amp; inheritAttr实现button禁用效果案例
2020/12/07 Vue.js
Python库urllib与urllib2主要区别分析
2014/07/13 Python
Python3遍历目录树实现方法
2015/05/22 Python
python 获取文件下所有文件或目录os.walk()的实例
2018/04/23 Python
python实现简单http服务器功能
2018/09/17 Python
Python合并同一个文件夹下所有PDF文件的方法
2019/03/11 Python
python爬取基于m3u8协议的ts文件并合并
2019/04/26 Python
django将数组传递给前台模板的方法
2019/08/06 Python
python 公共方法汇总解析
2019/09/16 Python
解决Python3.7.0 SSL低版本导致Pip无法使用问题
2020/09/03 Python
Python 中Operator模块的使用
2021/01/30 Python
Python如何定义一个函数
2015/09/01 面试题
毕业生机械建模求职信
2013/10/14 职场文书
音乐专业应届生教师求职信
2013/11/04 职场文书
机电工程学生自荐信范文
2013/12/07 职场文书
工伤赔偿协议书
2014/04/15 职场文书
三傻大闹宝莱坞观后感
2015/06/03 职场文书
签字仪式主持词
2015/07/03 职场文书
初中军训感想
2015/08/07 职场文书
Python Flask搭建yolov3目标检测系统详解流程
2021/11/07 Python