2.1.C++项目:网络版五子棋对战之前置知识

文章目录

  • 一、Websocketpp
    • (一)Websocket介绍
    • (二)报文格式
    • (三)Websocketpp介绍
    • (四)Websocketpp使用
      • 1.websocketpp常用接口介绍
      • 2. http/websocket服务器
    • (五)JsonCpp使用

一、Websocketpp

(一)Websocket介绍

WebSocket 是从 HTML5 开始支持的⼀种网页端和服务端保持长 连接的 消息推送机制。

  • 传统的 web 程序都是属于 “一问一答” 的形式,即客户端给服务器发送了⼀个 HTTP 请求,服务器给客户端返回一个 HTTP 响应。这种情况下服务器是属于被动的一方,如果客户端不主动发起请求服务器就无法主动给客户端端响应
  • 像网页即时聊天或者我们做的五子棋游戏这样的程序都是非常依赖 “消息推送” 的, 即需要服务器主动推动消息到客户端。如果只是使用原生的 HTTP 协议,要想实现消息推送⼀般需要通过 “轮询” 的方式实现, 而轮询的成本比较高并且也不能及时的获取到消息的响应。
    基于上述两个问题, 就产生了WebSocket协议。WebSocket 更接近于 TCP 这种级别的通信方式,一旦连接建立完成客户端或者服务器都可以主动的向对方发送数据。
    WebSocket 协议本质上是⼀个基于 TCP 的协议。为了建立一个 WebSocket 连接,客户端浏览器首先要向服务器发起⼀个 HTTP 请求,这个请求和通常的 HTTP 请求不同,包含了⼀些附加头信息,通过这个附加头信息完成握手过程并升级协议的过程。
    在这里插入图片描述
    在这里插入图片描述

(二)报文格式

在这里插入图片描述
我们重点关注这几个字段:

  • FIN: WebSocket传输数据以消息为概念单位,一个消息有可能由⼀个或多个帧组成,FIN字段为1表示末尾帧。
  • RSV1~3:保留字段,只在扩展时使⽤,若未启用扩展则应置1,若收到不全为0的数据帧,且未协商扩展则立即终止连接。
  • opcode: 标志当前数据帧的类型
  • 0x0: 表示这是个延续帧,当 opcode 为 0 表⽰本次数据传输采⽤了数据分片,当前收到的帧为其中⼀个分片
  • 0x1: 表示这是文本帧
  • 0x2: 表示这是⼆进制帧
  • 0x3-0x7: 保留,暂未使用
  • 0x8: 表示连接断开
  • 0x9: 表示 ping 帧
  • 0xa: 表示 pong 帧
  • 0xb-0xf: 保留,暂未使⽤
  • mask:表示Payload数据是否被编码,若为1则必有Mask-Key,用于解码Payload数据。仅客户端发送给服务端的消息需要设置。
  • Payload length:数据载荷的长度,单位是字节, 有可能为7位、7+16位、7+64位。假设Payloadlength = x
  • x为0~126:数据的长度为x字节
  • x为126:后续2个字节代表⼀个16位的无符号整数,该无符号整数的值为数据的长度
  • x为127:后续8个字节代表⼀个64位的无符号整数(最高位为0),该⽆符号整数的值为数据的长度
  • Mask-Key:当mask为1时存在,长度为4字节,解码规则: DECODED[i] = ENCODED[i] ^ MASK[i% 4]
  • Payload data: 报文携带的载荷数据

(三)Websocketpp介绍

WebSocketpp是⼀个跨平台的开源(BSD许可证)头部专用C++库,它实现了RFC6455(WebSocket协议)和RFC7692(WebSocketCompression Extensions)。它允许将WebSocket客户端和服务器功能集成到C++程序中。在最常见的配置中,全功能网络I/O由Asio网络库提供。
WebSocketpp的主要特性包括:

  • 事件驱动的接口
  • 支持HTTP/HTTPS、WS/WSS、IPv6
  • 灵活的依赖管理 — Boost库/C++11标准库
  • 可移植性:Posix/Windows、32/64bit、Intel/ARM
  • 线程安全

WebSocketpp同时支持HTTP和Websocket两种网络协议, 比较适用于我们本次的项目,所以我们选用该库作为项目的依赖库用来搭建HTTP和WebSocket服务器。

(四)Websocketpp使用

1.websocketpp常用接口介绍

