网页版五子棋——对战模块(服务器端开发②)

前一篇文章:网页版五子棋——对战模块(服务器端开发①)-CSDN博客

项目源代码:Java: 利用Java解题与实现部分功能及小项目的代码集合 - Gitee.com

目录

·前言

 一、创建并注册 GameAPI 类

1.创建 GameAPI 类

2.注册 GameAPI 类

二、实现 GameAPI 中继承的方法

1.通知玩家就绪

2.处理连接成功

3.处理落子请求

4.通知对手获胜

5.处理玩家退出

三、测试对战功能

·结尾


·前言

        在前一篇文章中介绍了五子棋项目中核心部分有关落子操作相关的逻辑,本篇文章将继续对五子棋项目中对战模块的服务器端代码进行编写,下面我们要进行 WebSocket 请求入口类的编写,实现其继承的方法,还有对整个对战模块功能的测试,本篇文章中将要新增的代码文件如下图圈起来的文件所示:

        下面就开始本篇文章的内容介绍: 

 一、创建并注册 GameAPI 类

1.创建 GameAPI 类

        创建 GameAPI 类,继承自 TextWebSocketHandler 它是作为处理 WebSocket 请求的入口类,其中要重写的几个方法,及每个方法的用途在前面文章已经进行了介绍,文章链接:网页版五子棋—— WebSocket 协议_网页可以实现websocket吗-CSDN博客 ,下面我们先把 GameAPI 类的一个空架子搭好,并且这里要准备几个对象如下所示:

  • 准备一个 ObjectMapper 对象,用来处理 JSON 数据;
  • 注入 RoomManager 对象,用来获取玩家所在房间,还有进行释放房间的操作;
  • 注入 OnlineUserManager 对象,用来获取当前玩家的在线状态,还有获取玩家的连接信息用于判断当前玩家是否多开,和给玩家返回响应;
  • 注入 UserMapper 对象,用于更新对局结束后玩家的信息。

        GameAPI 类空架子的代码及详细介绍如下所示: 

// 通过这个类来处理对战模块中的 WebSocket 请求
@Component
public class GameAPI extends TextWebSocketHandler {// 创建 RoomManager 对象, 用来获取玩家所在房间,还有进行释放房间的操作@Autowiredprivate RoomManager roomManager;// 创建 ObjectMapper 对象, 用来处理 JSON 数据private ObjectMapper objectMapper = new ObjectMapper();// 创建 OnlineUserManager 用来管理玩家的状态信息@Autowiredprivate OnlineUserManager onlineUserManager;// 创建 UserMapper 对象,用于更新对局结束后玩家的信息@Autowiredprivate UserMapper userMapper;// 连接就绪后就会触发这个方法@Overridepublic void afterConnectionEstablished(WebSocketSession session) throws Exception {}// 客户端/服务器 给 服务器/客户端 发送信息通过这个方法就可以接收到信息@Overrideprotected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {}// 传输出现异常就会触发这个方法@Overridepublic void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {}// 如果客户端/服务器关闭连接就会执行这个方法@Overridepublic void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {}}

2.注册 GameAPI 类

        修改 WebSocketConfig 类,把 GameAPI 注册进去,修改后的 WebSocketConfig 类的具体代码及详细介绍如下所示:

// @EnableWebSocket 注解用来告诉 Spring 这是配置 WebSocket 的类
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {// 自动注入@Autowiredprivate TestAPI testAPI;@Autowiredprivate MatchAPI matchAPI;@Autowiredprivate GameAPI gameAPI;@Overridepublic void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {// 当客户端连接 /test 这样的路径后就会触发 testAPI 进而调用其内部的方法registry.addHandler(testAPI,"/test");// 当客户端连接 /findMatch 这样的路径后就会触发 matchAPI 进而调用其内部的方法registry.addHandler(matchAPI,"/findMatch")// 把之前登录过程中往 HttpSession 中存放的 User 对象, 放到 WebSocket 的 session 中// 方便后面代码可以获取到当前的用户信息.addInterceptors(new HttpSessionHandshakeInterceptor());// 当客户端连接 /game 这样的路径后就会触发 gameAPI 进而调用其内部的方法registry.addHandler(gameAPI, "/game")// 把之前登录过程中往 HttpSession 中存放的 User 对象, 放到 WebSocket 的 session 中// 方便后面代码可以获取到当前的用户信息.addInterceptors(new HttpSessionHandshakeInterceptor());}
}

二、实现 GameAPI 中继承的方法

1.通知玩家就绪

