【五子棋】
五子棋
文章目录
- 五子棋
- 前言
- 一、登录功能
- 二.哈希表管理用户的会话和房间
- 三.基于Websocket连接开发的功能
- 1.匹配功能
- 2.游戏房间
- 3.挑战功能
- 4.人机对战
- 5.聊天功能
前言
这篇博客主要详细介绍我的五子棋项目的核心功能的实现细节,也就是详细介绍五子棋各个功能是如何实现前后端交互的。
由于该项目对数据要求做实时性的发送和接收,所以大部分的API都是基于websocket来实现的。
并且我会按照我在开发该项目时的思路来写这篇博客,比如:在开发一个匹配功能时,我会先约定好前后端的交互发送请求的接收响应的接口详情,然后就是编写前端和后端代码。
一、登录功能
功能详情:不支持游客,必须登录才能进行游戏。
思路分析:数据库有张用户表,记录着用户的信息,登录功能就是通过前端发送数据,然后在后端根根据用户名查表并且进行密码校验,考虑到该项目只是一个小游戏,所以没有对密码采用MD5+盐算法.
1.约定前后端交互接口
前端发送的http请求我设计这个样子:
后端处理完请求之后返回的响应,我设计成这个样子:
2.编写前端代码
这里解释一下,这里只是贴出了前端代码的核心内容,就是通过一个点击事件触发mysub()这个函数,然后给后端发送ajax请求.
function mysub(){// 1.非空效验var username = jQuery("#username");var password = jQuery("#password");if(username.val()==""){alert("请先输入用户名!");username.focus();return;}if(password.val()==""){alert("请先输入密码!");password.focus();return;}// 2.ajax 请求登录接口jQuery.ajax({url:"/user/login",type:"POST",data:{"username":username.val(),"password":password.val()},success:function(result){if(result!=null && result.code==200 && result.data!=null){// 登录成功location.href = "game_hall.html";// location.replace("/game_hall.html")}else{alert("抱歉登录失败,用户名或密码输入错误,请重试!");}}});}
3.后端代码
由于这是一个ssm项目,所以我先解释一下项目的目录结构:
这里我就贴出我controller层的一段代码:
@RequestMapping("/login")public User login(String username, String password){//参数非空校验if (username == null || password == null){AjaxResult.failed(-1,"登录失败",null);}//获取user表里username对应的密码User user =userService.getUserByusername(username);
// if (user == null || !password.equals(user.getPassword())){
// return AjaxResult.failed(-1,"登录失败",null);
// }//创建session会话HttpSession session = request.getSession(true);session.setAttribute(AppVariable.USER_SESSION_KEY,user);//屏蔽敏感信息user.setPassword(null);
// return AjaxResult.success("登录成功",user);return user;}
更多的细节,可以通过码云访问我的码云项目源代码
然后注册功能和游戏大厅页面获取登录用户信息功能区别都不大,就是普通的进行数据库的插入和查询操作,这里不再赘述了.
提一点,我这里使用AOP面向切面的思想对登录功能做了拦截器和统一数据返回格式:
拦截器类:
public class LoginInterceptor implements HandlerInterceptor {/*** 返回true,说明以登录* 返回false:未登录 跳转到登录页面*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {HttpSession session = request.getSession(false);if(session != null && session.getAttribute(AppVariable.USER_SESSION_KEY)!=null){//打印会话对象里的用户名User user = (User) session.getAttribute(AppVariable.USER_SESSION_KEY);System.out.println("拦截器,当前用户会话:"+user.getUsername());return true;}response.sendRedirect("/login.html");return false;}
}
注册里拦截器并设置拦截路由:
@Configuration
public class WebConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**")//静态页面.excludePathPatterns("/login.html").excludePathPatterns("/css/**").excludePathPatterns("/img/**").excludePathPatterns("/js/**").excludePathPatterns("/register.html").excludePathPatterns("/TestAPI.html")//接口.excludePathPatterns("/user/login").excludePathPatterns("/user/reg");}
}
统一数据返回格式:
自定义返回的数据格式:
public class AjaxResult {private Integer code;private String msg;private Object data;/*** 成功的完成一次请求* @param data* @return*/public static AjaxResult success(Object data){AjaxResult ajaxResult = new AjaxResult();ajaxResult.setCode(200);ajaxResult.setMsg("请求成功");ajaxResult.setData(data);return ajaxResult;}public static AjaxResult success(String msg,Object data){AjaxResult ajaxResult = new AjaxResult();ajaxResult.setCode(200);ajaxResult.setMsg(msg);ajaxResult.setData(data);return ajaxResult;}/*** 没有成功的返回一次请求* @param code 根据不同的情况自定义 -1.-2* @param msg 根据不同的情况自定义:用户名为空....* @param data 根据不同的情况自定义:null* @return*/public static AjaxResult failed(Integer code,String msg,Object data){AjaxResult ajaxResult = new AjaxResult();ajaxResult.setCode(code);ajaxResult.setMsg(msg);ajaxResult.setData(data);return ajaxResult;}public static AjaxResult failed(Integer code,String msg){AjaxResult ajaxResult = new AjaxResult();ajaxResult.setCode(code);ajaxResult.setMsg(msg);return ajaxResult;}
}
返回数据前对数据格式进行统一处理:
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {@AutowiredObjectMapper objectMapper;//用于处理字符串/*** 开关,为true才会执行beforeBodyWrite方法* @param returnType* @param converterType* @return*/@Overridepublic boolean supports(MethodParameter returnType, Class converterType) {return true;}/*** 在response返回给前端之前会执行的方法,参数只关注body* @param body* @param returnType* @param selectedContentType* @param selectedConverterType* @param request* @param response* @return*/@SneakyThrows@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {//如果body已经被封装成AjaxResult了,直接返回if(body instanceof AjaxResult){return body;}//如果body是string类型,得使用jackson序列化if(body instanceof String){return objectMapper.writeValueAsString(AjaxResult.success(body));}//如果没被封装,就调用success方法封装一下System.out.println("这里对响应进行了统一的封装处理");return AjaxResult.success(body);}
}
# 二、使用步骤
## 1.引入库
>代码如下(示例):```c
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
二.哈希表管理用户的会话和房间
使用了多张哈希表来管理userId和session对应的关系,roomId和room的对应关系,userId与room的对应关系,极大的简化的后序的开发。
三.基于Websocket连接开发的功能
1.匹配功能
功能详情: 在线用户通过"随机匹配",匹配到在线玩家进行五子棋对局.
思路分析:创建一系列的匹配队列,把根据分数梯段把触发"匹配功能"的用户放到同一个匹配队列里,然后把两个用户从队列里拿出来进行一句游戏。
队列具有"先进先出"的特性,这有就能简单实现“谁先来就开始游戏”的随机性。
当两名玩家比配成功之后,就安排两名玩家进入游戏。
1.约定前后端交互接口
2.前端代码:
创建websocket对象,点击事件触发send()函数给服务器发送‘’匹配请求‘’和"停止匹配请求",websocket.onmessage根据约定的前后端交互接口来处理逻辑。
```javascript//在此处进行初始化websocket,并且实现前端的匹配逻辑//此处的路径必须写作是 /findmatch,千万不要写作 /findmatch/let websocketurl = 'ws://'+location.host+'/findmatch'//location.host根据浏览器的html页面获取到服务器的ip和端口let websocket = new WebSocket(websocketurl)//ws://就是websocket协议名//回调函数websocket.onopen = function(){console.log('[MatchAPI]连接成功')}websocket.onerror = function () {console.log('[MatchAPI]出现异常')}//服务器websocket.onclose = function(){console.log('[MatchAPI]连接失败')}//监听页面关闭事件(前端页面关闭),在页面关闭(离开已经建立websocket连接的页面)之前,手动调用这里的websocket的close方法window.onbeforeunload = function(){websocket.close();}//重点实现,处理服务器返回的响应websocket.onmessage = function(e){//处理服务器返回的响应数据,这个响应就是针对:"开始匹配"/"停止匹配"来对应的//解析得到的响应对象,返回的数据是一个JSON字符串,先解析成 js 对象let resp = JSON.parse(e.data);if(!resp.ok){//ok不为true//有新用户上上线,调用if(resp){console.log("有新用户上线/下线,调用top10()");top10();return;}console.log("游戏大厅中接收到了失败响应" + resp.reason);return;}//响应的三种情况if(resp.message == 'startMatch'){//开始匹配请求发送成功console.log('进入匹配队列成功!');//修改button标签内容为 正在匹配中...(点击停止)matchButton.innerHTML='正在匹配中...(点击停止)';}else if(resp.message == 'stopMatch'){//停止匹配请求发送成功console.log('退出匹配队列成功!');//修改button标签内容为 开始匹配matchButton.innerHTML = '开始匹配';}else if(resp.message == 'matchSuccess'){//已经匹配到对手console.log('匹配到对手,进入游戏房间');location.assign('/game_room.html');// location.replace('/game_room.html')}else if(resp.message == 'repeatConnection'){//检查到多开console.log('检测到多开!当前用户已经在其他设备上登录!');location.assign('/login.html');//使用replace,浏览器不会将页面入栈保存// location.replace('/login.html')}else{//这三种情况之外的情况,认为是异常的情况console.log('收到了非法的响应! message='+resp.message);}}//点击按钮,给服务器发送匹配请求function send(){//在触发 websocket 请求之前,先确认一下 websocket 连接是否正常if(websocket.readyState == websocket.OPEN){//当readyState处在OPNE状态,说明连接是正常的//这里发送数据有两种可能: 1.开始匹配 2.停止匹配if (matchButton.innerHTML == '开始匹配'){//发送开始匹配的请求console.log('开始匹配');//发送json格式的请求websocket.send(JSON.stringify({message: 'startMatch',}));}else if(matchButton.innerHTML == '正在匹配中...(点击停止)'){//发送停止匹配的请求console.log('停止匹配')websocket.send(JSON.stringify({message:'stopMatch',}));}}else{//if没进入,到这里说明连接是不正常的alert('当前您的连接已断开!请重新登录!');location.assign('/login.html');alert("")}}
3.后端代码:
后端代码比较多,不是核心的我就不贴了,我在这里描述一下逻辑:
匹配的核心是Match类,这个类里创建了匹配队列,扫描匹配队列的线程和匹配函数:
首先是创建匹配队列:
然后创建多线程持续扫描队列:调用handleMatch()方法
handleMatch()方法
总结:点击匹配的用户会被加入到匹配队列里,不同分段匹配队列全局只需要一份,spring项目支持多线程所以存在线程安全问题,使用synchronized,匹配队列有线程在一直扫描该队列,为了保证cpu资源不被浪费是用来wait和notify,当队列里玩家少于2时,线程不用继续扫描了,等待该队列添新的用户时唤醒线程继续扫描,当队列元素为偶数时,可以匹配出两名玩家,并且为他们创建游戏房间并返回匹配成功的响应。
具体源代码:码云项目源代码
2.游戏房间
功能需求:进行五子棋的游戏。
思路分析:进入游戏房间的用户可以进行五子棋对局,前端使用画canvasAPI画布画出棋盘,然后在二维坐标上画子。前端点击棋盘的二维坐标,给后端发送落子响应(row:哪行,col:那列)给的后端,后端根据请求来判断对局是否结结束,然后将响应返回给两个前端,前端在画子。
1.约定前后端交互接口
websocket连接
ws://127.0.0.1:8080/game
连接响应:
{message: 'gameReady', // 游戏就绪ok: true, // 是否成功. reason: '', // 错误原因roomId: 'abcdef', // 房间号. 用来辅助调试. thisUserId: 1, // 玩家自己的 idthatUserId: 2, // 对手的 idwhiteUser: 1, // 先手方的 id
}
落子请求:
{message: 'putChess',userId: 1,row: 0,col: 0
}
落子响应:
{message: 'putChess',userId: 1,row: 0,col: 0
}
2.前端页面
核心:用 onclick 来处理用户点击事件. 当用户点击的时候通过这个函数来控制绘制棋子.
前端匹配成功后跳转到game_room.html页面
chess.onclick = function (e) {if (over) {return;}if (!me) {return;}let x = e.offsetX;let y = e.offsetY;// 注意, 横坐标是列, 纵坐标是行let col = Math.floor(x / 30);let row = Math.floor(y / 30);if (chessBoard[row][col] == 0) {// 发送坐标给服务器, 服务器要返回结果send(row, col);// 留到浏览器收到落子响应的时候再处理(收到响应再来画棋子)// oneStep(col, row, gameInfo.isWhite);// chessBoard[row][col] = 1;}}
在 initGame 之前, 处理的是游戏就绪响应, 在收到游戏响应之后, 就改为接收落子响应了.
me 变量用来表示当前是否轮到我落子. over 变量用来表示游戏结束.
websocket.onmessage = function (event) {console.log('handlerPutChess: ' + event.data);let response = JSON.parse(event.data);if (response.message != 'putChess') {console.log('响应类型错误!');return;}// 1. 判断 userId 是自己的响应还是对方的响应, // 以此决定当前这个子该画啥颜色的if (response.userId == gameInfo.thisUserId) {oneStep(response.col, response.row, gameInfo.isWhite);} else if (response.userId == gameInfo.thatUserId) {oneStep(response.col, response.row, !gameInfo.isWhite);} else {console.log('[putChess] response userId 错误! response=' + JSON.stringify(response));return;}chessBoard[response.row][response.col] = 1;me = !me; // 接下来该下个人落子了. // 2. 判断游戏是否结束if (response.winner != 0) {// 胜负已分if (response.winner == gameInfo.thisUserId) {alert("你赢了!");} else {alert("你输了");}// 如果游戏结束, 则关闭房间, 回到游戏大厅. location.assign('/game_hall.html')}// 3. 更新界面显示setScreenText(me);
}
3后端:
先引入了Room和RoomManager类,Room类用来进行一局游戏RoomManager来管理Room。
Room类,在匹配成功和挑战成功创建。
RoomManager类,用来管理room
GameAPI类websocket连接,处理游戏房间的逻辑
在两名玩家成功跳转到game——room页面后,再将两名玩家安排进房间,且先连接到后端的玩家被设置为先手
handleTextMessage来处理前端的落子请求
putchess落子和判断胜负,然后将落子响应返回给前端
总结:游戏房间负责游戏进行,在匹配成功和挑战匹配之后,会创建room,在前端收到“gameReady”之后会跳转到game_room,这个时又会建立websocket连接,将玩家安排进房间里面,同时返回‘’gameReady‘’个前端,聊天功能的websocket连接也会用到这个“gameReady”。
具体源代码:码云项目源代码
3.挑战功能
功能需求:存在天梯积分top10排行榜,用户可以挑战排行榜上的在线用户,当然被挑战者也可以拒绝挑战。
思路分析:
当前挑战者是lisi,如果他想挑zahnsgan,点击“挑战”需要给zhangsan发送挑战申请:
zhangsan得能看到lisi的个人数据,然后选择接受/拒绝挑战
这就需要在点击“挑战”时,可以从前端获取到“挑战者”和“被挑战者”的信息,然后在后端将挑战请求转发给“被挑战者”,“被挑战者”的接受/拒绝也得通过后端转发给“挑战者”。
1.约定前后端交互接口
挑战者发出的请求
被挑战者受到的响应:
挑战者收到的响应:
2.前端代码
top10获取到分数前10的玩家,动态拼接前端标签。
function top10(){jQuery.ajax({type: 'get',url: 'user/top10',data:{},success: function(result){//获取父标签scoreboard_listlet scoreboard_listDiv = document.querySelector('.scoreboard_list');if(result!=null && result.code==200 && result.data.length>0){// 清空父标签的子元素scoreboard_listDiv.innerHTML = '';//for(var i= 0;i< result.data.length;i++){//创建scoreboard_list_child标签let scoreboard_list_childDiv = document.createElement('div');// //标签设置classscoreboard_list_childDiv.class = 'scoreboard_list_child';//每一个玩家信息都在list里面let usertop = result.data[i];//判断用户当前的状态let state = ''if(usertop.state == 'no'){state = '离线';}else if(usertop.state == 'yes'){state = '在线';}else if(usertop.state == 'busy'){state = '正在游戏中'}let userName = usertop.username;let userState = usertop.state;//拼标签scoreboard_list_childDiv.innerHTML =' <div class="scoreboard_list_child">\n' +' <div class="rank">'+(i+1)+'</div>'+' <div class="username">'+usertop.username+'</div>'+'<div class="score">'+usertop.score+'</div>'+' <div class="state">'+state+'</div>'+ ' <button onClick="myfun(' + usertop.userId + ', \'' + userName + '\', \'' + userState + '\');challenge();" class="click" style="color:rgb(255, 133, 127);">挑战Ta </button>\n'+ '<div class="userid">'+usertop.userId+'</div>\n'+' </div>';////子标签插入到父标签scoreboard_listDiv.appendChild(scoreboard_list_childDiv);}}else{alert("用户信息异常!!!");}}})}
注意top10的调用时时机:
因为在游戏中的用户和离线的用户都不能被挑战,所以得确保积分榜上的用户数据是实时更新的,所以得在后端处理一下:当有用户在游戏大厅页面上下线时,当用户在进入/退出游戏页面时都调用一下top10,保证top10的数据是最新的
用户在游戏大厅上线
用户在游戏大厅下线
用户在游戏房间上线
用户在游戏大厅下线
前端判断调用top10
被挑战者接受挑战,挑战者跳转到游戏房间页面
被挑战者点击“接受挑战”会跳转到游戏房间页面
3后端代码
总结:挑战功能实际上是将两个与服务器websocket连接的玩家放入到同一个room里进行游戏,所以关键在于如何通过服务器把客户端的两个websocket连接的用户安排在一起,这就需要在发起挑战请求时就获取到挑战者和被挑战者的信息,这样在后端就可以通过userId,把两个用户的session关联在一起,就是在服务器与不同的前端存在大量的websocket连接下如何连接两个客户端的websocket连接。
具体源代码:码云项目源代码
4.人机对战
功能需求:可以挑战AI
思想分析:点击匹配按钮直接进入游戏房间,并且AI先手。
这里直接建立在ai游戏页面aigame的websocket连接,点击人机对战之后直接跳转ai游戏页面,AI先下手,人机的落子都在后端。
1.约定前后端交互接口
人机对战请求:
落子请求和响应
2.前端代码
前端注意以下over为true时不能在触发点击事件在棋盘上落子了。
3后端代码
AIGame里封装了人机对战的所有逻辑,包括:落子,打印棋盘,判断胜负,AI算生成落子坐标。
多线程环境下,每个玩家的人机对战得有独一无二的棋盘,不同的玩家之间不能相互影响,所以使用哈希表记录了每一个用户自己的AIGame
算法的用的是五元组和一个积分统统计表,该算法会计算出棋盘上每个空位子处的分数,分数越高说明能赢的概率就越大,因此会根据棋盘上已有棋子,在分数在高的空位上落子。
总结:双人对局的简化版,在后端要处理好AI落子,然后在后端生成落子请求多用putchess,AI先落子,有个细节要处理一下:由于玩家发送一个落子请求之后,前端得收到玩家落子响应和AI落子响应,而玩家获胜之后,AI可以不用在继续落子了。
具体源代码:码云项目源代码
5.聊天功能
功能需求:在游戏界面里实时聊天。
思想分析:就是弄一个websocket连接,然后后端在转发消息时,可以精准的找到发送方和接收方的session进行转发即可。
1.前后端接口交互
2.前端代码
3.后端代码
总结:为了使前端发送的数据跟完整:有发送方和接收方,使用了“gameReady”里的数据,在后端进行转发时,借助了onlineUserManager和 roomManager来找消息接收方的会话。
具体源代码:码云项目源代码
相关文章:
【五子棋】
五子棋 文章目录 五子棋前言一、登录功能二.哈希表管理用户的会话和房间三.基于Websocket连接开发的功能1.匹配功能2.游戏房间3.挑战功能4.人机对战5.聊天功能 前言 这篇博客主要详细介绍我的五子棋项目的核心功能的实现细节,也就是详细介绍五子棋各个功能是如何实…...
docker 01(初识docker)
一、docker概念 Docker是一个开源的应用容器引擎;诞生于2013年初,基于Go 语言实现,dotCloud公司出品(后改名为Dockerlnc);Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的Linux …...
爬虫逆向实战(十九)--某号站登录
一、数据接口分析 主页地址:某号站 1、抓包 通过抓包可以发现登录接口 2、判断是否有加密参数 请求参数是否加密? 通过查看“载荷”模块可以发现有一个jsondata_rsa的加密参数 请求头是否加密? 无响应是否加密? 无cookie是否…...
linux环境docker安装mysql
1:docker拉取mysql镜像(可有自己选择mysql版本) [rootlocalhost ~]# docker pull mysql:8.02: Docker 中启动 MySQL 容器,可以使用以下命令: docker run -d --name mysql_container -v ./sql:/docker-…...
taro h5 formData上传图片的坑-Required request part ‘file‘ is not present
描述:用formData上传图片 1、生成formData const formData new FormData() formData.append(file, data) // data是file formData.append(xxx, xxx) // 添加其他参数2、用taro.request请求 Taro.request({url: xxxx,data: formData,header: {Content-Type: mult…...
GNU GRUB version 2.06 Minimal Bash-lke line editing is supported 问题修复
一、问题背景 博主喜欢折腾系统,电脑原来有一个windows系统,想整一个Linux双系统,结果开机时出现以下画面: GNU GRUB version 2.06 Minimal Bash-lke line editing is supported. TAB lists possible comand completions, Anywh…...
Embedding 向量生成GPT数据使用相关
如果使用python3.6的版本,使用pycharm创建工程,那么默认会使用 docx包,这样运行程序会爆异常,突然想起以前请教的一个大神,想当 初,这个问题困扰了我 两天时间,在此记录一下: pytho…...
Jenkins工具系列 —— 配置邮箱 每个job下动态设置临时发送人
文章目录 安装插件添加邮箱认证邮箱申请(以QQ邮箱网页为例)jenkins添加邮箱认证 jenkins设置邮箱相关信息配置全局邮件单个JOB邮箱配置 安装插件 点击 左侧的 Manage Jenkins —> Plugins ——> 左侧的 Available plugins 添加邮箱认证 邮箱申请…...
华纳云:ubuntu中怎么查看进程占用的端口
在Ubuntu中,你可以使用以下命令来查看进程占用的端口: 打开终端(Terminal)。 使用 netstat 命令来查看进程占用的端口。以下是几个常用的命令示例: 查看所有进程占用的端口和地址: netstat -tuln 查看TCP端…...
【学会动态规划】 最长递增子序列(26)
目录 动态规划怎么学? 1. 题目解析 2. 算法原理 1. 状态表示 2. 状态转移方程 3. 初始化 4. 填表顺序 5. 返回值 3. 代码编写 写在最后: 动态规划怎么学? 学习一个算法没有捷径,更何况是学习动态规划, 跟我…...
Azure虚拟网络对等互连
什么是Azure虚拟网络对等互联 Azure虚拟网络对等互联(Azure Virtual Network peering)是一种连接两个虚拟网络的方法,使得这两个虚拟网络能够在同一地理区域内进行通信。它通过私有IP地址在虚拟网络之间建立网络连接,不论是在同一…...
CTFhub-sql-整数注入
判断存在 sqli 注入 1 1 and 11 1 and 12 因为 11 为真,12 为假,且 11 与 1 显示的数据一样,那么就存在 sqli 注入 查询该数据表的字段数量 一、 2 3 1,2成功带出数据,3没有数据,所以有两个字段 二、 1 order by …...
管理类联考——逻辑——真题篇——按知识分类——汇总篇——二、论证逻辑——归纳——第三节 归纳论证有效性
文章目录 第三节 归纳论证有效性真题(2007-37)——归纳——归纳论证有效性——两面验证法真题(2000-60)——归纳——归纳论证有效性——构造对照组实验真题(2001-44)——归纳——归纳论证有效性——寻找针对该缺陷的选项第三节 归纳论证有效性 真题(2007-37)——归纳—…...
PaddleRS 1.0.0版本安装
PaddleRS 1.0.0版本安装 PaddleRS是百度飞桨、遥感科研院所及相关高校共同开发的基于飞桨的遥感影像智能解译开发套件, 支持图像分割、目标检测、场景分类、变化检测、图像复原等常见遥感任务。 PaddleRS致力于帮助遥感领域科研从业者快速完成算法的研发、验证和调…...
三维重建 PyQt Python MRP 四视图(横断面,冠状面,矢状面,3D)
本文实现了 Python MPR 的 四视图,横断面,冠状面,矢状面,3D MPR(multi-planner reformation)也称多平面重建,多重面重建是将扫描范围内所有的轴位图像叠加起来再对某些标线标定的重组线所指定的组织进行冠状、矢状位、…...
使用vscode编写插件-php语言
https://blog.csdn.net/qq_45701130/article/details/125206645 一、环境搭建 1、安装 Visual Studio Code 2、安装 Node.js 3、安装 Git 4、安装生产插件代码的工具:npm install -g yo generator-code 二、创建工程 yo code 选择项解释: 选择编写扩…...
【笔记】Spark3 AQE(Adaptive Query Execution)
提效 7 倍,Apache Spark 自适应查询优化在网易的深度实践及改进 Performance Tuning 配置Spark SQL开启Adaptive Execution特性 How To Use Spark Adaptive Query Execution (AQE) in Kyuubi 【spark系列3】spark 3.0.1 AQE(Adaptive Query Exection)分析 玩转Spark…...
【雷达】接收和去噪L波段雷达接收到的信号研究(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
【云原生】Docker Cgroups资源控制管理
目录 一、cgroups简介 cgroups有四大功能: 二、cpu时间片的概念 三、对CPU使用的限制 3.1 设置CPU使用率上限 (1)查看容器的默认CPU使用限制 (2)进行压力测试 (3)创建容器时设置CPU使用时…...
k8s部署prometheus
1、prometheus部署yml文件地址 github地址 2、下载yml文件 rootiZj6cd9joygowsf7am5hryZ:~# git clone https://github.com/redhatxl/k8s-prometheus-grafana.git Cloning into k8s-prometheus-grafana... remote: Enumerating objects: 21, done. remote: Total 21 (delta 0)…...
飞书小程序开发
1.tt.showModal后跳转页面 跳转路径要为绝对路径,相对路径跳转无响应。 2.手机息屏后将不再进入onload()生命周期,直接进入onshow()生命周期。 onLoad()在页面初始化的时候触发,一个页面只调用一次。 onShow()在切入前台时就会触发&#x…...
Revit 3D高效处理:cad exchanger sdk 3.21 Crack
3D 格式概述:Revit Revit 已成为寻求高效、准确的建筑信息建模的专业人士的首选解决方案。在这篇引人入胜的功能概述中了解 Revit 的特性和影响。 什么是Revit? Autodesk Revit 是一款流行的 CAD 软件,重点关注 BIM,被建筑师、工…...
【已解决】Linux中启动docker 出现 ‘ Failed to start docker.service: Unit not found. ’ 错误
启动docker 出现 ‘ Failed to start docker.service: Unit not found. ’ 错误 这是因为缺少 rhel-push-plugin.socket 单元,该单元是rhel-push-plugin软件包的一部分。所以我们执行以下指令就可以成功解决: curl -sSL https://get.docker.com/ | sh 执…...
【学习日记】【FreeRTOS】时间片的实现
前言 本文以野火的教程和代码为基础,对 FreeRTOS 中时间片的概念作了解释,并且给出了实现方式,同时发现并解决了野火教程代码中的 bug。 一、时间片是什么 在前面的文章中,我们已经知道任务根据不同的优先级被放入就绪列表中不…...
CentOS Docker仓库和代理配置
无法直接访问外部网络时,除了Host自己的全局代理设置之外,需要单独给Docker Client和Instance设置代理。 如执行docker run时遇到下面的错误 docker: Error response from daemon: Get "https://registry-1.docker.io/v2/": dial tcp 3.216.…...
Lnton羚通算法算力云平台在环境配置中Windows10终端和VSCode下如何打开Anaconda-Prompt
在Windows 10的终端和VSCode中,可以直接打开Anaconda Prompt。下面是两种方法: Windows 10终端:在开始菜单中搜索"Anaconda Prompt",然后点击打开。这将启动Anaconda Prompt终端,你可以在其中执行conda相关命…...
Python web实战之细说Django的集成测试
关键词: Python Web开发、Django、集成测试、实战、测试驱动开发、自动化测试、Selenium、测试框架、测试用例、代码覆盖率、持续集成 今天给大家分享一下Python Web开发——Django的集成测试,如何利用集成测试来提高代码质量、减少bug。 1. 什么是集成…...
Laravel 模型的作用域 模型的访问器和修改器 ⑨
作者 : SYFStrive 博客首页 : HomePage 📜: THINK PHP 📌:个人社区(欢迎大佬们加入) 👉:社区链接🔗 📌:觉得文章不错可以点点关注 ὄ…...
每日一学——交换机
交换机是一种网络设备,用于连接多台计算机和其他网络设备,以实现数据的交换和传输。它通过将数据包在不同端口之间转发,将数据从一个设备发送到目标设备。交换机可以提供高速、可靠和安全的局域网连接。 交换机的工作原理是根据目标MAC地址来…...
数学建模大全及优缺点解读
分类模型 1、距离聚类(系统聚类)(常用,需掌握) 优点: ①将一批样本数据按照他们在性质上的亲密程度在没有先验知识的情况下自动进行分类 ②是一种探索性的分析方法,分类结果不一定相同 例如&am…...
海口网站建设费用/福州seo公司
(250条消息) hive计算map数和reduce数_一只刚刚上路的猿-CSDN博客 hive优化之——控制hive任务中的map数和reduce数 – lxw的大数据田地 (lxw1234.com) (偏向于控制map和reduce的个数,比较老的文章,但是还是有参考价值)...
巴南网站制作/武汉seo网站优化
创建一个序列(NewStudNo),初始值为10001,步长为1,最大值为99999 create sequence newstudno increment by 1 --每次增长1start with 10001 --表示从1开始计值maxvalue 99999 --有两个可选值,要么无最大值&a…...
做网站设计制作的/google网页版
阅读本文前,请您先点击上面的“蓝色字体”,再点击“关注”,这样您就可以继续免费收到文章了。每天都会有分享,都是免费订阅,请您放心关注。注:本文转载自网络,不代表本平台立场,仅供…...
免费网站模板 带后台/新媒体代运营
在Java中,引用分为强引用、软引用、弱引用和虚引用四种。 强引用,代码中普遍存在的形式,例如常见的普通类new出对象后的引用。GC不会回收强引用的对象。软引用,软引用对象会在内存溢出异常之前进行回收,也就是说在内存…...
昆明做网站猫咪科技/全球网站排名查询
BUG是每个软件都会出现的问题,但有的漏洞对软件的运行影响不大,有的漏洞可能会导致软件无法正常运行。 这时候就需要把这些bug记录下来,提交给别人修改。 一般来说,其中包含哪些错误? 让我们来看看。 1、bug的标题和描述…...
互联网创业就是做网站吗/网络营销的基本方式有哪些
BMW 病毒样本 开源、开放、自由、分享转载于:https://blog.51cto.com/missuniverse110/905332...