python---websocket的使用

news/2024/12/2 6:30:48/

一:简介

推文:WebSocket 是什么原理?为什么可以实现持久连接?

推文:WebSocket:5分钟从入门到精通(很好)

WebSocket协议是基于TCP的一种新的协议。WebSocket最初在HTML5规范中被引用为TCP连接,作为基于TCP的套接字API的占位符。它实现了浏览器与服务器全双工(full-duplex)通信。其本质是保持TCP连接,在浏览器和服务端通过Socket进行通信。

二:对比:

Http:

  socket实现,单工通道(浏览器只发起,服务端只做响应),短连接,请求响应

WebSocket:

  socket实现,双工通道,请求响应,推送。socket创建连接,不断开

三:socket实现步骤

服务端:

1. 服务端开启socket,监听IP和端口
3. 允许连接
* 5. 服务端接收到特殊值【加密sha1,特殊值,migic string="258EAFA5-E914-47DA-95CA-C5AB0DC85B11"* 6. 加密后的值发送给客户端

客户端:

2. 客户端发起连接请求(IP和端口)
* 4. 客户端生成一个xxx,【加密sha1,特殊值,migic string="258EAFA5-E914-47DA-95CA-C5AB0DC85B11"】,向服务端发送一段特殊值
* 7. 客户端接收到加密的值

注意:这个魔数是固定的 258EAFA5-E914-47DA-95CA-C5AB0DC85B11

四:简单实现,实现连接

服务端:

# coding:utf8
# __author:  Administrator
# date:      2018/6/29 0029
# /usr/bin/env python
import socket,base64,hashlibdef get_headers(data):'''将请求头转换为字典'''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[0].split(" ")) == 3:header_dict['method'],header_dict['url'],header_dict['protocol'] = header_list[0].split(" ")else:k,v=header_list[i].split(":",1)header_dict[k]=v.strip()return header_dictsock = socket.socket()
sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
sock.bind(("127.0.0.1",8080))
sock.listen(5)#等待用户连接
conn,addr = sock.accept()
print("conn from ",conn,addr)
#获取握手消息,magic string ,sha1加密
#发送给客户端
#握手消息
data = conn.recv(8096)
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'))
'''
b'
GET / HTTP/1.1\r\n
Host: 127.0.0.1:8080\r\n
Connection: Upgrade\r\n
Pragma: no-cache\r\n
Cache-Control: no-cache\r\n
Upgrade: websocket\r\n
Origin: http://localhost:63342\r\n
Sec-WebSocket-Version: 13\r\n
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36\r\n
Accept-Encoding: gzip, deflate, br\r\n
Accept-Language: zh-CN,zh;q=0.8\r\n
Sec-WebSocket-Key: +uL/aiakjNABjEoMzAqm6Q==\r\n
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits\r\n\r\n'
'''
请求头

