日常生活中,有很多需要数据的实时更新,比如群聊信息的实时更新,还有投票系统的实时刷新等

  实现的方式有很多种,比如轮询、长轮询、websocket

轮询

  轮询是通过设置页面的刷新频率(设置多长时间自动刷新一次页面)来实现的。

使用轮询的机制模拟投票系统的实时刷新

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Title</title>
</head>
<body>
<ul>
{% for k,v in dic.items() %}
<li style="cursor:pointer" ondblclick="ticket('{{k}}')">{{v.name}}:{{v.count}}</li>
{% endfor %}
</ul> </body>
<script src="/static/jquery-3.3.1.js"></script>
<script>
function ticket(nid){
$.ajax({
url:'/ticket',
type:'post',
data:{nid:nid},
success:function (arg) { }
})
}
setInterval(function () {
window.location.reload();
},2000); </script> </html>

HTML

备注:可以给标签添加样式:style="cursor:pointer",点击时,鼠标样式为小手。

from flask import Flask,request,render_template

app = Flask(__name__,static_folder='static',template_folder='template')
app.debug = True dic = {'':{'name':'a','count':0},'':{'name':'b','count':0}} @app.route('/index',methods=['GET','POST'])
def index(): return render_template('index.html',dic=dic) @app.route('/ticket',methods=['POST'])
def ticket():
if request.method == 'POST':
nid = request.form.get('nid')
print(nid)
dic[nid]['count'] += 1
return '投票成功' if __name__ == '__main__':
app.run(host='127.0.0.1',port=5000)

.py

  缺点:需要频繁的发送请求,服务端压力大,如果数据长时间没有更新就会造成资源的浪费,而且因为设置了多长时间刷新一次,所以数据的显示有延迟。

长轮询

  web版的qq或者微信等都是采用长轮询实现的。

  原理:服务端将用户的请求夯住,比如夯住10s,如果在这10s中有票数或消息,则立刻返回响应,否则,到指定时间后自动返回响应,然后,客户端自动再次发送请求。

import queue
import uuid
import json
from flask import Flask,request,render_template,make_response,session,jsonify app = Flask(__name__)
app.secret_key = 'asdf asdf' USER_DICT = {
'':{'name':'野味','count':1},
'':{'name':'海龙','count':1},
} QUEUE_DICT = {} @app.route('/index',methods=['GET','POST'])
def index():
# 为每一个访问页面的用户生成一个唯一的标识
user_id = str(uuid.uuid4())
# 为每一个用户生成一个q对象,并添加到全局中,同时在用户的cookie中携带这个唯一标识
QUEUE_DICT[user_id] = queue.Queue()
session['user_id'] = user_id
return render_template('index.html',user_dict=USER_DICT) @app.route('/get_new_vote')
def get_new_vote():
result = {'status':True,'data':None} # 根据user_id获取当前用户的queue
user_id = session['user_id']
q = QUEUE_DICT[user_id]
try:
data = q.get(timeout=10)
result['data'] =data
except queue.Empty as e:
result['status'] = False # return json.dumps(result)
return jsonify(result) # JsonResponse @app.route('/vote',methods=['POST'])
def vote():
uid = request.form.get('uid')
USER_DICT[uid]['count'] += 1 ticket_info = {'uid':uid, 'count':USER_DICT[uid]['count']} for q in QUEUE_DICT.values():
q.put(ticket_info) return '投票成功' if __name__ == '__main__':
app.run(host='0.0.0.0',debug=False,threaded=True)

py

备注:uuid.uuid4()  不是json支持的序列化数据类型,所有需要先转化为str

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>最丑的男人</h1>
<ul>
{% for k,v in user_dict.items() %}
<li id="user_{{k}}" style="cursor: pointer" ondblclick="vote({{k}})">{{v.name}}<span>{{v.count}}</span></li>
{% endfor %}
</ul>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<script>
$(function () {
get_new_vote();
}); function get_new_vote() {
$.ajax({
url:'/get_new_vote',
type:'GET',
dataType:'json',
success:function (arg) {
if(arg.status){
$('#user_'+arg.data.uid).find('span').text(arg.data.count);
} get_new_vote();
}
})
} function vote(uid) {
$.ajax({
url:'/vote',
type:'POST',
data:{uid:uid},
success:function (arg) {
console.log(arg);
}
})
} </script>
</body>
</html>

