Java swing + socket 写的一个五子棋网络对战游戏

2023-11-16

自从开始接触Swing以来,就喜欢写写各种管理系统,写多了就萌生了一种类似于实时在线对战的游戏,经过一番构思后就开始着手设计这个网络对战版本的五子棋了。
游戏代码包含两部分,常规的C/S模式(C代表客户端,S代表服务端)
下载代码后先启动服务器,服务器正常启动后,你会在控制台看到相关的日志(这里注意,服务器是没有做界面管理的),接着启动客户端(可以启动多个客户端),连接服务器后点击菜单栏联网、对战、匹配等操作

游戏效果图:
欢迎大家支持新自的研游戏:中国象棋

这里写图片描述

下载在线客户端版本试玩:

链接:https://pan.baidu.com/s/1-Bt8tcuZGkVj-jFxN4YDbg 密码:w2oy
要求:jdk环境 1.6或以上
使用方式:环境正常安装后,解压下载的文件,点击 startClient.bat 就可以打开了
备注:若你只开一个客户端,进行匹配的话可能没人跟你玩,建议不是为了技术纯测试的话与你和你的朋友一起对战

阅读本文前,您需要了解:

  • java swing(好像是废话)
  • socket
  • json
  • 多线程(不多,计时用了一下)

1:服务端与客户端数据交互如何约定?

在c/s程序的设计之初,如何按照约定的方式进行数据交互一直是一个需要解决的问题,在我这个程序中,有一个常量类定义如下,这是我与客户端进行的一个约定,任何请求都会含有一个基础数据(key),而所做的工作就是这个基础数据(key)所对应的基础数据(value)
package cn.xt.net;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;

public class Const {
	

	public static final int PORT = 7001; // 监听端口
	
	// 基础数据(key)
	public static final String ID = "id";
	public static final String MSG = "msg";

	// 基础数据(value)
	public static final String ID_STATUS_ERROR = "idError"; // 错误信息
	public static final String ID_STATUS_INIT = "初始化客户端"; // 向服务器请求初始化
	public static final String ID_STATUS_PP = "匹配玩家";
	public static final String PP_SUCCESS = "匹配成功";
	public static final String ID_STATUS_PUT = "传送落子位置";
	public static final String ID_STATUS_GET = "获取落子位置";
	public static final String ID_STATUS_OVER = "对局结束";
	public static final String ID_STATUS_MSG = "聊天消息";
	public static final String ID_STATUS_BACK = "请求悔棋";
	public static final String ID_STATUS_FAIL = "认输";
	public static final String ID_STATUS_HANDSNAKE = "初次握手";
	public static final String ID_STATUS_BACK_RESULT = "请求悔棋结果";
	public static final String ID_STATUS_OVERTIME = "游戏超时";
	public static final String SIZE = "棋盘长度";
	public static final String EXISTS = "该用户名已存在系统中";
	public static final String USER_NAME = "userName";
	public static final String INIT_SUCCESS = "初始化成功";
	public static final String X = "x";
	public static final String Y = "y";
	public static final String STATUS = "status"; // 当前棋子的状态
	public static final String COLOR = "落子颜色";
	public static final String SYSTEM_MSG = "系统消息";

	// key - value
	public static final String MY = "my"; // 玩家
	public static final String YOU = "you"; // 对家
	public static final String FIRST = "先手方"; // 1:先手; 0:后手
	
	// 属于页面的专属数据
	private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
	
	public static String getId() {
		return getSysDate() + "-" + UUID.randomUUID().toString();
	}
	
	public static String getSysDate(){
		return sdf.format(new Date());
	}

//	public static void main(String[] args) {
//		System.out.println(Const.getId());
//	}
}

2:服务端构建

客户端的连接信息将会在服务器定义一个Map保存,该实体类定义如下:
保存客户端所有信息的Map:
	private static volatile Map<String, UserData> client = new HashMap<>();