namespace websocketpp {
typedef lib::weak_ptr<void> connection_hdl;template <typename config>
class endpoint : public config::socket_type {
typedef lib::shared_ptr<lib::asio::steady_timer> timer_ptr;
typedef typename connection_type::ptr connection_ptr;
typedef typename connection_type::message_ptr message_ptr;
typedef lib::function<void(connection_hdl)> open_handler;
typedef lib::function<void(connection_hdl)> close_handler;
typedef lib::function<void(connection_hdl)> http_handler;
typedef lib::function<void(connection_hdl,message_ptr)> message_handler;
/* websocketpp::log::alevel::none 禁⽌打印所有⽇志*/void set_access_channels(log::level channels);/*设置⽇志打印等级*/void clear_access_channels(log::level channels);/*清除指定等级的⽇志*//*设置指定事件的回调函数*/void set_open_handler(open_handler h);/*websocket握⼿成功回调处理函数*/void set_close_handler(close_handler h);/*websocket连接关闭回调处理函数*/void set_message_handler(message_handler h);/*websocket消息回调处理函数*/void set_http_handler(http_handler h);/*http请求回调处理函数*//*发送数据接⼝*/void send(connection_hdl hdl, std::string& payload, frame::opcode::value op);void send(connection_hdl hdl, void* payload, size_t len, frame::opcode::value op);/*关闭连接接⼝*/void close(connection_hdl hdl, close::status::value code, std::string& reason);/*获取connection_hdl 对应连接的connection_ptr*/connection_ptr get_con_from_hdl(connection_hdl hdl);/*websocketpp基于asio框架实现,init_asio⽤于初始化asio框架中的io_service调度器*/void init_asio();/*设置是否启⽤地址重⽤*/void set_reuse_addr(bool value);/*设置endpoint的绑定监听端⼝*/void listen(uint16_t port);/*对io_service对象的run接⼝封装,⽤于启动服务器*/std::size_t run();/*websocketpp提供的定时器,以毫秒为单位*/
timer_ptr set_timer(long duration, timer_handler callback);
};
template <typename config>
class server : public endpoint<connection<config>,config> {
/*初始化并启动服务端监听连接的accept事件处理*/
void start_accept();
}template <typename config>
class connection
: public config::transport_type::transport_con_type
, public config::connection_base {
/*发送数据接⼝*/
error_code send(std::string&payload, frame::opcode::value 
op=frame::opcode::text);
/*获取http请求头部*/
std::string const & get_request_header(std::string const & key)
/*获取请求正⽂*/
std::string const & get_request_body();
/*设置响应状态码*/
void set_status(http::status_code::value code);
/*设置http响应正⽂*/
void set_body(std::string const & value);
/*添加http响应头部字段*/
void append_header(std::string const & key, std::string const & val);
/*获取http请求对象*/
request_type const & get_request();
/*获取connection_ptr 对应的 connection_hdl */
connection_hdl get_handle();
};
namespace http {
70 namespace parser {
71 class parser {
72 std::string const & get_header(std::string const & key)
73 }
74 class request : public parser {
75 /*获取请求⽅法*/
76 std::string const & get_method()
77 /*获取请求uri接⼝*/
78 std::string const & get_uri()
79 };
80 }};
81 
82 namespace message_buffer {
83 /*获取websocket请求中的payload数据类型*/
84 frame::opcode::value get_opcode();
85 /*获取websocket中payload数据*/
86 std::string const & get_payload();
87 };
88 
89 namespace log {
90 struct alevel {
91 static level const none = 0x0;
92 static level const connect = 0x1;
93 static level const disconnect = 0x2;
94 static level const control = 0x4;
static level const frame_header = 0x8;
96 static level const frame_payload = 0x10;
97 static level const message_header = 0x20;
98 static level const message_payload = 0x40;
99 static level const endpoint = 0x80;
100 static level const debug_handshake = 0x100;
101 static level const debug_close = 0x200;
102 static level const devel = 0x400;
103 static level const app = 0x800;
104 static level const http = 0x1000;
105 static level const fail = 0x2000;
106 static level const access_core = 0x00003003;
107 static level const all = 0xffffffff;
108 };
109 }
110 
111 namespace http {
112 namespace status_code {
113 enum value {
114 uninitialized = 0,
115
116 continue_code = 100,
117 switching_protocols = 101,
118
119 ok = 200,
120 created = 201,
121 accepted = 202,
122 non_authoritative_information = 203,
123 no_content = 204,
124 reset_content = 205,
125 partial_content = 206,
126
127 multiple_choices = 300,
128 moved_permanently = 301,
129 found = 302,
130 see_other = 303,
131 not_modified = 304,
132 use_proxy = 305,
133 temporary_redirect = 307,
134
135 bad_request = 400,
136 unauthorized = 401,
137 payment_required = 402,
138 forbidden = 403,
139 not_found = 404,
140 method_not_allowed = 405,
141 not_acceptable = 406,
142 proxy_authentication_required = 407,
143 request_timeout = 408,
144 conflict = 409,
145 gone = 410,
146 length_required = 411,
147 precondition_failed = 412,
148 request_entity_too_large = 413,
149 request_uri_too_long = 414,
150 unsupported_media_type = 415,
151 request_range_not_satisfiable = 416,
152 expectation_failed = 417,
153 im_a_teapot = 418,
154 upgrade_required = 426,
155 precondition_required = 428,
156 too_many_requests = 429,
157 request_header_fields_too_large = 431,
158
159 internal_server_error = 500,
160 not_implemented = 501,
161 bad_gateway = 502,
162 service_unavailable = 503,
163 gateway_timeout = 504,
164 http_version_not_supported = 505,
165 not_extended = 510,
166 network_authentication_required = 511
167 };}}
168 namespace frame {
169 namespace opcode {
170 enum value {
171 continuation = 0x0,
172 text = 0x1,
173 binary = 0x2,
174 rsv3 = 0x3,
175 rsv4 = 0x4,
176 rsv5 = 0x5,
177 rsv6 = 0x6,
178 rsv7 = 0x7,
179 close = 0x8,
180 ping = 0x9,
181 pong = 0xA,
182 control_rsvb = 0xB,
183 control_rsvc = 0xC,
184 control_rsvd = 0xD,
185 control_rsve = 0xE,
186 control_rsvf = 0xF,
187 };}}
188 }