HTML

  我们借助了队列实现了长轮询,同样的也可以使用redis实现,借助redis的列表也可以实现队列。

备注:redis中的blpop(name,timeout="xx")  ,可以设置超时时间。

websocket协议

  websocket协议与HTTP协议的最大的区别是:HTTP协议是一次请求和一次响应后断开连接,而websocket是一次请求和一次连接后连接不断开。

  由于在flask中默认使用wsgi的模块是werkzeug,而werkzeug是不支持websocket的,所以,使用flask发送websocket请求时,需要借助一个第三方的包----->gevent-websocket

  安装: pip install gevent-websocket

  gevent-websocket是一个wsgi协议的模块,内部支持http请求,同时也支持websocket请求,所以,使用flask时,要将flask的werkzeug替换掉。

  用法:

  先导入:

  from geventwebsocket.handler import WebSocketHandler
  from gevent.pywsgi import WSGIServer

在启动flask时,替换掉原来的启动方式:

http_server = WSGIServer(('0.0.0.0', 5000), app, handler_class=WebSocketHandler)    # 启动传入ip、端口,还有app对象
http_server.serve_forever()

  使用websocket发送请求的方式:在js中new一个WebSocket("ws://127.0.0.1:5000/get_vote");页面就会自动发送一个websocket的请求。

备注:websocket中的参数url的格式:  ws://ip:port/路径      不同于HTTP请求的:   http://....     websocket的请求是以ws起头的。

  同样的,对于一个路径,既可以接收HTTP协议的请求,也可以接收websocket的请求,所以,在后端,试图函数层就要对请求使用的协议加以区分。

  后端视图函数区分websocket和HTTP请求的方法:request.environ.get("wsgi.websocket")  ,如果是http请求,返回None,如果是websocket请求,返回一个<geventwebsocket.websocket.WebSocket object at 0x00000232849FE458>

  可以借助这个参数加以区分。在后端,可以使用接收到的ws对象    ws=request.environ.get("wsgi.websocket")

  发送数据:   ws.send("数据")                       接收数据:  ws.receive()  

  在前端,可以通过一个回调函数接收数据 ,通过send发送数据

<script>
var ws = new WebSocket("ws://127.0.0.1:5000/get_vote");
// 有消息时,会自动触发执行回调函数
ws.onmessage = function (ev) {
console.log(ev.data);
}; function ticket(nid){
ws.send('发送的数据');
} </script>

基于websocket实现投票示例:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>最丑的男人</h1>
<ul>
{% for k,v in user_dict.items() %}
<li id="user_{{k}}" style="cursor: pointer" ondblclick="vote({{k}})">{{v.name}}<span>{{v.count}}</span></li>
{% endfor %}
</ul>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<script>
// 向后台发送websocekt请求
var ws = new WebSocket('ws://192.168.12.200:5000/get_new_vote'); ws.onmessage = function (ev) {
var ticket = JSON.parse(ev.data);
$('#user_'+ticket.uid).find('span').text(ticket.count);
}; function vote(uid) {
ws.send(uid)
} </script>
</body>
</html>

HTML

import queue
import uuid
import json
from flask import Flask,request,render_template,make_response,session,jsonify from geventwebsocket.handler import WebSocketHandler
from gevent.pywsgi import WSGIServer app = Flask(__name__)
app.secret_key = 'asdf asdf' USER_DICT = {
'':{'name':'野味','count':1},
'':{'name':'海龙','count':1},
} @app.route('/index',methods=['GET','POST'])
def index():
return render_template('index.html',user_dict=USER_DICT) # http://127.0.0.1:5000/get_new_vote
# ws://127.0.0.1:5000/get_new_vote
WEBSOCKET_LIST = [] @app.route('/get_new_vote')
def get_new_vote():
ws = request.environ.get('wsgi.websocket')
if not ws:
return "请使用websocket协议"
# 浏览器发送的socket客户端
WEBSOCKET_LIST.append(ws)
while True:
uid = ws.receive()
// 如果用户关闭浏览器,会收到一个空
if not uid:
ws.close()
WEBSOCKET_LIST.remove(ws)
USER_DICT[uid]['count'] += 1
# ws.send('666')
ticket_info = {'uid':uid,'count':USER_DICT[uid]['count']}
for item in WEBSOCKET_LIST:
item.send(json.dumps(ticket_info)) if __name__ == '__main__':
# app.run(host='0.0.0.0',debug=False,threaded=True)
# app.run(debug=False,threaded=True)
# http_server = WSGIServer(('127.0.0.1', 5000), app, handler_class=WebSocketHandler)
http_server = WSGIServer(('0.0.0.0', 5000), app, handler_class=WebSocketHandler)
http_server.serve_forever()