实体类定义(类名:UserData):
	private MessageFormat msgFormat; // 处理消息的对象
	private Socket socket; // 客户端套接字对象
	private Thread thread; // 处理消息的线程
	private String userId; // 登陆的用户(唯一性)
	private String userName; // 客户的名字(自己取的名称)
	private String status; // 状态(0:登陆; 1:准备; 2:对局开始; 3:观战)
	private String enemy; // 对家
	private String isFirst; // 是否为先手
	private List<Chess> chessBoard; // 棋盘
	private boolean isOver; // 对局是否结束
定义一个普通的启动类,继承Thread,重写Thread类中的run方法以监听客户端socket请求,其中MessageFormat类是处理客户端的发送/传输类,每个客户端连接后都会生成一个inputstream与outputstream(I/O)流,MessageFormat对其进行了相关的封装,也是统一处理客户端发送的信息和发送信息给客户端,在下面我会重点介绍该类,该类也是处理消息的一个核心类。
private ServerSocket ss;
private Socket s;
private boolean start = false;
private static volatile Map<String, UserData> client = new HashMap<>();

public static void main(String[] args) {
	SuperServlet servlet = new SuperServlet();
	servlet.startService();
}

public SuperServlet() {
	try {
		ss = new ServerSocket(Const.PORT);
	} catch (IOException e) {
		e.printStackTrace();
	}
}

// 线程用来接收客户端连接的请求
@Override
public void run() {
	try {
		while (start) {
			s = ss.accept(); // 一直监听客户端的请求
			synchronized (s) {
				String userId = Const.getId(); // 返回一个唯一id
				MessageFormat cs = new MessageFormat(s); // 建立传输入数据的线程(服务类)
				// 为每个连接的客户端定义一个线程为其服务
				Thread th = new Thread(cs); 
				// 定义用户信息类
				UserData ud = new UserData();
				ud.setUserId(userId); 
				ud.setMsgFormat(cs);
				ud.setSocket(s);
				ud.setThread(th);
				client.put(userId, ud); // 将这个用户保存到服务器中
				th.start(); // 启动这个线程
				// 将识别ID发给客户端保存
				JSONObject responseMsg = new JSONObject();
				responseMsg.put(Const.ID, Const.ID_STATUS_HANDSNAKE);
				responseMsg.put(Const.MSG, userId);
				cs.send(responseMsg); // 发送消息给客户端
				System.out.println("一个客户连接... 在线数量:" + client.size());
			}
		}
	} catch (Exception er) {
		System.out.println("服务已启动或端口被占用!");
		er.printStackTrace();
		System.exit(0);
	}
}

2.1:服务器之MessageFormat类

前面我讲到,该类处理客户端的请求与服务端发送给该客户端的请求,为不给服务器造成阻塞,该类肯定是一个线程,简单的看一下该类定义的几个方法:
// 默认构造器,对新连接的客户端的I/O流进行封装
public MessageFormat(Socket s)
// 重写该线程的run方法,用于循环监听客户端的发送的信息
public void run()
// 发送信息给该客户端
public void send(final JSONObject msg)
// 该客户端主动发送信息给别的客户端
public void send(String userId, JSONObject msg)
// 客户端断开时关闭I/O流
public void close()
// 消息处理中心(msg为客户端发过来的json数据)
public void addInfo(String msg)
服务端重点处理的是客户端发过来的信息,所以先来讲解处理的方式:

对着下面的代码,我们先是循环监听客户端发过来的信息,dis.readUTF()是一个阻塞式的方法,若客户端发过来信息则返回一个字符串,接着在这里只是进行简单的打印就将信息传送给了addInfo() 这个方法;在我的catch中进行了相关处理,这里主要处理客户端若遇到不可描述的事情断开后,服务器应主动踢出这个客户端,这里我是先遍历了服务器中所有的客户端,找到一个线程ID与之匹配的,然后把这个线程踢出Map

