侯体宗的博客
  • 首页
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

python制作websocket服务器实例分享

Python  /  管理员 发布于 7年前   214

一、开始的话

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

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

二、代码

  需求:在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-8import osimport structimport base64import hashlibimport socketimport threadingimport paramikodef 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_strdef 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 Truedef 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 Truedef 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    breakdef 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 += 1ws_service()

get_ssh的代码如下:

import paramikodef 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给服务端,所以可以看不同类型,不同机器上的日志,

 页面代码如下:

<!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 描述符(Descriptor)入门
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • 在python语言中Flask框架的学习及简单功能示例(0个评论)
    • 在Python语言中实现GUI全屏倒计时代码示例(0个评论)
    • Python + zipfile库实现zip文件解压自动化脚本示例(0个评论)
    • python爬虫BeautifulSoup快速抓取网站图片(1个评论)
    • vscode 配置 python3开发环境的方法(0个评论)
    • 近期文章
    • 在go语言中实现字符串可逆性压缩及解压缩功能(0个评论)
    • 使用go + gin + jwt + qrcode实现网站生成登录二维码在app中扫码登录功能(0个评论)
    • 在windows10中升级go版本至1.24后LiteIDE的Ctrl+左击无法跳转问题解决方案(0个评论)
    • 智能合约Solidity学习CryptoZombie第四课:僵尸作战系统(0个评论)
    • 智能合约Solidity学习CryptoZombie第三课:组建僵尸军队(高级Solidity理论)(0个评论)
    • 智能合约Solidity学习CryptoZombie第二课:让你的僵尸猎食(0个评论)
    • 智能合约Solidity学习CryptoZombie第一课:生成一只你的僵尸(0个评论)
    • 在go中实现一个常用的先进先出的缓存淘汰算法示例代码(0个评论)
    • 在go+gin中使用"github.com/skip2/go-qrcode"实现url转二维码功能(0个评论)
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-10
    • 2016-11
    • 2018-04
    • 2020-03
    • 2020-04
    • 2020-05
    • 2020-06
    • 2022-01
    • 2023-07
    • 2023-10
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客