.py文件

备注:如果用户关闭浏览器,ws.receive()会接收到空,所以要将ws关闭,ws.close().

websocket建立连接时,会先进行握手,认证的过程,

请求和响应的【握手】信息需要遵循规则:

  • 从请求【握手】信息中提取 Sec-WebSocket-Key
  • 利用magic_string 和 Sec-WebSocket-Key 进行hmac1加密,再进行base64加密
  • 将加密结果响应给客户端

注:magic string为:258EAFA5-E914-47DA-95CA-C5AB0DC85B11

  发起websocket请求时,会先进行一个认证握手的过程,这个过程为:

  1.用户发起websocket请求后,会先在请求头中获取Sec-WebSocket-Key对应的值,再将这个值与majic_string(魔法字符串<这个字符串是固定的>)相加,将相加的结果先通过hashlib.sha1加密,在通过base64加密,然后在将加密后的结果返回给客户端。客户端收到后校验是不是采用的这张加密,通过后,建立起连接,否则就会拒绝连接。

  握手成功后才能发送数据,而且收发数据是加密的。

import socket
import base64
import hashlib def get_headers(data):
"""
将请求头格式化成字典
:param data:
:return:
"""
header_dict = {}
data = str(data, encoding='utf-8') for i in data.split('\r\n'):
print(i)
header, body = data.split('\r\n\r\n', 1)
header_list = header.split('\r\n')
for i in range(0, len(header_list)):
if i == 0:
if len(header_list[i].split(' ')) == 3:
header_dict['method'], header_dict['url'], header_dict['protocol'] = header_list[i].split(' ')
else:
k, v = header_list[i].split(':', 1)
header_dict[k] = v.strip()
return header_dict sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('127.0.0.1', 8002))
sock.listen(5) conn, address = sock.accept()
data = conn.recv(1024)
headers = get_headers(data) # 提取请求头信息
# 对请求头中的sec-websocket-key进行加密
response_tpl = "HTTP/1.1 101 Switching Protocols\r\n" \
"Upgrade:websocket\r\n" \
"Connection: Upgrade\r\n" \
"Sec-WebSocket-Accept: %s\r\n" \
"WebSocket-Location: ws://%s%s\r\n\r\n"
magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
value = headers['Sec-WebSocket-Key'] + magic_string
ac = base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest())
response_str = response_tpl % (ac.decode('utf-8'), headers['Host'], headers['url'])
# 响应【握手】信息
conn.send(bytes(response_str, encoding='utf-8'))

客户端和服务端收发数据

  客户端和服务端传输数据时,需要对数据进行【封包】和【解包】。

  客户端的JavaScript类库已经封装【封包】和【解包】过程,但Socket服务端需要手动实现。

解包过程:

  服务端接收到客户端发送的数据后,后先取出第二个字节的后七位,然后,对这后七位进行一个判断:(后七位转化为数字最大为127),如果后七位<=125,那么前两个字节就是报文;如果后七位=126,则继续往后读16个字节,也就是取前四个字节作为报文;如果后七位=127,则往后读64位,也就是取前10个字节作为报文。然后剩余的数据取前四个字节作为masking_key(掩码key),对剩下的数据在每个字节的与masking_key做位运算,最终得到真实的数据。

Decoding Payload Length

To read the payload data, you must know when to stop reading. That's why the payload length is important to know. Unfortunately, this is somewhat complicated. To read it, follow these steps:

    Read bits 9-15 (inclusive) and interpret that as an unsigned integer. If it's 125 or less, then that's the length; you're done. If it's 126, go to step 2. If it's 127, go to step 3.