@Override
public void run() {
	while (clientStart) {
		try {
			// 客户端传来的信息
			String requestMsg = dis.readUTF(); 
			System.out.println("service do  in:" + requestMsg);
			// LogUtils.write("service do  in:" + requestMsg);
			// 消息处理
			addInfo(requestMsg);
		} catch (IOException e) {
			// 如果无法接收客户端的信息
			for(Entry<String, UserData> set : SuperServlet.getClient().entrySet()){
				String userId = set.getKey(); 
				UserData ud = set.getValue();
				// 匹配退出的客户端进程
				if(this == ud.getMsgFormat()){
					// 给对手发送退出信息
					JSONObject responseMsg = new JSONObject();
					responseMsg.put(Const.ID, Const.ID_STATUS_FAIL);
					UserData clientData = SuperServlet.getClient().get(ud.getEnemy());
					if(clientData != null && !clientData.isOver()){
						clientData.getMsgFormat().send(responseMsg);
					}
					MessageFormatHelper.ppList.remove(ud.getUserId());
					System.out.println("退出的客户端为:"+ ud.getUserName() + " --> " + ud.getUserId());
					SuperServlet.getClient().remove(userId); // 服务器移除这个用户
					System.out.println("在线数量:" + SuperServlet.getClient().size());
					// ud.getThread().interrupt(); // 等待线程关闭
					close(); // 关闭相应的流
					clientStart = false; // 预先停止 while循环
					ud.getThread().stop(); // 等待线程关闭
				}
					
			}
		}
	}
}
由上面的代码可以看到,客户端的所有信息我是将由addInfo()方法处理的:

对着下面代码,我们先是将传过来的信息进行转义替换,然后转换成JSON,取到开始时常量类里面定义的key,进行switch匹配,匹配到对应的ID就做对应的事,这种做法在SpringMVC中跟RequestMapper有着异曲同工之想法。在这里,有一处地方我没有做讲解,MessageFormatHelper这个类是做什么的呢?在我们写Javaweb项目时,常会写一个dao 与 daoImpl,在这里MessageFormatHelper相当于 daoImpl类,也就是实现具体功能的类。

public void addInfo(String msg) {
	JSONObject json = JSONObject.fromObject(msg.replaceAll("\"", "\\\""));
	MessageFormatHelper.initialized(json); // 优先初始化服务器数据
	String id = json.get(Const.ID).toString();
	switch (id) {
		// 初始化
	case Const.ID_STATUS_INIT:
		MessageFormatHelper.init();
		break;
		// 匹配玩家
	case Const.ID_STATUS_PP:
		MessageFormatHelper.matchUser();
		break;
		// 落子
	case Const.ID_STATUS_PUT:
		MessageFormatHelper.putChess();
		break;
		// 获取棋子
//		case Const.ID_STATUS_GET:
//			MessageFormatHelper.getChess();
//			break;
		// 对局结束
	case Const.ID_STATUS_OVER:
		MessageFormatHelper.gameOver();
		break;
		// 悔棋请求
	case Const.ID_STATUS_BACK:
		MessageFormatHelper.toBack();
		break;
		// 悔棋请求的结果
	case Const.ID_STATUS_BACK_RESULT:
		MessageFormatHelper.toBackResult();
		break;
		// 认输
	case Const.ID_STATUS_FAIL:
		MessageFormatHelper.fail();
		break;
		// 聊天消息
	case Const.ID_STATUS_MSG:
		MessageFormatHelper.chatMsg();
		break;
		// 游戏超时
	case Const.ID_STATUS_OVERTIME:
		MessageFormatHelper.overTime();
		break;
	default:
		System.out.println("server: 未匹配到分支; id:" + id);
		break;
	}
	// 未找到匹配分支
//		JSONObject result = new JSONObject();
//		result.put(Const.ID, "未找到匹配分支");
//		return result;
}
MessageFormatHelper类实现的功能在MessageFormat中皆有体现并有注释,因代码太多,大家自行下载代码研究,我的代码注释一般都写的比较详细,且思路通俗易懂;这里贴几个少一点的功能代码
/**
 * 双方棋子互传
 */
public static void putChess() {
	// 设置我方棋盘
	String userId = json.getString(Const.MY);
	// 获取坐标
	int x = json.getInt(Const.X);
	int y = json.getInt(Const.Y);
	String color = json.getString(Const.COLOR);
	
	// 更新我方棋盘
	UserData my = SuperServlet.getClient().get(userId);
	my.getChessBoard().add(new Chess(x, y, color));
	// 更新对方棋盘
	UserData you = SuperServlet.getClient().get(my.getEnemy());
	you.getChessBoard().add(new Chess(x, y, color));
	
	// 更新服务器数据
	SuperServlet.updateClient(userId, my);
	SuperServlet.updateClient(you.getUserId(), you);
	
	// 将棋子同步给对手
	if(!you.isOver()){
		json.put(Const.ID, Const.ID_STATUS_GET);
		you.getMsgFormat().send(json);
	} else {
		System.out.println("无将棋子同步给对手, 对方已结束游戏");
	}
}