        这里我们在 GameAPI 中写一个通知玩家就绪的方法 —— noticeGameReady() ,通过这个方法我们来给连接游戏房间页面成功的两个玩家返回 “准备就绪” 的响应,关于这个方法的具体代码及详细介绍如下所示:

    // 用来给客户端返回 "准备就绪" 的响应private void noticeGameReady(Room room, User thisUser, User thatUser) throws IOException {// 构造 GameReadyResponse 作为返回的响应对象, 来返回响应GameReadyResponse response = new GameReadyResponse();response.setMessage("gameReady");response.setOk(true);response.setReason("");response.setRoomId(room.getRoomId());response.setThisUserId(thisUser.getUserId());response.setThatUserId(thatUser.getUserId());response.setWhiteUser(room.getWhiteUser());// 把当前的响应数据传回给对应的玩家// 获取当前玩家的连接信息WebSocketSession webSocketSession = onlineUserManager.getFromGameRoom(thisUser.getUserId());// 通过连接给当前玩家返回响应webSocketSession.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));}

2.处理连接成功

        这里我们来实现 GameAPI 中的 afterConnectionEstablished 方法,我们要在 afterConnectionEstablished 方法中完成以下工作:

  1. 需要检测用户的登录状态,从 Session 中拿到当前用户的信息;
  2. 需要判断当前玩家是否在房间中;
  3. 需要对多开进行判定,如果玩家已经在游戏中,就不能再进行连接;
  4. 需要把两个玩家放到对应的房间对象中,当两个玩家都建立了连接,房间就放满了,这时调用 noticeGameReady 方法,来通知两个玩家双方都准备就绪;
  5. 如果有第三个玩家尝试加入房间,就需要给出提示:“房间已经满了”;

        在编写代码之前,我们要注意在上面需要完成的工作中,把两个玩家放到对应的房间对象过程中会涉及到线程安全问题,我们在这里会规定先进入房间的玩家是先手,当两个玩家对同一个房间对象进行操作时就会出现如下图所示的情况:        此时,玩家1 与 玩家2 都认为自己是先手方,就会出现线程安全的问题,为了解决这个问题,我们就需要进行加锁操作,下面我们就需要考虑对谁进行加锁,这里我们加锁的原则是对竞争的资源进行加锁,很显然,此时两位玩家竞争的资源就是这个房间对象,所以在执行上图中代码逻辑时,要在我们用 synchronized 对 room 进行加锁操作。

        解决完线程安全的问题后,我们就开始进行实现 afterConnectionEstablished 方法代码的编写,关于这个方法的具体代码及详细介绍如下所示:

    // 连接就绪后就会触发这个方法@Overridepublic void afterConnectionEstablished(WebSocketSession session) throws Exception {GameReadyResponse response = new GameReadyResponse();// 1. 获取用户的身份信息. (从 HttpSession 里拿到当前用户的对象)User user = (User) session.getAttributes().get("user");if (user == null) {response.setOk(false);response.setReason("用户尚未登录!");session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));return;}// 2. 判断当前用户是否已经进入房间. (用房间管理器进行查询)Room room = roomManager.getRoomByUserId(user.getUserId());if (room == null) {// 如果为 null ,当前没有找到对应的房间, 该玩家没有匹配成功.response.setOk(false);response.setReason("用户尚未匹配到对手!");session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));return;}// 3. 判断当前是不是多开 (该用户是不是已经在其他地方进入游戏了)//    前面准备了一个 OnlineUserManagerif (onlineUserManager.getFromGameHall(user.getUserId()) != null|| onlineUserManager.getFromGameRoom((user.getUserId())) != null) {// 如果一个账号, 一边在游戏大厅, 一边在游戏房间, 也视为多开response.setOk(true);response.setReason("禁止多开游戏页面");response.setMessage("repeatConnection");session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));return;}// 4. 设置当前玩家上线!onlineUserManager.enterGameRoom(user.getUserId(), session);// 5. 把两个玩家加入到游戏房间中.//    前面的创建房间/匹配过程, 是在 game_hall.html 页面中完成的.//    因此前面匹配到对手之后, 需要经过页面跳转, 来到 game_room.html 才算是正式进入游戏房间//    当前这个逻辑是在 game_room.html 页面加载的时候进行的.//    执行到当前逻辑, 说明玩家已经页面跳转成功了!!//    页面跳转, 要进行很多的操作, 很可能出现 "失败" 的情况synchronized (room) {if (room.getUser1() == null) {// 第一个玩家尚未加入房间.// 就把当前连上 WebSocket 的玩家作为 user1, 加入到房间中.room.setUser1(user);// 把先连入房间的玩家作为先手方.room.setWhiteUser(user.getUserId());System.out.println("玩家 " + user.getUsername() + " 已经准备就绪! 作为玩家1 ");return;}if (room.getUser2() == null) {// 如果进入这个逻辑, 说明玩家1 已经加入到房间, 现在要把当前玩家作为玩家2room.setUser2(user);System.out.println("玩家 " + user.getUsername() + " 已经准备就绪! 作为玩家2 ");// 当两个玩家都加入成功之后, 就要让服务器给这两个玩家都返回 WebSocket 的响应数据// 通知这两个玩家, 游戏双方都已经准备好了.// 通知玩家1noticeGameReady(room, room.getUser1(), room.getUser2());// 通知玩家2, 注意这里的传参顺序noticeGameReady(room, room.getUser2(), room.getUser1());return;}}// 6. 此处如果又有玩家尝试连接同一个房间, 就提示报错.//    这种情况理论上是不存在的, 为了让程序更加的健壮, 这里再做一个判断和提示.response.setOk(false);response.setReason("当前房间人数已满,您不能加入房间!");session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));}

3.处理落子请求

        这里我们来实现 GameAPI 中的 handleTextMessage 方法,我们要在 handleTextMessage 方法中完成以下工作:

  1. 需要检测用户的登录状态,从 Session 中拿到当前用户的信息;
  2. 需要根据玩家的 id 来获取到房间对象;
  3. 需要解析客户端发来的请求,通过房间对象调用 putChess 方法来处理这次具体的请求。

        关于 handleTextMessage 方法的具体代码及详细介绍如下所示:

    // 客户端/服务器 给 服务器/客户端 发送信息通过这个方法就可以接收到信息@Overrideprotected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {GameReadyResponse response = new GameReadyResponse();// 1. 先从 session 里拿到当前用户的身份信息User user = (User) session.getAttributes().get("user");if (user == null) {System.out.println("[handleTextMessage] 当前玩家尚未登录! ");response.setOk(false);response.setReason("用户尚未登录!");session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));return;}// 2. 根据玩家 id 获取到房间对象Room room = roomManager.getRoomByUserId(user.getUserId());// 3. 通过 room 对象来处理这次具体的请求room.putChess(message.getPayload());}

4.通知对手获胜

        在进行五子棋对战的过程中,一方玩家很可能在进行对弈过程中由于网络或自身的原因退出了当前游戏页面,此时我们就需要通知另一方玩家获胜,不要让另一方玩家一直处于等待,下面我们在 GameAPI 中写一个通知对手获胜的方法 —— noticeThatUserWin(),在这个方法中,我们要完成以下的工作:

  1. 根据当前玩家找到玩家所在的房间,如果房间不存在,说明对局已经正常结束,就不用进行后续的操作了;
  2. 根据房间寻找对手,查看对手在线状态,如果对手也掉线了,那就不用再进行通知;
  3. 构造响应,通知对手获胜;
  4. 判断胜负后更改玩家的信息;
  5. 释放房间对象。