Read the next 16 bits and interpret those as an unsigned integer. You're done.
Read the next 64 bits and interpret those as an unsigned integer (The most significant bit MUST be 0). You're done. Reading and Unmasking the Data If the MASK bit was set (and it should be, for client-to-server messages), read the next 4 octets (32 bits); this is the masking key. Once the payload length and masking key is decoded, you can go ahead and read that number of bytes from the socket. Let's call the data ENCODED, and the key MASK. To get DECODED, loop through the octets (bytes a.k.a. characters for text data) of ENCODED and XOR the octet with the (i modulo 4)th octet of MASK. In pseudo-code (that happens to be valid JavaScript): var DECODED = "";
for (var i = 0; i < ENCODED.length; i++) {
DECODED[i] = ENCODED[i] ^ MASK[i % 4];
} Now you can figure out what DECODED means depending on your application.

基于python实现websocket的解包过程

import socket
import base64
import hashlib def get_headers(data):
"""
将请求头格式化成字典
:param data:
:return:
"""
header_dict = {}
data = str(data, encoding='utf-8') # for i in data.split('\r\n'):
# print(i)
header, body = data.split('\r\n\r\n', 1)
header_list = header.split('\r\n')
for i in range(0, len(header_list)):
if i == 0:
if len(header_list[i].split(' ')) == 3:
header_dict['method'], header_dict['url'], header_dict['protocol'] = header_list[i].split(' ')
else:
k, v = header_list[i].split(':', 1)
header_dict[k] = v.strip()
return header_dict sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('127.0.0.1', 8002))
sock.listen(5) conn, address = sock.accept()
data = conn.recv(1024)
headers = get_headers(data) # 提取请求头信息
# 对请求头中的sec-websocket-key进行加密
response_tpl = "HTTP/1.1 101 Switching Protocols\r\n" \
"Upgrade:websocket\r\n" \
"Connection: Upgrade\r\n" \
"Sec-WebSocket-Accept: %s\r\n" \
"WebSocket-Location: ws://%s%s\r\n\r\n"
magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
value = headers['Sec-WebSocket-Key'] + magic_string
ac = base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest())
response_str = response_tpl % (ac.decode('utf-8'), headers['Host'], headers['url'])
# 响应【握手】信息
conn.send(bytes(response_str, encoding='utf-8'))
while True:
info = conn.recv(8096) payload_len = info[1] & 127
if payload_len == 126:
extend_payload_len = info[2:4]
mask = info[4:8]
decoded = info[8:]
elif payload_len == 127:
extend_payload_len = info[2:10]
mask = info[10:14]
decoded = info[14:]
else:
extend_payload_len = None
mask = info[2:6]
decoded = info[6:] bytes_list = bytearray()
for i in range(len(decoded)):
chunk = decoded[i] ^ mask[i % 4]
bytes_list.append(chunk)
body = str(bytes_list, encoding='utf-8')
print(body)

python实现解包

封包过程:

   向客户端发送数据【封包】

def send_msg(conn, msg_bytes):
"""
WebSocket服务端向客户端发送消息
:param conn: 客户端连接到服务器端的socket对象,即: conn,address = socket.accept()
:param msg_bytes: 向客户端发送的字节
:return:
"""
import struct token = b"\x81"
length = len(msg_bytes)
if length < 126:
token += struct.pack("B", length)
elif length <= 0xFFFF:
token += struct.pack("!BH", 126, length)
else:
token += struct.pack("!BQ", 127, length) msg = token + msg_bytes
conn.send(msg)
return True

基于python实现封包

基于python  socket实现WebSocket服务端