/**
 * 聊天消息处理
 */
public static void chatMsg() {
	result.put(Const.ID, Const.ID_STATUS_MSG);
	String userId = json.getString(Const.MY);
	UserData my = SuperServlet.getClient().get(userId);
	// 获取对手名称
	UserData you = SuperServlet.getClient().get(my.getEnemy());
	if(you != null){
		result.put(Const.MSG, json.getString(Const.MSG));
		result.put(Const.MY, my.getUserName());
		you.getMsgFormat().send(result);
	}
}

/**
 * 悔棋请求
 */
public static void toBack(){
	String userId = json.getString(Const.MY);
	UserData my = SuperServlet.getClient().get(userId);
	
	result.put(Const.ID, Const.ID_STATUS_BACK);
	UserData you = SuperServlet.getClient().get(my.getEnemy());
	you.getMsgFormat().send(result);
}

/**
 * 悔棋结果
 */
public static void toBackResult(){
	String userId = json.getString(Const.MY);
	UserData my = SuperServlet.getClient().get(userId);
	
	result.put(Const.ID, Const.ID_STATUS_BACK_RESULT);
	result.put(Const.MSG, json.getString(Const.MSG));
	UserData you = SuperServlet.getClient().get(my.getEnemy());
	you.getMsgFormat().send(result);
	
	// 更新服务器棋盘以备观战
	if("同意".equals(json.getString(Const.MSG))){
		my.getChessBoard().remove(my.getChessBoard().size() - 1);
		you.getChessBoard().remove(you.getChessBoard().size() - 1);
		SuperServlet.updateClient(my.getUserId(), my);
		SuperServlet.updateClient(you.getUserId(), you);
	}
}

3:客户端

客户端传送信息与服务器交互采用的方式跟服务器处理信息的方式是一样的,所以没有什么可讲的,主要讲一下棋盘的绘制与如何判断已经胜利。

3.1:客户端之JPanel

在这个之前还有一个JFrame,学过awt的应该知道 frame,而JFrame则是swing,swing是AWT的之类,相关资料可以查询API,JPanel是一个容器,而JFrame是一个窗口,这里直接跳过JFrame讲JPanel,因为绘制棋盘的甩有功能都是在这个容器里面实现的
/**
 * 相关属性定义
 */
private int span = 35; // 棋盘格子宽度
private int margin = 22;
private final int DIAMETER = 35; // 直径
private final int row = 15; // 棋盘行、列
private final int col = 15;
private int i = 0;
private boolean isBlack = true;
private boolean isPicture = true;// 是否用图片作为背景(图片是正常游戏背景,false为测试游戏背景)
private ImageIcon img = new ImageIcon("src/images/board.jpg");
private List<Chess> list = new LinkedList<Chess>(); // 整个棋盘
public boolean gameOver = true; // 默认结束游戏

// 网络数据
public boolean isNetworkPK = false;
public boolean myChessColor = false; // 记录我方落子的颜色
public boolean failFlag = false; // 认输标记
public MessageQueuePanel MQPanel;
public String userName = null; // 我的名称
public String userId = null; // 我的ID

以上定义中,棋盘就是我们的List,重写 JPanel 的Paint方法绘制游戏棋盘