2. http/websocket服务器

使⽤Websocketpp实现⼀个简单的http和websocket服务器:

#include <iostream>
#include <string>
#include <websocketpp/server.hpp>
#include <websocketpp/config/asio_no_tls.hpp>typedef websocketpp::server<websocketpp::config::asio> wsserver_t;void print(const std::string &str)
{std::cout << str << std::endl;
}void http_callback(wsserver_t *srv, websocketpp::connection_hdl hdl) {//给客户端返回一个hello world的页面wsserver_t::connection_ptr conn = srv->get_con_from_hdl(hdl);std::cout << "body: " << conn->get_request_body() << std::endl; websocketpp::http::parser::request req = conn->get_request();std::cout << "method: " << req.get_method() << std::endl;cmake --versionstd::cout << "uri: " << req.get_uri() << std::endl;std::string body = "<html><body><h1>Hello World</h1></body></html>";//conn->set_body(body);//conn->append_header("Content-Type", "text/html");conn->set_body(conn->get_request_body());conn->set_status(websocketpp::http::status_code::ok);wsserver_t::timer_ptr tp = srv->set_timer(5000, std::bind(print, "ive"));tp->cancel();//定时任务的取消,会导致定时任务立即被执行
}
void wsopen_callback(wsserver_t *srv, websocketpp::connection_hdl hdl) {std::cout << "websocket握手成功!!\n";
}
void wsclose_callback(wsserver_t *srv, websocketpp::connection_hdl hdl) {std::cout << "websocket连接断开!!\n";
}
void wsmsg_callback(wsserver_t *srv, websocketpp::connection_hdl hdl, wsserver_t::message_ptr msg) {wsserver_t::connection_ptr conn = srv->get_con_from_hdl(hdl);std::cout << "wsmsg: " << msg->get_payload() << std::endl;std::string rsp = "client say: " + msg->get_payload();conn->send(rsp, websocketpp::frame::opcode::text);
}
int main()
{//1. 实例化server对象wsserver_t wssrv;//2. 设置日志等级wssrv.set_access_channels(websocketpp::log::alevel::none);//3. 初始化asio调度器wssrv.init_asio();wssrv.set_reuse_addr(true);//4. 设置回调函数wssrv.set_http_handler(std::bind(http_callback, &wssrv, std::placeholders::_1));wssrv.set_open_handler(std::bind(wsopen_callback, &wssrv, std::placeholders::_1));wssrv.set_close_handler(std::bind(wsclose_callback, &wssrv, std::placeholders::_1));wssrv.set_message_handler(std::bind(wsmsg_callback, &wssrv, std::placeholders::_1, std::placeholders::_2));//5. 设置监听端口wssrv.listen(8085);//6. 开始获取新连接wssrv.start_accept();//7. 启动服务器wssrv.run();return 0;
}