import socket
import base64
import hashlib def get_headers(data):
"""
将请求头格式化成字典
:param data:
:return:
"""
header_dict = {}
data = str(data, encoding='utf-8') header, body = data.split('\r\n\r\n', 1)
header_list = header.split('\r\n')
for i in range(0, len(header_list)):
if i == 0:
if len(header_list[i].split(' ')) == 3:
header_dict['method'], header_dict['url'], header_dict['protocol'] = header_list[i].split(' ')
else:
k, v = header_list[i].split(':', 1)
header_dict[k] = v.strip()
return header_dict def send_msg(conn, msg_bytes):
"""
WebSocket服务端向客户端发送消息
:param conn: 客户端连接到服务器端的socket对象,即: conn,address = socket.accept()
:param msg_bytes: 向客户端发送的字节
:return:
"""
import struct token = b"\x81"
length = len(msg_bytes)
if length < 126:
token += struct.pack("B", length)
elif length <= 0xFFFF:
token += struct.pack("!BH", 126, length)
else:
token += struct.pack("!BQ", 127, length) msg = token + msg_bytes
conn.send(msg)
return True def run():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('127.0.0.1', 8003))
sock.listen(5) conn, address = sock.accept()
data = conn.recv(1024)
headers = get_headers(data)
response_tpl = "HTTP/1.1 101 Switching Protocols\r\n" \
"Upgrade:websocket\r\n" \
"Connection:Upgrade\r\n" \
"Sec-WebSocket-Accept:%s\r\n" \
"WebSocket-Location:ws://%s%s\r\n\r\n" value = headers['Sec-WebSocket-Key'] + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
ac = base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest())
response_str = response_tpl % (ac.decode('utf-8'), headers['Host'], headers['url'])
conn.send(bytes(response_str, encoding='utf-8')) while True:
try:
info = conn.recv(8096)
except Exception as e:
info = None
if not info:
break
payload_len = info[1] & 127
if payload_len == 126:
extend_payload_len = info[2:4]
mask = info[4:8]
decoded = info[8:]
elif payload_len == 127:
extend_payload_len = info[2:10]
mask = info[10:14]
decoded = info[14:]
else:
extend_payload_len = None
mask = info[2:6]
decoded = info[6:] bytes_list = bytearray()
for i in range(len(decoded)):
chunk = decoded[i] ^ mask[i % 4]
bytes_list.append(chunk)
body = str(bytes_list, encoding='utf-8')
send_msg(conn,body.encode('utf-8')) sock.close() if __name__ == '__main__':
run()

基于python

基于Javascript实现客户端

<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div>
<input type="text" id="txt"/>
<input type="button" id="btn" value="提交" onclick="sendMsg();"/>
<input type="button" id="close" value="关闭连接" onclick="closeConn();"/>
</div>
<div id="content"></div> <script type="text/javascript">
var socket = new WebSocket("ws://127.0.0.1:8003/chatsocket"); socket.onopen = function () {
/* 与服务器端连接成功后,自动执行 */ var newTag = document.createElement('div');
newTag.innerHTML = "【连接成功】";
document.getElementById('content').appendChild(newTag);
}; socket.onmessage = function (event) {
/* 服务器端向客户端发送数据时,自动执行 */
var response = event.data;
var newTag = document.createElement('div');
newTag.innerHTML = response;
document.getElementById('content').appendChild(newTag);
}; socket.onclose = function (event) {
/* 服务器端主动断开连接时,自动执行 */
var newTag = document.createElement('div');
newTag.innerHTML = "【关闭连接】";
document.getElementById('content').appendChild(newTag);
}; function sendMsg() {
var txt = document.getElementById('txt');
socket.send(txt.value);
txt.value = "";
}
function closeConn() {
socket.close();
var newTag = document.createElement('div');
newTag.innerHTML = "【关闭连接】";
document.getElementById('content').appendChild(newTag);
} </script>
</body>
</html>

客户端

websocket的使用场景

  websocket主要用于页面数据的实时更新。

总结:web实现数据的实时更新的方案:

  1.长轮询           兼容性好

  2.websocket          性能更优

                              