@Override
public void paint(Graphics g) {
	super.paint(g);
	// span = this.getHeight() / row; // 当窗口被拖动,动态刷新窗口
	Graphics2D g2 = (Graphics2D) g;
	// 正常游戏绘制的棋盘是一张背景图片
	g.drawImage(img.getImage(), 1, 0, null); 
	RadialGradientPaint rgp = null;
	// 画棋子
	for (i = 0; i < list.size(); ++i) {
		Chess chess = list.get(i);
		int xPos = chess.getX() * span + margin; // 将真实坐标转换成网格坐标
		int yPos = chess.getY() * span + margin;
		g2.setColor(chess.getColors()); // 设置画笔颜色
		if (chess.getColors() == Color.BLACK) {
			rgp = new RadialGradientPaint(xPos - DIAMETER / 2 + 26, //
					yPos - DIAMETER / 2 + 12, 20, new float[] { 0.0f, 1.0f }, //
					new Color[] { Color.WHITE, Color.BLACK });
			g2.setPaint(rgp);
		} else {
			// x, y, 直径, 渐变度, 渐变色
			rgp = new RadialGradientPaint(xPos - DIAMETER / 2 + 25, //
					yPos - DIAMETER / 2 - 30, 60, new float[] { 0f, 1f }, //
					new Color[] { Color.BLACK, Color.WHITE });
			g2.setPaint(rgp);
		}
		// 去锯齿
		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		g2.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_DEFAULT);
		g2.fillOval(xPos - DIAMETER / 2, yPos - DIAMETER / 2, span - 1, span);
		// 画红色矩形
		g2.setColor(Color.RED);
		if (i == list.size() - 1)
			g2.draw3DRect(xPos - DIAMETER / 2 - 1, // 
					yPos - DIAMETER / 2 - 1, span, span + 1, true);
	}
}

3.2:客户端之如何判断胜利