(五)JsonCpp使用

Json数据格式
Json 是⼀种数据交换格式,它采用完全独立于编程语言的文本格式来存储和表示数据。
例如: 我们想表示⼀个同学的学生信息:
C 表示:

char *name = "xx";
int age = 18;
float score[3] = {88.5, 99, 58};

Json 表示:

{"姓名" : "xx","年龄" : 18,"成绩" : [88.5, 99, 58]
}
[{"姓名":"⼩明", "年龄":18, "成绩":[23, 65, 78]},{"姓名":"⼩红", "年龄":19, "成绩":[88, 95, 78]} 
]

Json 的数据类型包括对象,数组,字符串,数字等。
对象:使用花括号 {} 括起来的表示一个对象
• 数组:使用中括号 [] 括起来的表示一个数组
• 字符串:使用常规双引号 “” 括起来的表示一个字符串
• 数字:包括整形和浮点型,直接使用

Jsoncpp 库主要是用于实现 Json 格式数据的序列化和反序列化,它实现了将多个数据对象组织成为 json 格式字符串,以及将 Json 格式字符串解析得到多个数据对象的功能。先看⼀下 Json 数据对象类的表示:

1 class Json::Value {
2 Value &operator=(const Value &other); //Value重载了[]和=,因此所有的赋值和获取
数据都可以通过
3 Value& operator[](const std::string& key);//简单的⽅式完成 val["name"] = 
"xx";
4 Value& operator[](const char* key);
5 Value removeMember(const char* key);//移除元素
6 const Value& operator[](ArrayIndex index) const; //val["score"][0]
7 Value& append(const Value& value);//添加数组元素val["score"].append(88); 
8 ArrayIndex size() const;//获取数组元素个数 val["score"].size();
9 bool isNull(); //⽤于判断是否存在某个字段
10 std::string asString() const;//转string string name = 
val["name"].asString();
11 const char* asCString() const;//转char* char *name = 
val["name"].asCString();
12 Int asInt() const;//转int int age = val["age"].asInt();
13 float asFloat() const;//转float float weight = val["weight"].asFloat();
14 bool asBool() const;//转 bool bool ok = val["ok"].asBool();
15 
};

Jsoncpp 库主要借助三个类以及其对应的少量成员函数完成序列化及反序列化

  • 序列化接口
1 class JSON_API StreamWriter {
2 virtual int write(Value const& root, std::ostream* sout) = 0;
3 }
4 class JSON_API StreamWriterBuilder : public StreamWriter::Factory {
5 virtual StreamWriter* newStreamWriter() const;
6 }
  • 反序列化接口
1 class JSON_API CharReader {
2 virtual bool parse(char const* beginDoc, char const* endDoc, 
3 Value* root, std::string* errs) = 0;
4 }
5 class JSON_API CharReaderBuilder : public CharReader::Factory {
6 virtual CharReader* newCharReader() const;
7 }
  • JsonCpp功能代码⽤例编写
