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操作json数据的一个简单例子
Apr 17 Python
python传递参数方式小结
Apr 17 Python
利用Python如何生成hash值示例详解
Dec 20 Python
python实现远程通过网络邮件控制计算机重启或关机
Feb 22 Python
解决python opencv无法显示图片的问题
Oct 28 Python
PIL对上传到Django的图片进行处理并保存的实例
Aug 07 Python
Python安装及Pycharm安装使用教程图解
Sep 20 Python
python 多进程并行编程 ProcessPoolExecutor的实现
Oct 11 Python
Python内置类型性能分析过程实例
Jan 29 Python
django rest framework使用django-filter用法
Jul 15 Python
python使用re模块爬取豆瓣Top250电影
Oct 20 Python
手把手教你实现PyTorch的MNIST数据集
Jun 28 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+jquery实时显示网站在线人数的方法
2015/01/04 PHP
javascript下利用arguments实现string.format函数
2010/08/24 Javascript
jquery插件制作 表单验证实现代码
2012/08/17 Javascript
推荐40款强大的 jQuery 导航插件和教程(上篇)
2012/09/14 Javascript
jquery实现checkbox 全选/全不选的通用写法
2014/02/22 Javascript
运用JQuery的toggle实现网页加载完成自动弹窗
2014/03/18 Javascript
jQuery+PHP打造滑动开关效果
2014/12/16 Javascript
js实现点击链接后窗口缩小并居中的方法
2015/03/02 Javascript
JavaScript实现cookie的写入、读取、删除功能
2015/11/05 Javascript
vue.js指令v-model实现方法
2016/12/05 Javascript
vuejs2.0实现分页组件使用$emit进行事件监听数据传递的方法
2017/02/22 Javascript
基于JS对象创建常用方式及原理分析
2017/06/28 Javascript
js判断数组是否包含某个字符串变量的实例
2017/11/24 Javascript
Vue中的scoped实现原理及穿透方法
2018/05/15 Javascript
vue.extend与vue.component的区别和联系
2018/09/19 Javascript
解决layui的form里的元素进行动态生成,验证失效的问题
2019/09/14 Javascript
koa2的中间件功能及应用示例
2020/03/05 Javascript
Javascript新手入门之字符串拼接与变量的应用
2020/12/03 Javascript
python网络编程示例(客户端与服务端)
2014/04/24 Python
python实现堆和索引堆的代码示例
2018/03/19 Python
对python的输出和输出格式详解
2018/12/08 Python
python3.7 利用函数os pandas利用excel对文件名进行归类
2019/09/29 Python
python通过链接抓取网站详解
2019/11/20 Python
Django 再谈一谈json序列化
2020/03/16 Python
Python基于opencv的简单图像轮廓形状识别(全网最简单最少代码)
2021/01/28 Python
H5 canvas中width、height和style的宽高区别详解
2018/11/02 HTML / CSS
玩具反斗城葡萄牙官方商城:Toys"R"Us葡萄牙
2016/10/21 全球购物
C#面试常见问题
2013/02/25 面试题
关于期中考试的反思
2014/02/02 职场文书
社区交通安全实施方案
2014/03/22 职场文书
护理专业毕业生自我鉴定总结
2014/03/24 职场文书
大学生毕业求职信
2014/06/12 职场文书
给老婆道歉的话
2015/01/20 职场文书
优秀团员主要事迹范文
2015/11/05 职场文书
漫画「古见同学有交流障碍症」第25卷封面公开
2022/03/21 日漫
MYSQL如何查看操作日志详解
2022/05/30 MySQL