在isWin方法中,我们传入整个棋盘,以及当前的落子位置和当前落子的颜色,五子棋判断胜利相关于一个‘米’字,实则只需要扫描一条边后,然后判断一下同颜色的连子数是否大于等于五个就可判断它是否已经胜利了,在代码中前两个循环是判断第一条横线,分别判断从落子位置的左边和右边,也就是米字中的 一 ,其它判断通俗易懂,大家可自行领悟
// 判断胜利的方法
private boolean isWin(List<Chess> list, int xPos, int yPos, boolean isBlack) {
	int chessCount = 1;
	final int max = 5; // 连子数
	int x = 0, y = 0;
	Color color = (isBlack ? Color.BLACK : Color.WHITE);
	// 当前位置向左
	for (x = xPos - 1; x >= 0; --x) {
		if (getChess(list, x, yPos, color) != null)
			chessCount++;
		else
			break;
	}
	// 当前位置向右
	for (x = xPos + 1; x <= row; ++x) {
		if (getChess(list, x, yPos, color) != null)
			chessCount++;
		else
			break;
	}
	if (chessCount >= max)
		return true;
	else
		chessCount = 1;

	// 当前位置向上
	for (y = yPos - 1; y >= 0; --y) {
		if (getChess(list, xPos, y, color) != null)
			chessCount++;
		else
			break;
	}
	// 当前位置向下
	for (y = yPos + 1; y <= col; ++y) {
		if (getChess(list, xPos, y, color) != null)
			chessCount++;
		else
			break;
	}
	if (chessCount >= max)
		return true;
	else
		chessCount = 1;

	// 左斜着向上
	for (x = xPos - 1, y = yPos - 1; x >= 0 && y >= 0; --x, --y) {
		if (getChess(list, x, y, color) != null)
			chessCount++;
		else
			break;
	}
	// 右斜着向下
	for (x = xPos + 1, y = yPos + 1; x <= row && y <= col; ++x, ++y) {
		if (getChess(list, x, y, color) != null)
			chessCount++;
		else
			break;
	}
	if (chessCount >= max)
		return true;
	else
		chessCount = 1;
	// 右斜着向上
	for (x = xPos + 1, y = yPos - 1; x <= row && y >= 0; ++x, --y) {
		if (getChess(list, x, y, color) != null)
			chessCount++;
		else
			break;
	}
	// 左斜着向下
	for (x = xPos - 1, y = yPos + 1; x >= 0 && y <= col; --x, ++y) {
		if (getChess(list, x, y, color) != null)
			chessCount++;
		else
			break;
	}
	if (chessCount >= max)
		return true;
	else
		chessCount = 1;
	return false;
}
完整服务器代码与客户端代码链接:https://download.csdn.net/download/qq_20698983/10448331
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java swing + socket 写的一个五子棋网络对战游戏 的相关文章

  • createImage(int width, int height) 的问题

    我有以下代码 作为游戏的一部分每 10 毫秒运行一次 private void gameRender if dbImage null createImage returns null if GraphicsEnvironment isHea
  • Java:无法从同一包中的不同类访问静态变量

    这很奇怪 因为我有一个可以访问 Frame dimension getWidth 的 Character 类 及其伙伴 getHeight 但是当我想在 Map 类中使用它时 Eclipse 强调了它并且无法给我反馈 运行该程序最终会出现
  • 使用 Ant 将非代码资源添加到 jar 文件

    我正在将 java 应用程序打包成 jar 文件 我正在使用 ant 和 eclipse 我实际上需要在 jar 中直接在根文件夹下包含几个单独的非代码文件 xml 和 txt 文件 而不是与代码位于同一位置 我正在尝试使用includes
  • 使用 GWT 读取非常大的本地 XML 文件

    我正在使用 GWT 构建我的第一个 Java 应用程序 它必须从一个非常大的 XML 文件中读取数据 当我尝试发送对文件中信息的请求时遇到问题 并且我不太确定它是否与文件的大小或我的语义有关 在我的程序中 我有以下内容 static fin
  • 是否有任何简单(且最新)的 Java 框架可用于在 Swing 应用程序中嵌入电影?

    我正在构建一个小型 Swing 应用程序 我想在其中嵌入一部电影 重要的是 这个应用程序是一个 WebStart 应用程序 并且该库应该能够打包在我启动的 jnlp 中 即 不依赖于本机库 我知道并尝试过 JMF 但我认为与其他框架相比 其
  • Java:在 eclipse 中导出到 .jar 文件

    我正在尝试将 Eclipse 中的程序导出到 jar 文件 在我的项目中 我添加了一些图片和 PDF s 当我导出到 jar 文件时 似乎只有main已编译并导出 我的意愿是如果可能的话将所有内容导出到 jar 文件 因为这样我想将其转换为
  • 不同类型的数组

    是否可以有一个包含两种不同类型数据的数组 我想要一个包含双精度型和字符串的数组 我尝试过 ArrayList
  • Spring RestTemplate 使用 cookie 遵循重定向

    最近我遇到了一个问题 我需要做一个GET请求远程服务 我假设使用一个简单的 servlet 并且 RestTemplate 返回Too many redirects 经过一番调查 似乎对指定远程服务发出的第一个请求实际上只是一个 302 重
  • Integer.parseInt("0x1F60A") 以 NumberformatException 结束

    我尝试从数据库中获取长字符串内的表情符号代码 格式如下 0x1F60A 所以我可以访问代码 但它将是String 起初 我尝试通过执行以下操作来转换变量tv setText beforeEmo getEmijoByUnicode int e
  • ConcurrentHashMap 内部是如何工作的?

    我正在阅读有关 Java 并发性的 Oracle 官方文档 我想知道Collection由返回 public static
  • 需要使用 joda 进行灵活的日期时间转换

    我想使用 joda 解析电子邮件中的日期时间字符串 不幸的是我得到了各种不同的格式 例如 Wed 19 Jan 2011 12 52 31 0600 Wed 19 Jan 2011 10 15 34 0800 PST Wed 19 Jan
  • 使用 Guice 优化注册表

    你好 今天思考了一种优化 有一些疑问 语境 我正在使用 Guice 2 进行 Java 开发 在我的网络应用程序中 我有一个转换器注册表 可以即时转换为某种类型 转换器描述如下 public class StringToBoolean im
  • Java Swing For mac 中的 DJ Native Swing 浏览器

    我有一个用 Swing 制作的 Java 应用程序 并且使用了一个 DJ Native Swing 浏览器 当我尝试在 OS X 上使用它时 它抛出了一个NoClassDefFoundError尽管我添加了 swt jar 但始终如此 有人
  • 了解joda时间PeriodFormatter

    我以为我明白了 但显然我不明白 你能帮我通过这些单元测试吗 Test public void second assertEquals 00 00 01 OurDateTimeFormatter format 1000 Test public
  • 如何在 Spring 属性中进行算术运算?

  • 读取电子邮件的文本文件转换为 Javamail MimeMessage

    我有一个电子邮件原始来源的文本文件 直接从 gmail 复制 如果您单击 查看原始文件 您就会看到它 我想读入该文件并将其转换为 MimeMessage 如果您好奇为什么 我设置了 JavaMaildir 并且需要用电子邮件填充它的收件箱以
  • 使用架构注册表对 avro 消息进行 Spring 云合约测试

    我正在查看 spring 文档和 spring github 我可以看到一些非常基本的内容examples https github com spring cloud samples spring cloud contract sample
  • Spring-ws:如何从没有“Request”元素的 xsd 创建 Wsdl

    尝试为客户端实现 SOAP Web 服务 我需要一个 wsdl 文件来通过soapUI 测试该服务 但正如您在下面看到的 这个 xsd 没有 Request 和 Response 方法 所有请求和响应都被定义为基本 ServiceProvi
  • 如何重新启动死线程? [复制]

    这个问题在这里已经有答案了 有哪些不同的可能性可以带来死线程回到可运行状态 如果您查看线程生命周期图像 就会发现一旦线程终止 您就无法返回到新位置 So 没有办法将死线程恢复到可运行状态 相反 您应该创建一个新的 Thread 实例
  • 在java中使用多个bufferedImage

    我正在 java 小程序中制作游戏 并且正在尝试优化我的代码以减少闪烁 我已经实现了双缓冲 因此我尝试使用另一个 BufferedImage 来存储不改变的游戏背景元素的图片 这是我的代码的相关部分 public class QuizApp