浏览器:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body></body>
</html>
<script>ws =new WebSocket("ws://127.0.0.1:8080");ws.onopen = function (ev) { //若是连接成功,onopen函数会执行console.log(22222)}
</script>

五:数据接收规则

 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len |    Extended payload length    |  #Payload len(第二个字节的前七位,最大127)决定头部的长度
|I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |  #若是小于126:Extended payload length扩展头部长度为0字节,后面全部为主体数据
|N|V|V|V|       |S|             |   (if payload len==126/127)   |  #若是等于126:Extended payload length扩展头部长度为2字节,后面全部为主体数据
| |1|2|3|       |K|             |                               |  #若是等于127:Extended payload length扩展头部长度为8字节,后面全部为主体数据
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
|     Extended payload length continued, if payload len == 127  |  #注意:主体数据中的前四位为mask掩码,用于后面的消息的解码,解码方式为循环异或操作
+ - - - - - - - - - - - - - - - +-------------------------------+
|                               |Masking-key, if MASK set to 1  |  #数据过长,需要分部发送,这时需要FIN和opcode
+-------------------------------+-------------------------------+
| Masking-key (continued)       |          Payload Data         |
+-------------------------------- - - - - - - - - - - - - - - - +
:                     Payload Data continued ...                :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
|                     Payload Data continued ...                |
+---------------------------------------------------------------+
The MASK bit simply tells whether the message is encoded. Messages from the client must be masked, so your server should expect this to be 1. (In fact, section 5.1 of the spec says that your server must disconnect from a client if that client sends an unmasked message.) When sending a frame back to the client, do not mask it and do not set the mask bit. We'll explain masking later. Note: You have to mask messages even when using a secure socket.RSV1-3 can be ignored, they are for extensions.

The opcode field defines how to interpret the payload data: 0x0 for continuation, 0x1 for text (which is always encoded in UTF-8), 0x2 for binary, and other so-called "control codes" that will be discussed later. In this version of WebSockets, 0x3 to 0x7 and 0xB to 0xF have no meaning.The FIN bit tells whether this is the last message in a series. If it's 0, then the server will keep listening for more parts of the message; otherwise, the server should consider the message delivered. More on this later.

Decoding Payload LengthTo 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 DataIf 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.
View Code

数据帧格式:

FIN:1个比特。

如果是1,表示这是消息(message)的最后一个分片(fragment),如果是0,表示不是是消息(message)的最后一个分片(fragment)。

RSV1, RSV2, RSV3:各占1个比特。

一般情况下全为0。当客户端、服务端协商采用WebSocket扩展时,这三个标志位可以非0,且值的含义由扩展进行定义。如果出现非零的值,且并没有采用WebSocket扩展,连接出错。

Opcode: 4个比特。

操作代码,Opcode的值决定了应该如何解析后续的数据载荷(data payload)。如果操作代码是不认识的,那么接收端应该断开连接(fail the connection)。可选的操作代码如下:%x0:表示一个延续帧。当Opcode为0时,表示本次数据传输采用了数据分片,当前收到的数据帧为其中一个数据分片。
%x1:表示这是一个文本帧(frame)
%x2:表示这是一个二进制帧(frame)
%x3-7:保留的操作代码,用于后续定义的非控制帧。
%x8:表示连接断开。
%x9:表示这是一个ping操作。
%xA:表示这是一个pong操作。
%xB-F:保留的操作代码,用于后续定义的控制帧。

Mask: 1个比特。

表示是否要对数据载荷进行掩码操作。从客户端向服务端发送数据时,需要对数据进行掩码操作;从服务端向客户端发送数据时,不需要对数据进行掩码操作。如果服务端接收到的数据没有进行过掩码操作,服务端需要断开连接。如果Mask是1,那么在Masking-key中会定义一个掩码键(masking key),并用这个掩码键来对数据载荷进行反掩码。所有客户端发送到服务端的数据帧,Mask都是1。掩码的算法、用途在下一小节讲解。

Payload length:数据载荷的长度,单位是字节。为7位,或7+16位,或1+64位。

假设数Payload length === x,如果x为0~126:数据的长度为x字节。
x为126:后续2个字节代表一个16位的无符号整数,该无符号整数的值为数据的长度。
x为127:后续8个字节代表一个64位的无符号整数(最高位为0),该无符号整数的值为数据的长度。
此外,如果payload length占用了多个字节的话,payload length的二进制表达采用网络序(big endian,重要的位在前)。

Masking-key:0或4字节(32位)

所有从客户端传送到服务端的数据帧,数据载荷都进行了掩码操作,Mask为1,且携带了4字节的Masking-key。如果Mask为0,则没有Masking-key。备注:载荷数据的长度,不包括mask key的长度。

Payload data:(x+y) 字节

载荷数据:包括了扩展数据、应用数据。其中,扩展数据x字节,应用数据y字节。扩展数据:如果没有协商使用扩展的话,扩展数据数据为0字节。所有的扩展都必须声明扩展数据的长度,或者可以如何计算出扩展数据的长度。此外,扩展如何使用必须在握手阶段就协商好。如果扩展数据存在,那么载荷数据长度必须将扩展数据的长度包含在内。应用数据:任意的应用数据,在扩展数据之后(如果存在扩展数据),占据了数据帧剩余的位置。载荷数据长度 减去 扩展数据长度,就得到应用数据的长度。

实现规则解码:

def get_data(info):    #info是我们连接后,接受的数据payload_len = info[1] & 127if 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 = Nonemask = 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')return body

 实现循环获取数据

import socket,base64,hashlibdef get_headers(data):'''将请求头转换为字典'''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[0].split(" ")) == 3:header_dict['method'],header_dict['url'],header_dict['protocol'] = header_list[0].split(" ")else:k,v=header_list[i].split(":",1)header_dict[k]=v.strip()return header_dictdef get_data(info):payload_len = info[1] & 127if 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 = Nonemask = 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')return bodysock = socket.socket()
sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
sock.bind(("127.0.0.1",8080))
sock.listen(5)#等待用户连接
conn,addr = sock.accept()
print("conn from ",conn,addr)
#获取握手消息,magic string ,sha1加密
#发送给客户端
#握手消息
data = conn.recv(8096)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:data = conn.recv(8096)data = get_data(data)print(data)
服务端代码
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body></body>
</html>
<script>ws =new WebSocket("ws://127.0.0.1:8080");ws.onopen = function (ev) { //若是连接成功,onopen函数会执行console.log(22222);ws.send("你好");}
</script>
客户端代码

注意:使用控制台完成发送,而不是刷新页面,会报错,因为我们关闭了连接,试图将关闭信号字节编码出错。这里我们需要利用mask(第二字节中,1表示连接,0断开)

六:数据发送规则(需要发送二进制包struct模块)

def send_msg(conn, msg_bytes):  """
    WebSocket服务端向客户端发送消息:param conn: 客户端连接到服务器端的socket对象,即: conn,address = socket.accept():param msg_bytes: 向客户端发送的字节:return: """
    import structtoken = b"\x81"  #接收的第一字节,一般都是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_bytesconn.send(msg)return True

实现发送数据

# coding:utf8
# __author:  Administrator
# date:      2018/6/29 0029
# /usr/bin/env python
import socket,base64,hashlibdef get_headers(data):'''将请求头转换为字典'''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[0].split(" ")) == 3:header_dict['method'],header_dict['url'],header_dict['protocol'] = header_list[0].split(" ")else:k,v=header_list[i].split(":",1)header_dict[k]=v.strip()return header_dictdef get_data(info):payload_len = info[1] & 127if 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 = Nonemask = 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')return bodydef send_msg(conn, msg_bytes):"""
    WebSocket服务端向客户端发送消息:param conn: 客户端连接到服务器端的socket对象,即: conn,address = socket.accept():param msg_bytes: 向客户端发送的字节:return:"""
    import structtoken = b"\x81" #接收的第一字节,一般都是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_bytesconn.send(msg)return Truesock = socket.socket()
sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
sock.bind(("127.0.0.1",8080))
sock.listen(5)#等待用户连接
conn,addr = sock.accept()
#获取握手消息,magic string ,sha1加密
#发送给客户端
#握手消息
data = conn.recv(8096)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:data = conn.recv(8096)data = get_data(data)print(data)send_msg(conn,bytes(data+"geah",encoding="utf-8"))
服务端
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body></body>
</html>
<script>ws =new WebSocket("ws://127.0.0.1:8080");ws.onopen = function (ev) { //若是连接成功,onopen函数会执行console.log(22222);ws.send("你好");}ws.onmessage = function (ev) {console.log(ev);}
</script>
前端onmessage 当数据接收会触发

七:tornado实现websocket聊天室

 tornado服务端

import tornado.ioloop
import tornado.web
import tornado.websocket
import datetimeclass MainHandler(tornado.web.RequestHandler):def get(self):self.render("s1.html")def post(self, *args, **kwargs):passusers = set()
class ChatHandler(tornado.websocket.WebSocketHandler):def open(self, *args, **kwargs):'''客户端连接'''print("connect....")print(self.request)users.add(self)def on_message(self, message):'''有消息到达'''now = datetime.datetime.now()content = self.render_string("recv_msg.html",date=now.strftime("%Y-%m-%d %H:%M:%S"),msg=message)for client in users:if client == self:continueclient.write_message(content)def on_close(self):'''客户端主动关闭连接'''users.remove(self)st ={"template_path": "template",#模板路径配置"static_path":'static',
}#路由映射   匹配执行,否则404
application = tornado.web.Application([("/index",MainHandler),("/wschat",ChatHandler),
],**st)if __name__=="__main__":application.listen(8080)#io多路复用tornado.ioloop.IOLoop.instance().start()

前端模板

<!DOCTYPE html>
<html lang="en">
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><link href="/static/css/bootstrap.min.css" rel="stylesheet"><link href="/static/css/nifty.min.css" rel="stylesheet"><link href="/static/css/demo/nifty-demo-icons.min.css" rel="stylesheet"><link href="/static/css/demo/nifty-demo.min.css" rel="stylesheet"><link href="/static/plugins/pace/pace.min.css" rel="stylesheet"><script src="/static/js/jquery-2.2.4.min.js"></script><script src="/static/plugins/pace/pace.min.js"></script><script src="/static/js/bootstrap.min.js"></script><script src="/static/js/nifty.min.js"></script><script src="/static/js/demo/nifty-demo.min.js"></script><script src="/static/plugins/flot-charts/jquery.flot.min.js"></script><script src="/static/plugins/flot-charts/jquery.flot.resize.min.js"></script><script src="/static/plugins/gauge-js/gauge.min.js"></script><script src="/static/plugins/skycons/skycons.min.js"></script><script src="/static/plugins/easy-pie-chart/jquery.easypiechart.min.js"></script><script src="/static/js/demo/widgets.js"></script>
</head>
<body>
<div id="container" class="effect  aside-bright mainnav-sm aside-right aside-in"><div class="boxed"><div id="content-container"><div class="row"><div class="col-md-8 col-lg-8 col-sm-8"><!--Chat widget--><!--===================================================--><div class="panel" style="height: 640px"><!--Heading--><div class="panel-heading"><h3 class="panel-title">Chat</h3></div><!--Widget body--><div style="height:510px;padding-top:0px;" class="widget-body"><div class="nano"><div class="nano-content pad-all"><ul class="list-unstyled media-block"></ul></div></div><!--Widget footer--><div class="panel-footer" style="height: 90px;"><div class="row"><div class="col-xs-9"><input type="text" placeholder="Enter your text" class="form-control chat-input"></div><div class="col-xs-3"><button class="btn btn-primary btn-block" οnclick="sendMsg(this);" type="submit">Send</button></div></div></div></div></div><!--===================================================--><!--Chat widget--></div><div class="col-md-4 col-lg-4 col-sm-4"><aside id="aside-container"><div id="aside"><div class="nano has-scrollbar"><div class="nano-content" tabindex="0" style="right: -17px;"><!--Nav tabs--><!--================================--><ul class="nav nav-tabs nav-justified"><li class="active"><a href="#demo-asd-tab-1" data-toggle="tab"><i class="demo-pli-speech-bubble-7"></i></a></li></ul><!--================================--><!--End nav tabs--><!-- Tabs Content --><!--================================--><div class="tab-content"><div class="tab-pane fade in active" id="demo-asd-tab-1"><p class="pad-hor text-semibold text-main"><span class="pull-right badge badge-success">0</span> Friends</p></div></div></div><div class="nano-pane" style="display: none;"><div class="nano-slider" style="height: 4059px; transform: translate(0px, 0px);"></div></div></div></div></aside></div></div></div></div>
</div>
</body>
</html><script>ws = new WebSocket("ws://127.0.0.1:8080/wschat");function sendMsg(ths) {var dt = new Date()var now_time = dt.toLocaleString();var msg = $(ths).parents(".row").find(".chat-input").val();$(ths).parents(".row").find(".chat-input").empty();var li = '<li class="mar-btm"><div class="media-right"><img src="" class="img-circle img-sm" alt="Profile Picture"></div>';li += '<div class="media-body pad-hor speech-right"><div class="speech"><a href="#" class="media-heading">游客</a>';li += '<p>'+msg+'</p>';li += '<p class="speech-time">';li += '<i class="demo-pli-clock icon-fw"></i>'+now_time;li += '</p></div></div></li>';$(ths).parents(".widget-body").find(".list-unstyled").append(li);$(ths).parents(".panel-footer").find(".chat-input").val("");ws.send(msg);}ws.onmessage=function (ev) {$(".list-unstyled").append(ev.data);}
</script>
s1.html

消息插件

                                <li class="mar-btm"><div class="media-left"><img src="img/profile-photos/1.png" class="img-circle img-sm" alt="Profile Picture"></div><div class="media-body pad-hor"><div class="speech"><a href="#" class="media-heading">游客</a><p>{{msg}}</p><p class="speech-time"><i class="demo-pli-clock icon-fw"></i>{{date}}</p></div></div></li>
recv_msg.html

实现效果

游客一:

游客二

 

转载于:https://www.cnblogs.com/ssyfj/p/9245150.html


http://www.ppmy.cn/news/614461.html

相关文章

php pthread安装编译,php 多线程扩展 pthreads 安装 及 使用

1、扩展的编译安装php(Linux)&#xff0c;编辑参数 --enable-maintainer-zts 是必选项&#xff1a;2、下载 php7&#xff1a;http://tw2.php.net/get/php-7.1.2.tar.gz/from/a/mirrorduoxc3、解压并编译phptar -zxf php-7.1.2.tar.gzcd php-7.1.2./configure --prefix/usr/loca…

MAML-Tracker: 目标跟踪分析:CVPR 2020(Oral)

MAML-Tracker: 目标跟踪分析&#xff1a;CVPR 2020(Oral) Tracking by Instance Detection: A Meta-Learning Approach 论文链接&#xff1a;https://arxiv.org/abs/2004.00830 摘要 把跟踪问题看作一类特殊的目标检测问题&#xff0c;称之为实例检测。通过适当的初始化&am…

02Lua入门

前言&#xff1a;语言学起来其实相似点很多&#xff0c;简单整理的知识点目录&#xff1a; 1.使用控制台 2.Lua基础 3.变量 4.运算符 5.控制结构 1.使用控制台 Lua脚本是包含一系列Lua命令的简单脚本&#xff08;扩展名为.lua的文本文件&#xff09;。Lua不关注格式&#xff0c…

php无表单上传文件,php – 来自表单的WP邮件附件,无文件管理器上传文件

从表单通过wp_mail函数我正在尝试发送带附件的电子邮件,而不将文件上传到文件管理器.我收到附件的电子邮件.但附件名称不正确,没有文件类型.请帮忙解决这个问题.这是HTML表单有我的PHP代码if (isset($_POST[Submit])) {$attachments $_FILES[Attached][tmp_name];$recipients …

目标跟踪算法

目标跟踪算法 一&#xff0e;互相关运算 给你一张我的正脸照&#xff08;没有经过美颜处理的&#xff09;&#xff0c;你该如何在人群中找到我呢&#xff1f;一种最直观的方案就是&#xff1a;“谁长得最像就是谁”。但是对于计算机来说&#xff0c;如何衡量“长得像”&#…

Spring Boot中的@RequestMapping注解,如何使用

Spring Boot中的RequestMapping注解 介绍 Spring Boot是一个流行的Java框架&#xff0c;它提供了许多方便的注解和工具&#xff0c;使得Web应用程序的开发变得更加容易。其中&#xff0c;RequestMapping注解是Spring Boot中最常用的注解之一&#xff0c;它可以帮助开发者定义…

JS继承的实现方式

原型链继承&#xff1a;//原型链继承&#xff1a;把父类的私有公有的属性和方法&#xff0c;都作为子类公有的属性&#xff1b;//核心&#xff1a;不是把父类私有公有的属性克隆一份一模一样的给子类的公有吧&#xff1b;他是通过__proto__建立和子类之间的原型链&#xff0c;当…

单目测距算法

单目测距算法 相似三角形 用相似三角形计算物体或者目标到相机的距离&#xff0c;将使用相似三角形来计算相机到一个已知的物体或者目标的距离。 假设有一个宽度为 W 的目标或者物体。然后将这个目标放在距离的相机为 D 的位置。用相机对物体进行拍照并且测量物体的像素宽度…