#include <iostream>
#include <sstream>
#include <vector>
#include <string>
#include <jsoncpp/json/json.h>//使用jsoncpp库进行多个数据对象的序列化
std::string serialize() {//1. 将需要进行序列化的数据,存储在Json::Value 对象中Json::Value root;root["姓名"] = "小明";root["年龄"] = 18;root["成绩"].append(98);root["成绩"].append(88.5);root["成绩"].append(78.5);//2. 实例化一个StreamWriterBuilder工厂类对象Json::StreamWriterBuilder swb;//3. 通过StreamWriterBuilder工厂类对象生产一个StreamWriter对象Json::StreamWriter *sw = swb.newStreamWriter();//4. 使用StreamWriter对象,对Json::Value中存储的数据进行序列化std::stringstream ss;int ret = sw->write(root, &ss);if (ret != 0) {std::cout << "json serialize failed!!\n";return "";}std::cout << ss.str() << std::endl;delete sw;return ss.str();
}void unserialize(const std::string &str)
{//1. 实例化一个CharReaderBuilder工厂类对象Json::CharReaderBuilder crb;//2. 使用CharReaderBuilder工厂类生产一个CharReader对象Json::CharReader *cr = crb.newCharReader();//3. 定义一个Json::Value对象存储解析后的数据Json::Value root;std::string err;//4. 使用CharReader对象进行json格式字符串str的反序列化// parse(char *start,  char *end,  Json::Value *val,  string *err);bool ret = cr->parse(str.c_str(), str.c_str() + str.size(), &root, &err);if (ret == false) {std::cout << "json unserialize failed: " << err << std::endl;return ;}//5. 逐个元素去访问Json::Value中的数据std::cout << "姓名:" << root["姓名"].asString()  << std::endl;std::cout << "年龄:" << root["年龄"].asInt()  << std::endl;int sz = root["成绩"].size();for (int i = 0; i < sz; i++) {std::cout << "成绩: " << root["成绩"][i].asFloat() << std::endl;}delete cr;
}
int main()
{std::string str = serialize();unserialize(str);return 0;
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/114171.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

动画制作软件 Animate 2024 mac中文版介绍说明(an2024) v24.0

Animate 2024 mac是一款动画制作软件&#xff0c;它能帮助用户轻松制作出各种精美的动画作品。 Animate 2024拥有强大而直观的设计工作流程&#xff0c;能够让用户自由地构建动画场景、绘制精美的图形&#xff0c;并轻松添加动态效果。无论是传统手绘风格还是骨骼动画&#xff…

【Lua语法】字符串

Lua语言中的字符串是不可变值。不能像在C语言中那样直接改变某个字符串中的某个字符&#xff0c;但是可以通过创建一个新字符串的方式来达到修改的目的 print(add2(1 , 2 ,15,3))a "no one"b string.gsub(a , "no" , "on1111")print(a) print…

第87步 时间序列建模实战:LSTM回归建模

基于WIN10的64位系统演示 一、写在前面 这一期&#xff0c;我们介绍大名鼎鼎的LSTM回归。 同样&#xff0c;这里使用这个数据&#xff1a; 《PLoS One》2015年一篇题目为《Comparison of Two Hybrid Models for Forecasting the Incidence of Hemorrhagic Fever with Renal…

Compose Desktop 使用中的几个问题(分平台加载资源、编写Gradle 任务下载平台资源、桌面特有组件、鼠标键盘事件)

前言 在我之前的文章 Compose For Desktop 实践&#xff1a;使用 Compose-jb 做一个时间水印助手 中&#xff0c;我们使用 Compose For Desktop 写了一个用于读取照片 EXIF 中的拍摄日期参数并以文字水印的方式添加到照片上的桌面程序。 但是事实上&#xff0c;这个程序的名字…

从入门到进阶 之 ElasticSearch SpringData 继承篇

&#x1f339; 以上分享 从入门到进阶 之 ElasticSearch SpringData 继承篇&#xff0c;如有问题请指教写。&#x1f339;&#x1f339; 如你对技术也感兴趣&#xff0c;欢迎交流。&#x1f339;&#x1f339;&#x1f339; 如有需要&#xff0c;请&#x1f44d;点赞&#x1f…

PHP数据加密传输和存储问题

PHP数据加密的类型 md5()&#xff0c;sha1()&#xff0c;crypt() 双md5加密加盐

AAPCS:最新的ARM子程序调用规则

AAPCS是arm公司发布的ARM架构应用程序二进制&#xff08;ABI&#xff09;程序调用接口&#xff0c;该文档由多个版本&#xff0c;博主第一次ARM程序调用规则是在《ARM体系与结构编程》&#xff0c;但书中描述的是ATPCS&#xff0c;AAPCS是ATPCS的升级版。后面去ARM官网看到了AA…

自然语言处理基础——词表示

词表示 把自然语言中最基本的语言单元——词转换为机器能够理解的 词表示能完成以下两个能力 词相似度计算 词与词之间语义的关系 近义词&上位词 使用近义词或上位词表示的问题 遗漏差异 遗漏新的释义 带有主观性 数据吸收 需要大量人工构建 One-Hot Representation …

Kafka学习(最新版3.6.0)

文章目录 一、初识MQ1.1 什么是MQ1.2 同步和异步通讯1.1.1 同步通讯1.1.2 异步通讯 1.3 技术对比1.4 MQ的两种模式 二、初识Kafka2.1 Kafka的使用场景2.2 Kafka基本概念2.3 Topic与Partition 三、Kafka基本使用3.1 部署前的准备3.2 启动kafka服务器3.3 Kafka核心概念之Topic3.4…

059:mapboxGL监听键盘事件,通过eastTo控制左右旋转

第059个 点击查看专栏目录 本示例是介绍演示如何在vue+mapbox中监听键盘事件,通过eastTo控制左右旋转。 本例通过easeTo方法来加减一定数值的bearing角度,通过.addEventListener的方法来监听键盘的按键动作。这里一定要设置interactive: false, 否则展现不出来旋转效果。 直…

机械设备经营小程序商城的作用是什么

由于机械设备厂商品牌需要各地招商代理&#xff0c;因此在管理方面也需要工具进行高效管理。如今各个行业都在开展数字化转型解决行业所遇难题或通过线上销售解决传统三公里难题及品牌扩张难题、用户消费渠道少等难题&#xff0c;构建会员体系精细化管理&#xff0c;同时还需要…

MySQL 主从复制原理

文章目录 1.主从复制方式1.1 异步复制1.2 半同步复制1.3 全同步复制 2.主从复制原理3.主从复制时推还是拉&#xff1f;参考文献 主从复制是 MySQL 高可用&#xff08;备份&#xff09;和高性能&#xff08;读写分离&#xff09;的基础&#xff0c;有了这个基础&#xff0c;MySQ…

【Ascend C算子开发(入门)】——Ascend C编程模式与范式

Ascend C编程模型与范式 1.并行计算架构抽象 Ascend C编程开发的算子是运行在AI Core上的&#xff0c;所以我们需要了解一下AI Core的结构。AI Core主要包括计算单元、存储单元、搬运单元。 计算单元包括了三种计算资源&#xff1a;Scalar计算单元&#xff08;执行标量计算&…

外骨骼机器人和人形机器人概览

前言&#xff1a;一点思考 外骨骼机器人和人形机器人都曾随着一些爆品的出现火热过一段时间&#xff0c;但总感觉当前技术条件还不成熟&#xff0c;真正能落地的应用场景不多。马斯克在擎天柱发布会上被问到人形机器人的落地与前景问题时并没有给出明确答案&#xff0c;只是用…

c++ pcl点云变换骨架枝干添加树叶源码实例

程序示例精选 c pcl点云变换骨架枝干添加树叶源码实例 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对《c pcl点云变换骨架枝干添加树叶源码实例》编写代码&#xff0c;代码整洁&#xff0c;…

自然语言处理---Tr ansformer机制详解之Transformer结构

1 Encoder模块 1.1 Encoder模块的结构和作用 经典的Transformer结构中的Encoder模块包含6个Encoder Block.每个Encoder Block包含一个多头自注意力层&#xff0c;和一个前馈全连接层. 1.2 Encoder Block 在Transformer架构中&#xff0c;6个一模一样的Encoder …

【JavaScript】深入浅出理解事件循环

1. 浏览器的进程模型 1.1 进程 程序运行需要有它自己专属的内存空间&#xff0c;可以把这块内存空间简单的理解为进程。 每个应用至少有一个进程&#xff0c;进程之间相互独立&#xff0c;即使要通信&#xff0c;也需要双方同意。 1.2 线程 有了进程后&#xff0c;就可以运…

【广州华锐互动】VR营销心理学情景模拟培训系统介绍

在高度竞争的汽车市场中&#xff0c;销售人员需要具备强大的专业知识、引人入胜的销售技巧&#xff0c;以及敏锐的市场洞察力。然而&#xff0c;传统的培训方式往往无法满足这些需求&#xff0c;因为它们往往忽略了实践的重要性。 为了解决这个问题&#xff0c;许多公司开始采用…

TCP/IP(十九)TCP 实战抓包分析(三)TCP 第一次握手 SYN 丢包

一 TCP 三次握手异常情况实战分析 说明&#xff1a; 本文是TCP 三次握手异常系列之一 ① 异常场景 接下里我用三个实验案例,带大家一起探究探究这三种异常关注&#xff1a; 如何刻意练习模拟上述场景 以及 wireshark现象 ② 实验环境 ③ 实验一&#xff1a;TCP 第一次握…

Python —— UI自动化之使用JavaScript进行元素点亮、修改、点击元素

1、JavaScript点亮元素 在控制台通过JavaScript语言中对元素点亮效果如下&#xff1a; 将这个语句和UI自动化结合&#xff0c;代码如下&#xff1a; locator (By.ID,"kw") # 是元组类型 web_element WebDriverWait(driver,5,0.5).until(EC.visibility_of_eleme…