随机推荐

  • 刀片式服务器与虚拟机,为什么人们在开发虚拟主机时更喜欢刀片服务器?

    服务器制造商正在不断开发刀片技术 因此刀片服务器的处理器性能和内存容量已达到10年前的超级计算机水平 毫无疑问 刀片服务器曾经实现了 事半功倍 的承诺 但现在需要重新考虑这个问题 人们为什么在虚拟主机的开发中更喜欢刀片服务器 使刀片服务器缺
  • 为什么mysql source命令导入数据比可视化工具执行sql文件快?

    在一般情况下 使用MySQL的source命令导入数据比使用可视化工具执行SQL文件更快 这是因为涉及到了不同的执行方式和优化策略 批量执行 vs 逐条执行 source命令会将整个SQL文件作为一个批量进行执行 而可视化工具往往是逐条读取
  • VS Code搭配code runnner编译时提示:g++: fatal error: no input files解决方法

    如下图所示 如果我们使用的是windows系统 当我们编写好C 文件之后 执行run code命令 就会出现的下面的错误提示 g error testCodeRunnner cpp No such file or directory g f
  • Even Degree【2020 年 “游族杯”E题】【欧拉回路】

    题目链接 题意 有N个点 M条边 每次可以删去一条两端点的度不都是奇数的边 问最多可以删除几条边 题目保证初始所有点度为偶数 首先 题目保证了初始的时候所有的点的度都是为偶数的 于是原图中的每一个联通块一定是一个欧拉回路 对于欧拉回路 最好
  • android:configChanges属性

    今天有幸去哥们的大公司做了半天的暂时工 一个偶现的Bug折腾了他好久 好不easy今天抓到了异常Log日志 大致的意思就是android view windowleaked 窗口泄漏 我在网上查了资料 Android的每个Activity都
  • 简单使用visio画时序图

    1 时序图作用 时序图是强调消息时间顺序的交互图 描述了对象之间传递消息的时间顺序 用来表示用例中德行为顺序 纵轴是时间轴 时间沿竖线向下延伸 横轴代表了协作各独立的对象 2 时序图包含了4个元素 1 对象 Object 2 生命线 Lif
  • h2数据库 mysql 区别_开源数据库 H2, HSQLDB, DERBY, PostgreSQL, MySQL区别/对比图表( 附加翻译) h2数据库...

    开源数据库 H2 HSQLDB DERBY PostgreSQL MySQL区别 对比图表 浪天涯博主翻译 referential integrity 参考完整性 transactions 事物 unicode 统一码 interface
  • 推荐一些PyCharm中常用的插件,实用且炫酷

    工欲善其事 必先利其器 PyCharm 上面的插件是非常实用的 能够巧妙的使用插件对于我们的开发功能的帮助非常大 下面小编为大家推荐一些不错的插件 1 Key Promoter X 快捷键 用来提示快捷键的插件 帮助我们尽可能的摆脱鼠标操作
  • mysql--窗口函数

    一 前言 由力扣题引发的一次窗口函数的学习 mysql从8 0开始支持窗口函数 使用窗口函数 会令我们的分组查询变得便捷 二 概念 一 定义 窗口函数 对一个查询SQL 将其结果集按指定的规则进行分区 每个分区可以看作是一个窗口 分区内的每
  • 安卓Kotlin 使用ViewPager2实现简易左右滑动翻页效果

    布局很简单 R layout onepage仅有一个占满全屏幕的ImageView名为onei R layout activity view仅有一个占满全屏幕的ViewPager2名为vp 以下为Activity的全部代码 class Vi
  • 读完 DALL-E 论文,我们发现大型数据集也有平替版

    内容提要 OpenAI 团队的新模型 DALL E 刷屏 这一新型神经网络 使用 120 亿参数 经过 特训 任意描述性文字输入后 都可以生成相应图像 如今 团队将这一项目的论文和部分模块代码开源 让我们得以了解这一神器背后的原理 原创 H
  • 做BI领域的ChatGPT,思迈特升级一站式ABI平台

    8月8日 以 指标驱动 智能决策 为主题 2023 Smartbi V11系列新品发布会在广州丽思卡尔顿酒店开幕 后疫情时代 BI发展趋势的观察与应对 在发布会上 思迈特CEO吴华夫在开场致辞中表示 当前大环境背景下 数字化转型是企业高质量
  • 网站服务器记录攻击日志ctf,通过网站日志分析sql注入攻击的痕迹

    一次偶然的机会 我的朋友给我发了一个日志文件 让我看一下服务器access log文件 说是CTF的题目 给了它这样一个access文件 随后要了flag 因为我是专业划水的 CTF基本上不碰它 我也不知道怎么做 所以我试着分析了一下下 打
  • 用c++制作c++

    用c 制作c 我想了半年了 终于成功了 那么接下来我给大家分享一下我用c 制作c 的代码吧 include
  • 免费获取知网文献----浙江图书馆+支付宝

    使用支付宝办理浙江图书馆读者证 在支付宝搜索 浙江图书馆 并进入其生活号 开通借阅服务 办理读者证 使用读者证号登录浙江图书馆官网 通过以上步骤成功办理读者证后 会获取一个读者证号 在支付宝 浙江图书馆 生活号的首页或个人中心可以看到 使用
  • matlab 日期加小时数_matlab建立连续时间的时间函数

    满意答案 dashengjia 推荐于 2016 05 22 采纳率 55 等级 12 已帮助 7603人 这个是内建函数 源码不公开 CUMSUM Cumulative sum of elements For vectors CUMSUM
  • JSON 数据类型转换工具

    简介 本文介绍一款数据类型转换工具 可以将JSON格式数据转换成YAML MYSQL XML TOML JavaScript等数据类型 背景 在日常的开发工程中 我们经常使用JSON表达数据 但有些时候我们需要使用YAML JavaScri
  • Cannot make a static reference to the non-static method getLocalActivityManager()

    Cannot make a static reference to the non static method getLocalActivityManager from the type ActivityGroup 想调用getLocalA
  • Python 异常捕获与处理

    文章目录 一 基础知识 1 1 什么是异常 1 2 异常分类 1 3 异常的结构 二 异常类型 三 异常处理语法结构 3 1 语法结构 3 2 万能异常 3 3 try语法结构结合else和finally 四 异常处理补充 4 1 断言 4
  • Java swing + socket 写的一个五子棋网络对战游戏

    自从开始接触Swing以来 就喜欢写写各种管理系统 写多了就萌生了一种类似于实时在线对战的游戏 经过一番构思后就开始着手设计这个网络对战版本的五子棋了 游戏代码包含两部分 常规的C S模式 C代表客户端 S代表服务端 下载代码后先启动服务器