你想了解的轮询、长轮询和websocket都在这里了的更多相关文章

  1. 使用轮询&长轮询实现网页聊天室

    前言 如果有一个需求,让你构建一个网络的聊天室,你会怎么解决? 首先,对于HTTP请求来说,Server端总是处于被动的一方,即只能由Browser发送请求,Server才能够被动回应. 也就是说,如 ...

  2. 轮询、长轮询、长连接、websocket

    Web端即时通讯技术:即时通讯技术简单的说就是实现这样一种功能:服务器端可以即时地将数据的更新或变化反应到客户端,例如消息即时推送等功能都是通过这种技术实现的.但是在Web中,由于浏览器的限制,实现即 ...

  3. Apollo 3 定时/长轮询拉取配置的设计

    前言 如上图所示,Apollo portal 更新配置后,进行轮询的客户端获取更新通知,然后再调用接口获取最新配置.不仅仅只有轮询,还有定时更新(默认 5 分钟一次).目的就是让客户端能够稳定的获取到 ...

  4. 了解轮询、长轮询、长连接、websocket

    业务开发中我们往往会有一些需要即时通信的场景,比如微信扫码登录.聊天功能. 下面这四种方式都可以实现即时通信. 轮询: 浏览器通过定时器每隔一段时间向服务器端发送请求,服务器端收到请求并响应请求.没有 ...

  5. Tornado长轮询和WebSocket

    Http协议是一种请求响应式协议, 不允许服务端主动向客户端发送信息. 短轮询是一种简单的实现服务端推送消息的解决方案, 客户端以一定间隔自动向服务端发送刷新请求, 服务端返回要推送的消息作为响应. ...

  6. es6- Generator函数实现长轮询

    1.Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同. 语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态.形式上,Gene ...

  7. 长连接、短连接、长轮询和WebSocket

    //转发,格式待整理 2017-08-0519784View0 对这四个概念不太清楚,今天专门搜索了解一下,总结一下: 长连接:在HTTP 1.1,客户端发出请求,服务端接收请求,双方建立连接,在服务 ...

  8. http轮询,长轮询

    轮询,长轮询 轮询 轮询:客户端定时向服务器发送Ajax请求,服务器接到请求后马上返回响应信息并关闭连接. 优点:后端程序编写比较容易. 缺点:请求中有大半是无用,浪费带宽和服务器资源. 实例:适于小 ...

  9. 三周,用长轮询实现Chat并迁移到Azure测试

    公司的OA从零开始进行开发,继简单的单点登陆.角色与权限.消息中间件之后,轮到在线即时通信的模块需要我独立去完成.这三周除了逛网店见爱*看动漫接兼职,基本上都花在这上面了.简单地说就是用MVC4基于长 ...

随机推荐

  1. ACE接受器-连接器模式

    转载于:http://www.cnblogs.com/TianFang/archive/2006/12/22/600191.html 接受器-连接器设计模式(Acceptor-Connector)使分 ...

  2. k好数 数位dp

    问题描述 如果一个自然数N的K进制表示中任意的相邻的两位都不是相邻的数字,那么我们就说这个数是K好数.求L位K进制数中K好数的数目.例如K = 4,L = 2的时候,所有K好数为11.13.20.22 ...

  3. 三大linux系统对比

    概述: centos作为服务器部署是第一选择.CentOS去除很多与服务器功能无关的应用,系统简单但非常稳定,命令行操作可以方便管理系统和应用,丰富的帮助文档和社区的支持. ubuntu最佳的应用领域 ...

  4. OpecnCV训练分类器详细整理

    本文主要是对下面网址博客中内容的实例操作: http://blog.csdn.net/byxdaz/article/details/4907211 在上述博客中,详细的讲述了OpenCV训练分类器的做 ...

  5. css+vue实现添加购物车效果

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  6. 【BZOJ1560】【JSOI2009】火星藏宝图 [DP]

    火星藏宝图 Time Limit: 10 Sec  Memory Limit: 64 MB[Submit][Status][Discuss] Description Input Output Samp ...

  7. Hadoop和大数据:60款顶级开源工具(山东数漫江湖)

    说到处理大数据的工具,普通的开源解决方案(尤其是Apache Hadoop)堪称中流砥柱.弗雷斯特调研公司的分析师Mike Gualtieri最近预测,在接下来几年,“100%的大公司”会采用Hado ...

  8. 【洛谷 P2303】 [SDOi2012]Longge的问题 (欧拉函数)

    题目链接 题意:求\(\sum_{i=1}^{n}\gcd(i,n)\) 首先可以肯定,\(\gcd(i,n)|n\). 所以设\(t(x)\)表示\(gcd(i,n)=x\)的\(i\)的个数. 那 ...

  9. [2009国家集训队]小Z的袜子(hose)(BZOJ2038+莫队入门题)

    题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=2038 题目: 题意:中文题意,大家都懂. 思路:莫队入门题.不过由于要去概率,所以我们假 ...

  10. Coursera在线学习---第九节(1).异常数据检测(Anomaly Detection)

    一.如何构建Anomaly Detection模型? 二.如何评估Anomaly Detection系统? 1)将样本分为6:2:2比例 2)利用交叉验证集计算出F1值,可以用F1值选取概率阈值ξ,选 ...