        关于 noticeThatUserWin 方法的具体代码及详细介绍如下所示:

    // 通知对手获胜private void noticeThatUserWin(User user) throws IOException {// 1. 根据当前玩家, 找到玩家所在的房间Room room = roomManager.getRoomByUserId(user.getUserId());if (room == null) {// 这个情况意味着房间已经释放了, 也就没有 "对手" 了System.out.println("当前房间已经释放, 无需通知对手!");return;}// 2. 根据房间找到对手User thatUser = (user == room.getUser1() ? room.getUser2() : room.getUser1());// 3. 找到对手的在线状态WebSocketSession webSocketSession = onlineUserManager.getFromGameRoom(thatUser.getUserId());if (webSocketSession == null) {// 说明对手也掉线了System.out.println("对手已经掉线, 无需通知");return;}// 4. 构造一个响应, 来通知对手: 它是获胜方GameResponse response = new GameResponse();response.setMessage("putChess");response.setUserId(thatUser.getUserId());response.setWinner(thatUser.getUserId());webSocketSession.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));// 5. 更新玩家的分数信息int winUserId = thatUser.getUserId();int loseUserId = user.getUserId();userMapper.userWin(winUserId);userMapper.userLose(loseUserId);// 6. 释放房间对象roomManager.remove(room.getRoomId(), room.getUser1().getUserId(), room.getUser2().getUserId());}

5.处理玩家退出

        这里我们要实现 GameAPI 中 handleTransportError 与 afterConnectionClosed 两个方法,在玩家下线或对局结束退出会触发这个方法,这两个方法中做的工作是一致的,需要完成以下的工作:

  1. 主要的工作是把玩家从 OnlineUserManager 的对象中进行移除;
  2. 退出的时候需要判断当前玩家退出的原因是不是因为多开的情况(一个 userId 对应到两个 WebSocket 连接),如果一个玩家开启了第二个 WebSocket 连接,那么这第二个 WebSocket 连接不会影响到玩家从 OnlineUserManager 中退出;
  3. 如果当前玩家退出游戏,就需要调用 noticeThatUserWin 方法,来判断对局是否是正常结束,如果对局没有结束是玩家主动退出,就需要通知对手获胜。

        关于 handleTransportError 与 afterConnectionClosed 方法的具体代码及详细介绍如下所示:

    // 传输出现异常就会触发这个方法@Overridepublic void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {User user = (User) session.getAttributes().get("user");if (user == null){// 此处简单处理, 在断开连接的时候不给客服端返回响应了.return;}WebSocketSession exitSession = onlineUserManager.getFromGameRoom(user.getUserId());if (exitSession == session) {// 加上这个判断, 目的是为了避免在多开的情况下, 第二个用户退出连接动作,导致第一个用户受到影响onlineUserManager.exitGameRoom(user.getUserId());// 通知对手获胜了noticeThatUserWin(user);}System.out.println("当前玩家: " + user.getUsername() + " 游戏房间连接异常");}// 如果客户端/服务器关闭连接就会执行这个方法@Overridepublic void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {User user = (User) session.getAttributes().get("user");if (user == null){// 此处简单处理, 在断开连接的时候不给客服端返回响应了.return;}WebSocketSession exitSession = onlineUserManager.getFromGameRoom(user.getUserId());if (exitSession == session) {// 加上这个判断, 目的是为了避免在多开的情况下, 第二个用户退出连接动作,导致第一个用户受到影响onlineUserManager.exitGameRoom(user.getUserId());// 通知对手获胜了noticeThatUserWin(user);}System.out.println("当前玩家: " + user.getUsername() + " 已经离开游戏房间");}

三、测试对战功能

        编写完上述代码之后,我们就把五子棋项目中对战模块的代码编写完毕了,与客户端交互的代码在上一篇文章中已经编写完毕,下面我们来启动服务器,在浏览器中输入:http://127.0.0.1:8080/login.html 进入登录页面,通过登录页面进入游戏大厅页面,在游戏大厅页面中点击匹配,进入游戏房间测试对战功能,下面我来测试一下对战功能是否存在问题,测试过程如下图所示:

