python实时更新数据_js+python websocket实时推数据

js与python 通过websocket通信

## user.html

{% extends "monitor.html" %}

{% load staticfiles %}

{% block title %}仿真策略监控{% endblock %}

{% block styles %}

th {

text-align: center;

}

label {

width: 100px;

}

input, select {

width: 160px;

height: 24px;

background: #0F0F0F;

border: 1px solid #3C3C3C;

border-radius: 1px;

}

button {

border: none;

}

thead {

background-color: #444;

}

.bootstrap-table {

border-style: solid;

border-width: 3px;

}

.pagination-info {

display: none

}

.page-list {

display: none

}

#sse {

color: white;

}

#startstg .modal-body {

background-color: #282923;

padding: 0px;

}

{#无数据时鼠标悬浮的背景颜色#}

.table-hover > tbody > tr:hover {

background-color: #444444;

cursor: pointer;

}

{#无数据时的奇数背景颜色#}

{# .table-striped > tbody > tr:nth-of-type(odd) {#}

{# background-color: transparent;#}

{# }#}

#StgFileShow, #ConFileShow {

background-color: #282923;

color: #90918b;

border-width: 0px;

width: 448px;

height: 100%;

}

#stg_check {

padding: 10px;

}

.row {

margin-top: 10px;

}

.left {

width: 60%;

height: 400px;

float: left;

}

.right {

width: 37%;

height: 400px;

float: left;

{#margin-right: 30px;#}

}

.buttom {

width: 97%;

height: 400px;

float: left;

margin-top: 90px;

}

{#tab表头的选中的样式#}

.nav-tabs-custom > .nav-tabs > li.active > a, .nav-tabs-custom > .nav-tabs > li.active:hover > a {

background-color: red;

color: white;

}

{#tab样式#}

.nav-tabs-custom > .nav-tabs > li {

border-top: transparent;

margin-bottom: -2px;

margin-right: 2px;

}

.nav-tabs > li {

float: left;

margin-bottom: -1px;

}

.nav > li > a {

position: relative;

display: block;

padding: 4px 20px;

}

{#tab头的位置及边框颜色透明#}

.nav-tabs-custom > .nav-tabs {

border-bottom-color: rgba(0, 0, 0, 0.2);

border-top-right-radius: 3px;

border-top-left-radius: 3px;

margin-left: 10px;

}

{#tab中的a标签悬浮样式#}

.nav-tabs-custom > .nav-tabs > li > a, .nav-tabs-custom > .nav-tabs > li > a:hover {

background: rgba(255, 255, 255, .15);

margin: 0;

}

.nav-tabs-custom > .nav-tabs > li > a {

color: #d2d6de;

border-radius: 0;

}

.simcheck {

float: left;

margin-top: 15px;

margin-left: 10px

}

.packheadiv {

float: left;

margin-top: 15px;

margin-left: 10px

}

.cancelheadiv {

float: left;

margin-top: 15px;

margin-left: 10px

}

.simcheckhead, .simHead, .packHead {

color: white;

margin-left: 30px;

}

.cancelheadiv, .packheadiv, .traheadiv {

margin-left: 30px;

}

.bootstrap-table .fixed-table-container .table {

width: 100%;

margin-bottom: 0 !important;

color: #939393;

}

.fixed-table-toolbar {

background-color: #141414;

}

.checksearch, .packsearch, .simsearch {

width: 87px;

height: 26px;

background: #3C3C3C;

border-radius: 3px;

}

.cancelorder {

width: 119px;

height: 28px;

background: inherit;

background-color: rgba(255, 204, 153, 1);

box-sizing: border-box;

border-width: 1px;

border-style: solid;

border-color: rgba(121, 121, 121, 1);

border-radius: 5px;

-moz-box-shadow: none;

-webkit-box-shadow: none;

box-shadow: none;

color: #2B2B2B;

margin-left: 15%;

cursor: pointer;

}

tr.activetable, input.activetable {

background-color: white;

width: 114px;

height: 30px;

}

{% endblock %}

{% block content %}

仿真策略监控

{{ allHtml.title }}

用户名:

密      码:

合约:

用户登录查看到的数据

{% endblock %}

{% block scripts %}

//页面加载后就调用

{#import lo from "../../../commonStatic/bower_components/moment/src/locale/lo";#}

{#$(function () {#}

{# alert("qew")#}

{# })#}

var timer, clickFlag, stgtype = false;//外部变量,这三个变量是定时器是否存在的标志

//初始化策略列表

$("#RuleTable").bootstrapTable('destroy').bootstrapTable({

uniqueId: "RuleID",

// 策略列表table

columns: [{

field: 'RuleID',

title: '策略ID'

}, {

field: 'RuleName',

title: '策略名称'

}, {

field: 'LServer',

title: '指定服务器',

{#formatter: function (value, row, index) {#}

{# $.post("{% url 'trade:serverfind' %}", {"ruleid": row.RuleID}, function (r) {#}

{# if (r){#}

{# console.log("123adce", r)#}

{# return r#}

{# }#}

{# })#}

{# }#}

}, {

field: 'RuleRunStatus',

title: '运行状态',

formatter: function (value, row, index) {

if (value === "0") {

return "停止"

} else if (value === "1") {

return "启动"

} else {

return value

}

}

}, {

field: 'LOption',

title: '操作',

formatter: function (value, row, index) {

if (row.RuleRunStatus === "1") {

var start = " 启动 ";

var end = "&nbsp 停止";

} else if (row.RuleRunStatus === "0") {

var start = " 启动 ";

var end = "&nbsp 停止";

}

return start + end + "&nbsp 人工录入";

}

},

{

field: 'LRisk',

title: '风控',

}],

onClickRow: function (row, $element, field) {

{#console.log(rules)#}

//当前行的父元素tbody下的所有tr移除样式类

$element.parent().children().removeClass("onclickrow")

$element.addClass("onclickrow")

var ruleid = row.RuleID

//点击一行数据,初始化策略参数和策略指标

RulePropIndic(ruleid)

//刚开始需要把上次的定时器取消掉,然后再进行定时任务。

if (clickFlag) {

clearInterval(timer)

}

clickFlag = true;

timer = setInterval(function () {

//先移除所有的数据,然后再append

$('#StgparaTable').bootstrapTable('removeAll');

$('#StgdicTable').bootstrapTable('removeAll');

for (let key in ruleProps) {

//策略参数

if (key.startsWith(ruleid + "prop")) {

$('#StgparaTable').bootstrapTable('append', ruleProps[key]);

}

//策略指标

else if (key.startsWith(ruleid + "indic")) {

$('#StgdicTable').bootstrapTable('append', ruleProps[key]);

}

}

var clickFlag = false

}, 2000)

//关闭定时器

{#clearInterval(timer)#}

}

})

//初始化策略参数

$("#StgparaTable").bootstrapTable('destroy').bootstrapTable({

{#uniqueId: "RuleID" + "prop_" + "PropKey",#}

// 策略列表table

columns: [{

field: 'RuleID',

title: '策略ID',

visible: false

}, {

field: 'PropKey',

title: '参数名'

}, {

field: 'PropValue',

title: '参数值'

}, {

field: 'Description',

title: '描述',

}, {

field: 'POption',

title: '操作',

formatter: function (value, row, index) {

return "

","+ "'" +row.PropValue+"'"+"," + "'" + row.Description+"')\" class='btn btn-success btn-xs btn-flat btn_operation' data-toggle='modal' data-target='#startstg'> 编辑

"

}

}]

})

//初始化策略指标

$("#StgdicTable").bootstrapTable('destroy').bootstrapTable({

uniqueId: "RuleID",

// 策略列表table

columns: [{

field: 'RuleID',

title: '策略ID',

visible: false

}, {

field: 'IndicatorKey',

title: '指标名'

}, {

field: 'IndicatorValue',

title: '指标值'

}, {

field: 'Description',

title: '指标描述',

}]

})

//初始化做市策略

$("#StgMMTable").bootstrapTable('destroy').bootstrapTable({

uniqueId: "RuleID",

// 做市策略table

columns: [

//第一行表头

[{

field: 'RuleID',

title: '',

{#visible: false#}

}, {

field: 'mm',

title: '做市券盘口',

colspan: 2

}, {

field: 'hedge',

title: '对冲券盘口',

colspan: 2

}, {

field: '',

title: '',

colspan: 2

}, {

field: "my",

title: "我方报价",

colspan: 2

}, {

field: '',

title: '',

colspan: 2

}],

//第二行表头

[{

field: "RuleName",

title: "策略名称"

}, {

field: "bid_mm",

title: "Bid"

}, {

field: "ofr_mm",

title: "Ofr"

}, {

field: "bid_hedge",

title: "Bid"

}, {

field: "ofr_hedge",

title: "Ofr"

}, {

field: "spread",

title: "合理利差"

}, {

field: "offset",

title: "偏移"

}, {

field: "bid_my",

title: "Bid"

}, {

field: "ofr_my",

title: "Ofr"

}, {

field: "position",

title: "持仓"

}, {

field: "is_broken",

title: "断腿警告"

}]

]

})

//初始化cta策略

$("#StgCtaTable").bootstrapTable('destroy').bootstrapTable({

uniqueId: "RuleID",

// CTA策略table

columns: [{

field: "RuleName",

title: "策略名称"

}, {

field: "indicator1",

title: "指标1"

}, {

field: "indicator2",

title: "指标2"

}, {

field: "trade_time",

title: "最新成交时间"

}, {

field: "yield",

title: "收益率"

}, {

field: "high",

title: "高"

}, {

field: "low",

title: "低"

}, {

field: "decision",

title: "决策点"

}, {

field: "trigger",

title: "触发点"

},

{

field: "direction",

title: "方向"

},

{

field: "position",

title: "持仓"

},

{

field: "volume_left",

title: "待执行量"

},

{

field: "ytm_signal",

title: "信号价格"

},

{

field: "ytm_limit",

title: "限价"

}]

})

//rules用于存储 策略列表和不同类型的策略, ruleprops用于存储策略参数和策略指标

var rules = {}, ruleProps = {};

var idsarry = []; //windows-server, 47.102.219.52:9000

var ws = $.trade("ws://127.0.0.1:9000/", {//

OnFrontConnected: function () {

$("#console").append("
TD连接成功");

},

OnFrontDisconnected: function () {

$("#console").append("
TD已断开");

},

OnRspUserLogin: function (r) {

//{TID: data:{},RspInfoField:{ErrorID:“”,ErrorMsg:“”}

if (r.RspInfoField.ErrorID == 0) {

$("#console").append("
" + r.data.CustomerID + " 交易登录成功");

user.CustomerID = r.data.CustomerID;

} else

$("#console").append("
交易登录失败:" + r.RspInfoField.ErrorMsg);

},

OnRspError: function (r) {

$("#console").append("
" + r.RspInfoField.ErrorID + ":" + r.RspInfoField.ErrorMsg);

},

OnRtnRule: function (r) {

if (rules[r.data.RuleID] == undefined) {

if (r.data.RuleRunStatus == undefined) {

r.data.RuleRunStatus = 0;

}

$('#RuleTable').bootstrapTable('append', r.data);

} else {

$('#RuleTable').bootstrapTable('updateRow', {

index: r.data.RuleID,

row: r.data

});

}

rules[r.data.RuleID] = r.data;

rules[r.data.RuleID].RuleRunStatus = 0;

{#ruleType[r.data.RuleID] = r.data#}

$("#console").append("
策略信息: " + r.data.RuleID + ":" + r.data.RuleName);

},

OnRtnRuleStatus: function (r) {

rules[r.data.RuleID].RuleRunStatus = r.data.RuleRunStatus;

if (rules[r.data.RuleID].RuleRunStatus == undefined) {

$('#RuleTable').bootstrapTable('append', r.data);

} else {

$('#RuleTable').bootstrapTable('updateRow', {

index: r.data.RuleID,

row: r.data

});

}

$("#console").append("
策略状态 " + r.data.RuleID + ":" + r.data.RuleRunStatus);

},

OnRtnRuleProp: function (r) {

if (rules[r.data.RuleID] !== undefined) {

rules[r.data.RuleID][r.data.PropKey] = r.data.PropValue

}

ruleProps[r.data.RuleID + "prop" + "_" + r.data.PropKey] = r.data

$("#console").append("
策略参数:" + r.data.RuleID + ":" + r.data.PropKey + ":" + r.data.PropValue + ":" + r.data.Description);

},

OnRtnRuleIndicator: function (r) {

if (rules[r.data.RuleID] !== undefined) {

rules[r.data.RuleID][r.data.IndicatorKey] = r.data.IndicatorValue

}

ruleProps[r.data.RuleID + "indic" + "_" + r.data.IndicatorKey] = r.data;

$("#console").append("
策略指标:" + r.data.RuleID + ":" + r.data.IndicatorKey + ":" + r.data.IndicatorValue + ":" + r.data.Description);

},

events: {

say: function (e) {

alert(e.data.name); // 'foo'

alert(e.data.text); // 'baa'

}

}

});

function sendMsg_login() {

var dt = {

"Tid": 1111, "data": {

'CustomerID': '1001',

'Password': '123456',

'MacAddress': '',

'SubInstrumentMethod': '',

'IsRule': ''

}

};

ws.ReqUserLogin("1001", "123456");

}

function submitit() {

$('#rf').submit();

}

function tabs(n) {

if (n == 1) {

$('#fa-stgpara').addClass('active');

$('#fa-stgindic').removeClass('active');

} else {

$('#fa-stgpara').removeClass('active');

$('#fa-stgindic').addClass('active');

}

}

//点击做事类策略或者CTA类策略

function btabs(n) {

initypestg()

if (n == 1) {

$('#fa-stgmm').addClass('active');

$('#fa-stgcta').removeClass('active');

clearInterval(stgtype)

stgtype = setInterval(function () {

//先移除所有的数据,然后再append

$('#StgMMTable').bootstrapTable('removeAll');

$('#StgCtaTable').bootstrapTable('removeAll');

//bootstraptable渲染不同类型的策略

for (let key in rules) {

if (rules[key].hasOwnProperty("bid_mm")) {

$('#StgMMTable').bootstrapTable('append', rules[key]);

} else if (rules[key].hasOwnProperty("trigger")) {

$('#StgCtaTable').bootstrapTable('append', rules[key]);

}

}

}, 2000)

} else {

$('#fa-stgmm').removeClass('active');

$('#fa-stgcta').addClass('active');

clearInterval(stgtype)

stgtype = setInterval(function () {

//先移除所有的数据,然后再append

$('#StgMMTable').bootstrapTable('removeAll');

$('#StgCtaTable').bootstrapTable('removeAll');

//bootstraptable渲染不同类型的策略

for (let key in rules) {

if (rules[key].hasOwnProperty("bid_mm")) {

$('#StgMMTable').bootstrapTable('append', rules[key]);

} else if (rules[key].hasOwnProperty("trigger")) {

$('#StgCtaTable').bootstrapTable('append', rules[key]);

}

}

}, 2000)

}

}

//做事类策略或者cTa类策略的初始化

function initypestg() {

//先移除所有的数据,然后再append

$('#StgMMTable').bootstrapTable('removeAll');

$('#StgCtaTable').bootstrapTable('removeAll');

//bootstraptable渲染做事类策略

for (let key in rules) {

if (rules[key].hasOwnProperty("bid_mm")) {

$('#StgMMTable').bootstrapTable('append', rules[key]);

} else if (rules[key].hasOwnProperty("trigger")) {

$('#StgCtaTable').bootstrapTable('append', rules[key]);

}

}

}

function status(th) {

var StgID = $(th).attr('id');

var S = $(th).attr('status');

Status = S.split('-')[0];

EnvID = S.split('-')[1];

$.post("{{url}}", {

'StgID': JSON.stringify([StgID]),

'Status': JSON.stringify([Status]),

'EnvID': JSON.stringify([EnvID])

}, function (r) {

if (r) {

window.location.reload();

}

})

}

function RefreshIt() {

var opt = {

url: "{{url}}",

silent: true,

query: {

env: $('#env').val()

}

};

$("#RuleTable").bootstrapTable('refresh', opt);

}

function pack(sid) {

$.post('{{url}}', {'sid': sid}, function (r) {

$("#packTable").bootstrapTable('refresh');

})

}

//策略参数和策略指标的定时任务

function RulePropIndic(ruleid) {

//先移除所有的数据,然后再append

$('#StgparaTable').bootstrapTable('removeAll');

$('#StgdicTable').bootstrapTable('removeAll');

for (let key in ruleProps) {

//策略参数

if (key.startsWith(ruleid + "prop")) {

$('#StgparaTable').bootstrapTable('append', ruleProps[key]);

}

//策略指标

else if (key.startsWith(ruleid + "indic")) {

$('#StgdicTable').bootstrapTable('append', ruleProps[key]);

}

}

}

function new_col(idn) {

var h = "

" + '' +

'

' οnclick="$(this).parent().parent().remove()">删除行' +

'

' +

'

' +

"

" +

"" +

"

" +

"

" +

"

";

$('#' + idn).append($(h));

};

//上传列表模态框展示

function uploadmodal() {

$("#upmodal").modal("show")

}

//启动策略

function startstg(id) {

ws.ReqOptionStg("start", id);

}

//停止策略

function endstg(id) {

ws.ReqOptionStg("end", id);

}

//编辑策略参数

function modify(id, key, value, desc) {

ws.ReqStgPara(id, key, value, desc)

}

{#加载数据时的样式#}

.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading {

align-items: center;

background: #141414;

display: none;

justify-content: center;

position: absolute;

bottom: 0;

width: 100%;

z-index: 1000;

}

{#bootstraptable表格布局样式#}

.bootstrap-table .fixed-table-container .table {

width: 90%;

margin-bottom: 0 !important;

margin: 0 auto;

border: solid #444 !important

}

{#bootstraptable上边框#}

.table-bordered {

border: 1px solid transparent !important;

}

{#bootstraptable左侧边框#}

.table-bordered > thead > tr > th, .table-bordered > tbody > tr > th, .table-bordered > tfoot > tr > th, .table-bordered > thead > tr > td, .table-bordered > tbody > tr > td, .table-bordered > tfoot > tr > td {

border: 1px solid rgba(255, 255, 255, .15);

}

{#每一个table的样式#}

.bootstrap-table .fixed-table-container .table thead th .th-inner {

padding: .75rem;

vertical-align: bottom;

overflow: hidden;

text-overflow: ellipsis;

white-space: nowrap;

border: 1px solid #444 !important;

}

.tab-content {

height: 380px;

}

.stginfo {

height: 400px;

}

.onclickrow {

background-color: #444444;

}

{% endblock %}

// websocket.js

/*

* jQuery Web Sockets Plugin v0.0.4

* https://github.com/dchelimsky/jquery-websocket

* http://code.google.com/p/jquery-websocket/

*

* This document is licensed as free software under the terms of the

* MIT License: http://www.opensource.org/licenses/mit-license.php

*

* Copyright (c) 2010 by shootaroo (Shotaro Tsubouchi).

*/

const TSS_DIALOG = 1;//对话流

const TSS_PRIVATE = 2;//会员私有流

const TSS_PUBLIC = 3;//公共流

const TSS_QUERY = 4;//查询

const TSS_USER = 5;//交易员私有流

//用户启动策略请求

const FTD_TID_ReqOptionStg = 0x00002101;

//编辑策略参数请求

const FTD_TID_ReqStgPara = 0x00002102

//用户登录请求

const FTD_TID_ReqUserLogin = 0x00001001;

//用户登录响应

const FTD_TID_RspUserLogin = 0x00001002;

//用户退出请求

const FTD_TID_ReqUserLogout = 0x00001005;

//用户退出响应

const FTD_TID_RspUserLogout = 0x00001006;

//报单录入请求

const FTD_TID_ReqOrderInsert = 0x00003001;

//报单录入错误时响应

const FTD_TID_RspOrderInsert = 0x00003002;

//报单修改/撤单请求

const FTD_TID_ReqOrderAction = 0x00003003;

//报单修改/撤单错误时响应

const FTD_TID_RspOrderAction = 0x00003004;

//请求查询合约

const FTD_TID_ReqQryInstrument = 0x00002001;

//请求查询合约响应

const FTD_TID_RspQryInstrument = 0x00002002;

//请求查询债券

const FTD_TID_ReqQryBond = 0x00001109;

//请求查询债券响应

const FTD_TID_RspQryBond = 0x0000110A;

//查询债券期货和可交割券关系表

const FTD_TID_ReqQryBondFutureDeliverable = 0x00001105;

//债券期货和可交割券关系表回报

const FTD_TID_RspQryBondFutureDeliverable = 0x00001106;

//请求查询物理账号资金

const FTD_TID_ReqQryAccount = 0x00002005;

//请求查询物理账号资金响应

const FTD_TID_RspQryAccount = 0x00002006;

//请求查询物理账号持仓

const FTD_TID_ReqQryPosition = 0x00002007;

//请求查询物理账号持仓响应

const FTD_TID_RspQryPosition = 0x00002008;

//请求订阅行情

const FTD_TID_ReqSubMarketData = 0x00002009;

//请求退订行情

const FTD_TID_ReqUnSubMarketData = 0x0000200b;

//请求订阅行情BAR

const FTD_TID_ReqSubMarketDataBar = 0x0000200d;

//请求退订行情BAR

const FTD_TID_ReqUnSubMarketDataBar = 0x0000200e;

//请求订阅新本币平台行情

const FTD_TID_ReqSubCFETSMarketData = 0x00001130;

//请求退订新本币平台行情

const FTD_TID_ReqUnsubCFETSMarketData = 0x00001132;

//查询债券成交信息

const FTD_TID_ReqQryBondExecReport = 0x0000110D;

//查询债券成交信息响应

const FTD_TID_RspQryBondExecReport = 0x0000110E;

//请求从行情中心订阅行情

const FTD_TID_ReqSubMDfromMC = 0x00001140;

//请求从行情中心退订行情

const FTD_TID_ReqUnSubMDfromMC = 0x00001142;

//请求订阅策略对应RFQ报价

const FTD_TID_ReqSubRFQByClientID = 0x00001150;

//请求退订策略对应RFQ报价

const FTD_TID_ReqUnSubRFQByClientID = 0x00001152;

//请求增加策略

const FTD_TID_ReqInsertRule = 0x00001010;

//请求增加策略响应

const FTD_TID_RspInsertRule = 0x00001011;

//请求修改策略状态

const FTD_TID_ReqUpdateRuleStatus = 0x00001030;

//请求修改策略状态响应

const FTD_TID_RspUpdateRuleStatus = 0x00001031;

//请求更新策略属性

const FTD_TID_ReqUpdateRuleProp = 0x00001040;

//请求更新策略属性响应

const FTD_TID_RspUpdateRuleProp = 0x00001041;

//策略回报

const FTD_TID_RtnRule = 0x00001012;

//策略运行状态变化回报

const FTD_TID_RtnRuleStatus = 0x00001032;

//策略属性变化回报

const FTD_TID_RtnRuleProp = 0x00001044;

//策略指标回报

const FTD_TID_RtnRuleIndicator = 0x00001045;

//深度行情回报

const FTD_TID_RtnDepthMarketData = 0x00006001;

//行情Bar回报

const FTD_TID_RtnMarketDataBar = 0x00006002;

//报价行情回报

const FTD_TID_RtnQuoteMarket = 0x00001103;

//债券市场成交回报

const FTD_TID_RtnBondExecReport = 0x00001110;

//新本币平台深度行情回报

const FTD_TID_RtnCFETSDepthMarketData = 0x00001133;

//报价接收回报

const FTD_TID_RtnCfetsRFQReceive = 0x00001134;

//CME/CBOT深度行情回报

const FTD_TID_RtnCMEDepthMarketData = 0x00001144;

//报单回报

const FTD_TID_RtnRuleOrder = 0x00001102;

//成交回报

const FTD_TID_RtnRuleTrade = 0x00001111;

//持仓变动回报

const FTD_TID_RtnRulePosition = 0x00001100;

//资金变动回报

const FTD_TID_RtnRuleAccount = 0x00001101;

//Api建立连接回报

const FTD_TID_RtnApiConnected = 0x0000110B;

//Api断开连接回报

const FTD_TID_RtnApiDisconnected = 0x0000110C;

//客户端显示消息回报

const FTD_TID_RtnUserMessage = 0x0000110F;

//用户分账号信息回报

const FTD_TID_RtnCustomerAccountInfo = 0x00001114;

//交易成员基本信息回报

const FTD_TID_RtnCfetsTradeMember = 0x00001135;

//心跳通知

const FTD_TID_NotifyHeartBeat = 0x00004500;

//请求更新合约做市状态

const FTD_TID_ReqUpdMakeMarketStatus = 0x00001115;

//请求更新合约做市状态响应

const FTD_TID_RspUpdMakeMarketStatus = 0x00001116;

//合约做市状态回报

const FTD_TID_RtnMakeMarketStatus = 0x00001117;

//请求更新合约做市参数

const FTD_TID_ReqUpdMakeMarketPara = 0x00001118;

//请求更新合约做市参数响应

const FTD_TID_RspUpdMakeMarketPara = 0x00001119;

//合约做市参数回报

const FTD_TID_RtnMakeMarketPara = 0x0000111A;

//请求查询策略限仓额度

const FTD_TID_ReqQryRulePositionLimit = 0x00001120;

//请求查询策略限仓额度响应

const FTD_TID_RspQryRulePositionLimit = 0x00001121;

//请求更改策略限仓额度

const FTD_TID_ReqRulePositionLimitAction = 0x00001122;

//请求更改策略限仓额度响应

const FTD_TID_RspRulePositionLimitAction = 0x00001123;

//请求订阅价差

const FTD_TID_ReqSubPriceSpread = 0x0000112a;

//请求退订价差

const FTD_TID_ReqUnsubPriceSpread = 0x0000112c;

//合约价差回报

const FTD_TID_RtnInsPriceSpread = 0x0000112b;

(function ($) {

$.extend({

websocket: function (url, s, protocols) {

var ws, _s = s;

var settings = {

message: function () {

console.log("message function is undefined");

},

options: {}, events: {}, ver: "1.0", company: "上海尔易信息科技有限公司"

};

_reconnect = function (url, protocols) {

if (protocols) {

ws = window['MozWebSocket'] ? new MozWebSocket(url, protocols) : window['WebSocket'] ? new WebSocket(url, protocols) : null;

} else {

ws = window['MozWebSocket'] ? new MozWebSocket(url) : window['WebSocket'] ? new WebSocket(url) : null;

}

ws._url = url;

ws._protocols = protocols;

$.extend(settings, $.websocketSettings, _s);

ws.nRequestID = 1;

if (ws) {

$(ws)

.bind('open', settings.OnFrontConnected)

.bind('close', settings.OnFrontDisconnected)

.bind('message', settings.message)

.bind('message', function (e) {

var m = JSON.stringify(e.originalEvent.data);

//eval("m=" + e.originalEvent.data);

var h = settings.events[m.Tid];//调用指定的方法

if (h) h.call(this, m);

});

ws._send = ws.send;

ws.send = function (type, data) {

var m = {Tid: type};//类型

m = $.extend(true, m, $.extend(true, {}, settings.options, m));

if (data) {

delete data["_id"];

m['data'] = data;

}

m['RequestID'] = ws.nRequestID++;

return this._send(JSON.stringify(m));

};

ws.reconnect = function () {

setTimeout(function () {

_reconnect(ws._url, ws._protocols);

}, 1000 * 30);

//_reconnect(ws._url, ws._protocols);

};

}

};

_reconnect(url, protocols);

// $(window).unload(function () {

// ws.close();

// ws = null;

// });

$(window).on("unload",function () {

ws.close();

ws = null;

});

return ws;

}

, trade: function (url, s, protocols) {

var tradeWS = {nRequestID: 1};

var settings = {

OnFrontConnected: function () {

console.log("OnFrontConnected");

},

OnFrontDisconnected: function () {

console.log("OnFrontDisconnected");

},

OnHeartBeatWarning: function (nTimeLapse) {

console.log("OnHeartBeatWarning: " + nTimeLapse);

},

OnRspError: function (r) {

},

OnRspUserLogin: function (r) {

},

OnRspUserLogout: function (r) {

},

OnRtnDepthMarketData: function (r) {

},

OnRspSubMarketData: function (r) {

},

OnRspUnSubMarketData: function (r) {

},

OnRtnRule: function (r) {

},

OnRtnRuleStatus: function (r) {

},

OnRtnRuleProp: function (r) {

},

OnRtnRuleIndicator: function (r) {

},

message: function (msg) {

try {

if (event.data instanceof Blob) {

var reader = new FileReader();

reader.onloadend = function () {

console.log(reader.result);

};

reader.readAsText(event.data, "gbk");//utf-8

} else {

eval("r=" + event.data);

switch (r.Tid) {

case FTD_TID_RtnDepthMarketData: {

tradeWS.OnRtnDepthMarketData(r);

break;

}

case FTD_TID_RspUserLogin: {

tradeWS.OnRspUserLogin(r);

break;

}

// case FTD_TID_RspError: {

// tradeWS.OnRspError(r);

// break;

// }

case FTD_TID_RtnRule: {//策略回报

tradeWS.OnRtnRule(r);

break;

}

case FTD_TID_RtnRuleStatus: {//策略运行状态变化回报

tradeWS.OnRtnRuleStatus(r);

break;

}

case FTD_TID_RtnRuleProp: {//策略属性变化回报

tradeWS.OnRtnRuleProp(r);

break;

}

case FTD_TID_RtnRuleIndicator: {//策略指标回报

tradeWS.OnRtnRuleIndicator(r);

break;

}

case FTD_TID_RspUserLogout: {

tradeWS.OnRspUserLogout(r);

break;

}

case FTD_TID_RspSubMarketData: {

tradeWS.OnRspSubMarketData(r);

break;

}

case FTD_TID_RspUnSubMarketData: {

tradeWS.OnRspUnSubMarketData(r);

break;

}

case FTD_TID_IntlRtnDepthMarketData: {

tradeWS.OnRspError(r);

break;

}

default:

OnHeartBeatWarning(r);

}

console.log("log: " + event.data);

}

} catch (e) {

console.log(e.message + " : " + event.data);

}

},

options: {},

events: {}

};

$.extend(settings, $.websocketSettings, s, tradeWS);

tradeWS.ws = $.websocket(url, settings, protocols);

tradeWS.OnFrontConnected = settings.OnFrontConnected;

tradeWS.OnFrontDisconnected = settings.OnFrontDisconnected;

tradeWS.OnHeartBeatWarning = settings.OnHeartBeatWarning;

tradeWS.OnRspError = settings.OnRspError;

tradeWS.OnRspUserLogin = settings.OnRspUserLogin;

tradeWS.OnRspUserLogout = settings.OnRspUserLogout;

tradeWS.OnRtnDepthMarketData = settings.OnRtnDepthMarketData;

tradeWS.OnRspSubMarketData = settings.OnRspSubMarketData;

tradeWS.OnRspUnSubMarketData = settings.OnRspUnSubMarketData;

tradeWS.OnRtnRule = settings.OnRtnRule;

tradeWS.OnRtnRuleStatus = settings.OnRtnRuleStatus;

tradeWS.OnRtnRuleProp = settings.OnRtnRuleProp;

tradeWS.OnRtnRuleIndicator = settings.OnRtnRuleIndicator;

//启动或停止策略发送到后台

tradeWS.ReqOptionStg = function(optype, ruleid){

this.ws.send(FTD_TID_ReqOptionStg, {

"optype": optype,

"stgid": ruleid

})

};

//策略参数发送到websocket

tradeWS.ReqStgPara = function(ruleid, key, value, desc){

this.ws.send(FTD_TID_ReqStgPara, {

"stgid": ruleid,

"stgname": key,

"stgvalue": value,

"stgdesc": encodeURI(desc) //中文乱码,用url编码,python在后台url解码

})

}

tradeWS.ReqUserLogin = function (u, p) {

this.ws.send(FTD_TID_ReqUserLogin, {

'CustomerID': u,

'Password': p,

'ProductInfo': '',

'InterfaceProductInfo': 'websocket'

});

};

tradeWS.SubscribeMarketData = function (ppInstrumentIDs) {

if (ppInstrumentIDs.length > 0)

this.ws.send(FTD_TID_ReqSubMarketData, {'InstrumentID': ppInstrumentIDs, "ExchangeID": "CFFEX"});

else

alert("请选择要订阅行情的合约!");

};

tradeWS.UnSubscribeMarketData = function (ppInstrumentIDs) {

if (ppInstrumentIDs.length > 0)

this.ws.send(FTD_TID_ReqUnSubMarketData, {'InstrumentIDs': ppInstrumentIDs});

else

alert("请选择要取消订阅行情的合约!");

};

return tradeWS;

}

});

})(jQuery);

## 后端代码(STCHftData.py)

# encoding: UTF-8

from time import sleep

from hft_api import HftApi

import os

import json

import threading

import copy

import multiprocessing

from websocket_server import WebsocketServer

global signal

from enum import Enum

from process_manager import process_manager

import pandas as pd

from sqlalchemy import create_engine

import urllib.parse

class run_status_enum(Enum):

运行中 = 1

已结束 = 2 # 回测模拟

已分析 = 3 # 回测模拟

未运行 = 4

待运行 = 5

待停止 = 6

错误 = 7 # 回测模拟

# signal = threading.Event()

def print_dict(d):

"""按照键值打印一个字典"""

for key, value in d.items():

print(key + ':' + str(value))

def simple_log(func):

"""简单装饰器用于输出函数名"""

def wrapper(*args, **kw):

print(str(func.__name__))

return func(*args, **kw)

return wrapper

class TestMdApi(HftApi):

"""测试用实例"""

def __init__(self, Queue1):

"""Constructor"""

super(TestMdApi, self).__init__()

# 1. 创建1个队列

self.tid_Queue = Queue1

@simple_log

def onFrontConnected(self):

"""服务器连接"""

signal = 1

pass

@simple_log

def onFrontDisconnected(self, n):

"""服务器断开"""

print(n)

signal = 0

@simple_log

def onHeartBeatWarning(self, n):

"""心跳报警"""

print(n)

@simple_log

def onRspError(self, error, n, last):

"""错误"""

print_dict(error)

@simple_log

def onRspUserLogin(self, data, error, data2, n, last):

"""登陆回报"""

print_dict(data)

print_dict(error)

@simple_log

def onRspUserLogout(self, data, error, n, last):

"""登出回报"""

print_dict(data)

print_dict(error)

def onRspUnSubMarketData(self, data, error, n, last):

"""退订合约回报"""

print_dict(data)

print_dict(error)

def onRtnDepthMarketData(self, data):

"""行情推送"""

print_dict(data)

# print('子进程1进程>>', os.getpid())

package = {'Tid': 0x00002008, 'data': data}

self.save_as_json(package, "onRtnDepthMarketData")

# ----------------------------------------------------------------------

def onRtnForQuoteRsp(self, data):

"""行情推送"""

print_dict(data)

def onRspUpdateRuleStatus(self, data, error, n, last):

"""更改C++ 策略更新状态响应"""

# print_dict(error)

package = {'Tid': 0x00001031, 'data': data}

self.save_as_json(package)

def onRspUpdateRuleProp(self, data, error, n, last):

# print_dict(error)

"""更改C++ 策略更新属性响应"""

package = {'Tid': 0x00001041, 'data': data}

self.save_as_json(package, "onRspUpdateRuleProp")

def onRtnRuleIndicator(self, data):

"""策略指标回报"""

package = {'Tid': 0x00001045, 'data': data}

self.save_as_json(package, "onRtnRuleIndicator")

# save ruleid+key

def onRtnRuleProp(self, data):

"""策略属性变化回报"""

package = {'Tid': 0x00001044, 'data': data}

self.save_as_json(package, "onRtnRuleProp")

# save

def onRtnRuleStatus(self, data):

"""策略状态变化回报"""

package = {'Tid': 0x00001032, 'data': data}

self.save_as_json(package)

@simple_log

def onRtnRule(self, data):

"""策略回报"""

package = {'Tid': 0x00001012, 'data': data}

self.save_as_json(package, "onRtnRule")

"""

主动API接口封装

"""

# 在C++环境中创建MdApi对象,传入参数是希望用来保存.con文件的地址

# @param pszFlowPath 存贮订阅信息文件的目录,默认为当前目录

# @return 创建出的UserApi

# modify for udp marketdata

def CreateFtdcMdApi(self, path):

if os.path.exists(path) and path[-1] == '\\':

type = ''

self.createUserApi(path, type)

else:

print('con file path error')

# 删除接口对象本身

# @remark 不再使用本接口对象时,调用该函数删除接口对象

def Release(self):

self.release()

# 初始化

# @remark 初始化运行环境,只有调用后,接口才开始工作

def Init(self):

self.init()

# 等待接口线程结束运行

# @return 线程退出代码

def Join(self):

self.join()

# 退出

def Exit(self):

self.exit()

# 获取当前交易日

# @retrun 获取到的交易日

# @remark 只有登录成功后,才能得到正确的交易日

def GetTradingDay(self):

trade_date = self.getTradingDay()

return trade_date

# 注册前置机网络地址

# @param pszFrontAddress:前置机网络地址。

# @remark 网络地址的格式为:“protocol://ipaddress:port”,如:”tcp://127.0.0.1:17001”。

# @remark “tcp”代表传输协议,“127.0.0.1”代表服务器地址。”17001”代表服务器端口号。

def RegisterFront(self, ip_address):

self.registerFront(ip_address)

# 用户登录请求

def ReqUserLogin(self):

# signal.wait()

# 登陆

print("ready to login")

loginReq = {} # 创建一个空字典

loginReq['CustomerID'] = '1001' # 参数作为字典键值的方式传入

loginReq['Password'] = '123456' # 键名和C++中的结构体成员名对应

loginReq['MacAddress'] = '00-50-56-C0-00-01'

loginReq['SubInstrumentMethod'] = '0'

loginReq['IsRule'] = 0

self.reqUserLogin(loginReq, 1)

# 登出请求

def ReqUserLogout(self):

self.reqUserLogout()

# 订阅行情。

# @param ppInstrumentID 合约ID

# @param nCount 要订阅/退订行情的合约个数

def SubscribeMarketData(self, msg):

subReq = {}

subReq['InstrumentID'] = msg['data']['InstrumentID']

subReq['ExchangeID'] = msg['data']['ExchangeID']

self.reqSubMarketData(subReq, 2)

# self.subscribeMarketData(ppInstrumentID)

# 退订行情。

# @param ppInstrumentID 合约ID

# @param nCount 要订阅/退订行情的合约个数

def UnSubscribeMarketData(self, ppInstrumentID):

self.unSubscribeMarketData(ppInstrumentID)

# 请求修改策略状态

def ReqUpdateRuleStatus(self, req):

self.reqUpdateRuleStatus(req, 3)

# 请求修改策略状态

def ReqUpdateRuleProp(self, req):

self.reqUpdateRuleProp(req, 4)

# 业务流程处理

def subs(self):

self.CreateFtdcMdApi('mdcon\\')

self.RegisterFront("tcp://127.0.0.1:32201")

self.Init()

sleep(1)

self.ReqUserLogin()

sleep(1)

# 请求修改策略状态_启动策略

ruleReq = {} # 创建一个空字典

ruleReq['RuleID'] = 1 # 策略代码

ruleReq['RuleOperType'] = '0' # 策略运行状态类型设定

self.ReqUpdateRuleStatus(ruleReq)

# 请求更新策略属性

# for i in range(0,1000):

ruleReq2 = {} # 创建一个空字典

ruleReq2['RuleID'] = 1 # 策略代码

ruleReq2['PropKey'] = 'OrderPrice' # 策略属性键

ruleReq2['PropValue'] = '99.5' # 策略属性值

ruleReq2['PropType'] = '1' # 策略属性值类型

ruleReq2['PropValueItems'] = '1' # 策略属性值选项

ruleReq2['Description'] = '报单价格'.encode('gbk') # 描述

self.ReqUpdateRuleProp(ruleReq2)

# # 请求修改策略状态_关闭策略

# ruleReq['RuleID'] = 1 # 策略代码

# ruleReq['RuleOperType'] = '1' # 策略运行状态类型设定

# self.ReqUpdateRuleStatus(ruleReq)

# 数据转换

def save_as_json(self, package, func=""):

json_package = json.dumps(package)

# self.tid_Queue.put(json_package)

self.tid_Queue.put(package)

# if self.tid_Queue.qsize() % 10 == 0:

# print('打包 现在队列里面有包数:', self.tid_Queue.qsize())

#####################################

class switch(object):

def __init__(self, value):

self.value = value

self.fall = False

def __iter__(self):

"""Return the match method once, then stop"""

yield self.match

raise StopIteration

def match(self, *args):

"""Indicate whether or not to enter a case suite"""

if self.fall or not args:

return True

elif self.value in args: # changed for v1.5, see below

self.fall = True

return True

else:

return False

# 自定义常量类,类名要大写,不可修改。python中只规定常量大写,建议不可修改,若想不可修改,自定义类。

# 策略回报

FTD_Tid_RtnRule = 0x00001012;

# 策略运行状态变化回报

FTD_Tid_RtnRuleStatus = 0x00001032;

# 策略属性变化回报

FTD_Tid_RtnRuleProp = 0x00001044;

# 策略指标回报

FTD_Tid_RtnRuleIndicator = 0x00001045;

global client_list

client_list = []

global total_dict

class SocketEnv():

def __init__(self, queue1):

"""Constructor"""

super(SocketEnv, self).__init__()

self.total_dict = {} # 全量数据

# tid_Queue 增量数据队列

self.tid_Queue = queue1

r = 'r是什么,r是传递的data,case是r.Tid'

v = 'ten' # 若按照此写法,v=r.Tid

def caseswitch(self, req_msg):

for case in switch(req_msg["Tid"]):

print('前端请求HFT数据:', type(req_msg), req_msg)

# 带on的是回报,不带on的是请求,关注请求即可

if case(FTD_Tid_RtnRule):

# self.api.onRtnDepthMarketData(req_msg)

break

if case(FTD_Tid_RtnRuleStatus):

# self.api.onRspUserLogin(req_msg)

break

if case(FTD_Tid_RtnRuleProp):

# self.api.OnRspError(req_msg)

break

if case(FTD_Tid_RtnRuleIndicator):

# self.api.OnRspUserLogout(req_msg)

break

if case(): # default, could also just omit condition or 'if True'

print('switch case default!')

def startWebsocketServer(self):

def login(msg):

# switch case

if msg["Tid"] == 8201:

api.SubscribeMarketData(msg)

print("行情定阅成功")

else:

data = msg["data"]

try:

if data['CustomerID'] == '1001' and data['Password'] == '123456':

return True

else:

return False

except Exception as e:

print(e)

return False

def onmessage(client, server, msg):

jsonObject = json.loads(msg)

print("前端传来了:", jsonObject)

if jsonObject["data"].get("stgdesc"):

stgdesc = jsonObject['data']

data = urllib.parse.quote(stgdesc)

print(data)

if jsonObject["data"].get("optype"):

try:

optype = jsonObject['data']["optype"]

stgid = jsonObject['data']['stgid']

if optype == "end":

self.init_run(6, stgid)

print("待停止发送成功!!")

elif optype == "start":

self.init_run(5, stgid)

print("待启动发送成功!!")

except Exception as e:

print(e)

islogin = login(jsonObject)

if client in client_list:

pass

# 请求switchcase方法

else:

if islogin:

client_list.append(client)

# 登录成功,推全量数据给用户

print('登录成功,推全量数据给用户')

print(self.total_dict)

for key in self.total_dict:

data = self.total_dict[key]

server.send_message_to_all(json.dumps(data))

return True

else:

pass

# 登录失败

# // server.send_message(client, json.dumps({"Tid": 1111, "data": {"msg": "登陆失败"}}))

def sendDataToAllClient():

while True:

jsonData = self.tid_Queue.get()

if jsonData == None:

sleep(0.1)

if jsonData["Tid"] == FTD_Tid_RtnRule: # 策略

self.total_dict[jsonData['data']['RuleID']] = jsonData

elif jsonData['Tid'] == FTD_Tid_RtnRuleStatus: # 状态4146

self.total_dict[str(jsonData['data']['RuleID']) + '_' + 'status'] = jsonData

elif jsonData["Tid"] == FTD_Tid_RtnRuleProp: # 参数4164

self.total_dict[str(jsonData['data']['RuleID']) + '_' + jsonData['data']['PropValue']] = jsonData

elif jsonData["Tid"] == FTD_Tid_RtnRuleIndicator: # 指标4165

self.total_dict[str(jsonData['data']['RuleID']) + '_' + jsonData['data']['IndicatorKey']] = jsonData

client_list_copy = copy.copy(client_list)

for client in client_list_copy:

sleep(0.1)

server.send_message(client, json.dumps(jsonData))

# 这是服务端,所以需要监听的是本机的ip,通过ipconfig拿到,

# 同时在linux上的客户端也需要监听这个ip和端口。

# server = WebsocketServer(9000, host='172.16.78.228')

# server = WebsocketServer(9000, host='172.19.190.191')

server = WebsocketServer(9000, host='127.0.0.1')

server.set_fn_message_received(onmessage)

t1 = threading.Thread(target=sendDataToAllClient)

t1.start()

server.run_forever()

server.server_close()

def init_run(self, run_status, stg_id):

'''

1运行中 2已结束 3已分析 4未运行 5待运行 6待停止 7错误

'''

# 数据库

user = 'root'

password = 'XCL29hao'

ip_address = '111.231.16.33'

port = 3306

db_name = 'strategy'

# 表名

self.db_approval = 't_paperapproval'

self.db_run = 't_paperrun'

# 配置文件

self.ini_host = '127.0.0.1'

self.ini_port = 50000

self.ini_api_mode = 'HFT_Address_IP'

self.ini_api_host = '127.0.0.1'

self.ini_api_port = '32201'

# python exe文件, 保存路径

self.python_path = 'D:\\program\\Anaconda3'

self.target_path = 'D:'

# 遍历周期

self.sleep_period = 10

# 数据库连接

self.engine_str = f'mysql+mysqlconnector://{user}:{password}@{ip_address}:{port}/{db_name}?charset=utf8'

self.engine = create_engine(self.engine_str)

try:

# 进程管理

process_main = process_manager()

print('运行一次进程启动...')

# 进程管理器

process_main.get_current_process()

# 读取需要启停的进程

content = f'select * from {self.db_run} where StgID = {stg_id}'

sql_df = pd.read_sql_query(content, self.engine)

if len(sql_df) == 0:

return 0

# 类型转换

run_status = int(run_status)

stg_id = str(stg_id)

path = sql_df['RunPath'].tolist()[0]

# 启动进程

if run_status == run_status_enum.待运行.value:

# 运行进程

flag = process_main.start_process(path)

if flag:

# 更新run表

try:

content = f'update {self.db_run} set RunStatus = \

{run_status_enum.运行中.value}, StartTime = \

"{datetime.datetime.today().strftime("%Y-%m-%d %H:%M:%S")}" \

where StgID = {stg_id}'

pd.read_sql_query(content, self.engine)

except Exception as e:

pass

# 终止进程

elif run_status == run_status_enum.待停止.value:

# 终止进程

flag = process_main.end_process(stg_id)

if flag:

# 更新run表

try:

content = f'update {self.db_run} set RunStatus = \

{run_status_enum.未运行.value}, EndTime = \

"{datetime.datetime.today().strftime("%Y-%m-%d %H:%M:%S")}" \

where StgID = {stg_id}'

pd.read_sql_query(content, self.engine)

except Exception as e:

pass

except Exception as e:

print(e)

"""

消耗多进程队列的func

"""

def main():

"""

@ERYI.tcy

"""

# 初始化

# print('父进程>>', os.getpid())

# 创建一个实时更新队列-存储实时数据

tid_Queue = multiprocessing.Queue()

p1 = multiprocessing.Process(target=TestMdApi, args=(tid_Queue,))

global api

api = TestMdApi(tid_Queue)

api.subs()

p1.start()

# p1.daemon = True

p2 = multiprocessing.Process(target=SocketEnv, args=(tid_Queue,))

p2.start()

socket_env = SocketEnv(tid_Queue)

# # api的业务操作测试 从hft取数据比如请求订阅行情、收策略回报等

socket_env.startWebsocketServer()

i = 1

while True:

if i == 0:

break

api.Exit()

print("父进程结束")

if __name__ == '__main__':

main()

遇到一些问题

'''

websocket服务端

如果部署在服务器上,则监听的是内网ip(阿里云:命令是ifconfig, windowserver:命令是ipconfig), 而客户端连接的是公网ip(服务器外网访问的ip)

如果部署在本地,则监听的是本机的ip, 客户端连接的是本机的ip,通过ipconfig查看本机(ipv4地址),不然的话有可能会抱胸:在其上下文,该请求地址无效,主要就是ip+端口错误导致的。

'''

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要实现Python WebSocket实时消息送,你可以使用Tornado或Flask等Web框架,并结合WebSocket协议库(如Tornado的WebSocketHandler或Flask-SocketIO)实现。 以下是一个使用Flask-SocketIO实现实时消息送的示例代码: 1. 安装Flask-SocketIO库: ```bash pip install flask-socketio ``` 2. 创建Flask应用程序并初始化SocketIO: ```python from flask import Flask, render_template from flask_socketio import SocketIO app = Flask(__name__) app.config['SECRET_KEY'] = 'secret!' socketio = SocketIO(app) ``` 3. 定义WebSocket事件处理器: ```python @socketio.on('connect') def handle_connect(): print('Client connected') @socketio.on('message') def handle_message(msg): print('Received message: ' + msg) # 将消息广播给所有客户端 socketio.emit('message', msg) @socketio.on('disconnect') def handle_disconnect(): print('Client disconnected') ``` 4. 在HTML模板中添加WebSocket客户端代码: ```html <!DOCTYPE html> <html> <head> <title>WebSocket Test</title> </head> <body> <h1>WebSocket Test</h1> <input type="text" id="message-input"> <button id="send-button">Send</button> <script src="//code.jquery.com/jquery-1.11.3.min.js"></script> <script src="//cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js"></script> <script> var socket = io.connect(); socket.on('connect', function() { console.log('Connected to server'); }); socket.on('message', function(msg) { console.log('Received message: ' + msg); // 在页面上显示收到的消息 $('#message-list').append($('<li>').text(msg)); }); $('#send-button').click(function() { var msg = $('#message-input').val(); socket.send(msg); }); </script> </body> </html> ``` 5. 运行应用程序并访问网页: ```python if __name__ == '__main__': socketio.run(app) ``` 以上是一个基本的WebSocket实时消息送示例,你可以根据自己的需求进行修改和优化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值