        经过上述的测试,结果都符合预期,对战功能就是一个正常的功能了。

·结尾

        文章到这里就要结束了,本篇文章主要介绍了 GameAPI 类的代码编写,作为 WebSocket 连接请求的入口类,其中包含的方法是客户端进入游戏房间页面后第一时间要进行调用的,这里需要对很多情况进行判断,防止对局过程中出现问题,那么到这,五子棋项目中的对战模块就编写完成了,结合前面用户模块与匹配模块,网页版五子棋这个项目就算是大功告成了,文章中新增模块对前面代码的一些细微修改可能在文章中并没有很好的体现出来,如果大家对本项目感兴趣,也欢迎在我的码云中获取项目的完整代码,项目源码链接:Java: 利用Java解题与实现部分功能及小项目的代码集合 - Gitee.com ,如果对文章内容有所疑惑,欢迎在评论区进行留言,如果感觉本篇文章还不错希望能收到你的三连支持,那么我们下一篇文章再见吧~~~

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

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

相关文章

STM32单片机WIFI语音识别智能衣柜除湿消毒照明

实践制作DIY- GC0196-WIFI语音识别智能衣柜 一、功能说明: 基于STM32单片机设计-WIFI语音识别智能衣柜 二、功能介绍: STM32F103C系列最小系统板LCD1602显示器ULN2003控制的步进电机(柜门开关)5V加热片直流风扇紫外消毒灯DHT11…

网络远程操控

1.给两个设备配上ip地址让他们能通 2.开启远程管理功能,打开telnet 3.创建远程管理的账号和密码,账号权限 输入system-view进入视图,不敲这个命令不能进行配置 配好ip后进入AR1ping一下AR2的ip看看通不通,接着进入AR2开启telnet权…

【go从零单排】Timer、Epoch 时间函数

🌈Don’t worry , just coding! 内耗与overthinking只会削弱你的精力,虚度你的光阴,每天迈出一小步,回头时发现已经走了很远。 📗概念 在 Go 语言中,time.Timer 是一个用于在指定时间后执行操作的计时器。…

鸿蒙自定义UI组件导出使用

上期讲解了在Entry入口写了一个系统的下拉列表组件,如果我们想要封装一个可供复用的组件供团队其他人使用,那么需要掌握一下自定义组件的写法: 1、自定义可导入组件 - export 声明模块 如果要定义一个在外部可使用的组件 , 需要再定义组件…

Web大学生网页作业成品——婚礼婚纱网页设计与实现(HTML+CSS)(6个页面)

🎉🎉🎉 常见网页设计作业题材有**汽车、环保、明星、文化、国家、抗疫、景点、人物、体育、植物、公益、图书、节日、游戏、商城、旅游、家乡、学校、电影、动漫、非遗、动物、个人、企业、美食、婚纱、其他**等网页设计题目, 可满足大学生网…

时序数据库TimescaleDB安装部署以及常见使用

文章目录 一、时序数据库二、TimescaleDB部署1、repository yum仓库配置2、yum在线安装3、插件配置4、TimescaleDB使用登录pg创建插件使用超表 一、时序数据库 什么是时序数据库?顾名思义,用于处理按照时间变化顺序的数据的数据库即为时序数据库&#x…

Matlab: 生成对抗网络,使用Datastore结构输入mat格式数据

使用matlab的生成对抗网络(Generative Adversarial Network,GAN)以及条件CGAN时,案例中 的生成器的输入为图像,改为.mat格式输入遇到的问题。解决方法 官方资源 训练条件生成对抗网络 (CGAN)- MATLAB & Simulink-…

Linux kernel 堆溢出利用方法(二)

前言 本文我们通过我们的老朋友heap_bof来讲解Linux kernel中off-by-null的利用手法。在通过讲解另一道相对来说比较困难的kernel off-by-null docker escape来深入了解这种漏洞的利用手法。(没了解过docker逃逸的朋友也可以看懂,毕竟有了root权限后&a…

设计模式:工厂方法模式和策略模式

工厂方法模式 什么是开闭原则? 开闭原则是扩展开发,对修改关闭 简单工厂(不是设计模式而是一种编程的习惯) 有三个角色 抽象产品:定义了产品的规范,描述了产品的特性和功能.具体产品:实现或者继承抽象产品的子类具体工厂:提供了创建产品的方法,调用者通过该方法获取产品 实…

深度学习代码笔记

一、U-NET 论文题目:U-Net: Convolutional Networks for Biomedical Image SegmentationUNet 的体系结构基于编码器-解码器范式,其中编码器从输入图像中提取特征,解码器基于这些特征生成分割图。但是,UNet还集成了编码器和解码器…

软件测试面试2024最新热点问题

大厂面试热点问题 1、测试人员需要何时参加需求分析? 如果条件循序 原则上来说 是越早介入需求分析越好 因为测试人员对需求理解越深刻 对测试工作的开展越有利 可以尽早的确定测试思路 减少与开发人员的交互 减少对需求理解上的偏差 2、软件测试与调试的关系 测…

L10.【LeetCode笔记】回文链表

目录 1.题目 2.自解 代码 提交结果 1.题目 给你一个单链表的头节点 head ,请你判断该链表是否为 回文链表 。如果是,返回 true ;否则,返回 false 。 示例 1: 输入:head [1,2,2,1] 输出:tru…

Lucene 和 Elasticsearch 中更好的二进制量化 (BBQ)

作者:来自 Elastic Benjamin Trent Lucene 和 Elasticsearch 中更好的二进制量化 (BBQ)。 嵌入模型输出 float32 向量,通常对于高效处理和实际应用来说太大。Elasticsearch 支持 int8 标量量化,以减小向量大小,同时保持性能。其他…

猿创征文|Inscode桌面IDE:打造高效开发新体验

猿创征文|Inscode桌面IDE:打造高效开发新体验 引言 在当今快速发展的软件开发领域,一个高效、易用的集成开发环境(IDE)是每个开发者必不可少的工具。Inscode 桌面 IDE 作为一款新兴的开发工具,凭借其强大…

【VBA实战】用Excel制作排序算法动画续

为什么会产生用excel来制作排序算法动画的念头,参见【VBA实战】用Excel制作排序算法动画一文。这篇文章贴出我所制作的所有排序算法动画效果和源码,供大家参考。 冒泡排序: 插入排序: 选择排序: 快速排序:…

IPguard与Ping32全面对比——选择最适合企业的数据安全解决方案

在如今数据安全威胁日益加剧的时代,企业必须高度重视保护敏感数据与信息。因此,选择一款合适的数据安全软件,尤其是防泄密和信息保护软件,显得尤为重要。在市场上,有两款备受企业青睐的数据安全解决方案——IPguard和P…

《情商》提升:增强自我意识,学会与情绪共处

在当今社会,情商(Emotional Intelligence,EQ)的重要性越来越受到人们的关注。情商是指个体运用情绪、情感、认知和行为反应的能力,来理解、管理、表达和处理情感的一种综合素养。情商的高低对于个人的成长、人际关系、…

k8s集群安装(kubeadm)

k8s集群安装(kubeadm) 1、环境准备(master和node节点都执行)1.1、替换yum源1.2、关闭selinux1.3、永久关闭防火墙1.4、永久关闭swap1.5、修改主机名添加host1.6、时间同步1.7、将桥接的IPv4流量传递到iptables的链1.8、docker安装…

使用Matlab建立随机森林

综述 除了神经网络模型以外,树模型及基于树的集成学习模型是较为常用的效果较好的预测模型。我们以下构建一个随机森林模型。 随机森林是一种集成学习方法,通过构建多个决策树并结合其预测结果来提高模型的准确性和稳定性。在MATLAB中,可以…

Wireshark

目录 解题思路 题目设计原理 总结 解题思路 首先下载文件,用 wireshark 打开一头雾水。 但是看看题目的提示,说管理员的密码就是 flag 的内容,我们可以知道,关键词估计是密码,passwd、password、pwd之类的。 所以我…