在完成java单机五子棋后,我开始尝试写联机五子棋(局域网内,因为没有公网IP)。上次的五子棋写的很乱,全部写在一个类中,这次我采用面向对象的思想,把特定的功能和属性都写成一个类。代码分为两部分,客户端和服务端。客户端是用AWT写的,主要由一个五子棋面板和一个功能键面板构成。网络通信使用的是TCP,通过序列化和反序列化完成消息的读和写。运行的时候先运行服务端程序,接着运行两个客户端,代码放在了文章的最后。
下面是客户端运行的效果:
这里是代码包的结构:
接着我来依次说下这些类所完成的功能
Media包
Media包:主要是放了五子棋的背景图片和播放音乐的类以及音乐内容
播放音乐这个类是我从室友那拿的,所以我也不是很懂,瞄了一眼是用Applet完成的,只能处理.wav后缀的音乐
Net包
Net包:包含两个类,细心的小伙伴应该注意到客户端是没有主方法的。客户端中其实是包含与服务端进行通信的socket的,其中包含一些读和写的方法。服务端我采用的是线程池的方法来处理客户端的请求(线程池这部分我也不是特别了解,用起来和多线程感觉差不多)。
View包
View包:包含四个类,分别是ChessBoard,ChessPanel,Pieces和WhoWin 类
ChessBoard是一个包含Main方法的JFrame,命令面板和棋盘面板都是添加到这个JFrame中的。
ChessPanel是一个棋盘面板,里面完成了如:画19*19的棋盘线条,加载背景图片,不停的接收来自服务端的消息并处理(把棋子加到面板),处理每次点击后添加棋子到面板并发送棋子到服务器,判断胜负并且给出提示消息。
Pieces是一个棋子,其中有包含如颜色,棋子半径,棋子位置和命令(和前面的命令面板配合使用,默认是发送)等属性。
WhoWin就是五子棋中判断谁输谁赢的部分。每下一步就需要判断。
播放音乐的类:
package Media.Music;
import java.io.File;
import java.io.IOException;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
public class PlayMusic {
private Clip clip;
public PlayMusic(String filePath) throws LineUnavailableException, UnsupportedAudioFileException, IOException {
File file = new File(filePath);
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(file);
clip = AudioSystem.getClip();
clip.open(audioInputStream);
}
public void play() {
clip.setFramePosition(1);
clip.start();
}
public void loop() {
clip.loop(Clip.LOOP_CONTINUOUSLY);
}
public void stop() {
clip.stop();
}
}
TcpClient:
package net;
import view.Pieces;
import java.io.*;
import java.net.Socket;
public class TcpClient{
private Socket socket;
private ObjectInputStream ois;
private ObjectOutputStream oos;
public TcpClient(Socket socket,ObjectInputStream ois,ObjectOutputStream oos){
this.socket= socket;
this.ois = ois;
this.oos = oos;
}
public Socket getSocket() {
return socket;
}
public void setSocket(Socket socket) {
this.socket = socket;
}
public ObjectInputStream getOis() {
return ois;
}
public void setOis(ObjectInputStream ois) {
this.ois = ois;
}
public ObjectOutputStream getOos() {
return oos;
}
public void setOos(ObjectOutputStream oos) {
this.oos = oos;
}
public void send(Pieces pieces) throws IOException {
oos.writeObject(pieces);
System.out.println(socket+"向服务器发送消息");
}
public Pieces accept() throws IOException, ClassNotFoundException {
Pieces pieces = (Pieces) ois.readObject();
System.out.println(socket+"从服务器读取消息");
return pieces;
}
public void close(){
;
}
}
TcpServer:
package net;
import view.Pieces;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TcpServer {
public static void main(String[] args) {
// 保存客户端处理的线程
ArrayList<UserThread> userList = new ArrayList<>();
// 固定大小的线程池只支持两个线程,用来处理客户端
ExecutorService es = Executors.newFixedThreadPool(2);
try {
ServerSocket server = new ServerSocket(10086);
System.out.println("服务器已经启动,正在等待客户端连接......");
while (true) {
//接收客户端的Socket,如果没有客户端连接就一直卡在这里
Socket socket = server.accept();
// 每来一个用户就创建一个线程
UserThread user = new UserThread(socket, userList);
// 开启线程
es.execute(user);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
class UserThread implements Runnable {
private Socket socket = null;
private static ArrayList<UserThread> list; // 客户端线程集合
private ObjectOutputStream oos;
private ObjectInputStream ois;
private boolean flag = true;// 标记
public UserThread(Socket socket, ArrayList<UserThread> list) {
this.socket = socket;
this.list = list;
list.add(this); // 把当前线程加入list中
}
@Override
public void run() {
UserThread user = null;
try {
System.out.println("客户端:" + socket.getInetAddress().getHostAddress() + "已经连接");
ois = new ObjectInputStream(socket.getInputStream());
oos = new ObjectOutputStream(socket.getOutputStream());
while(true){
Pieces pieces = (Pieces) ois.readObject(); // 客户端发给服务端的消息,把他写到其它套接字中去
int size = list.size();
for (int i = 0; i < size; i++) {
user = list.get(i);
if (user.socket != socket) {
user.oos.writeObject(pieces);
System.out.println("从"+socket+"向"+user.socket+"发送消息");
}
}
}
} catch(SocketException e){ // todo 客户端掉线后,移除客户端。没想好{1.从客户端列表移除当前元素,关闭当前:socket,通知另一方:这一方已经掉线,然后关闭这一方的socket}
try {
int i = list.size();
if (i ==2){
list.remove(user);
System.out.println("已经删除了一个客户端");
list.get(0).oos.writeObject(new Pieces("对方掉线"));
}else if (i==1){
list.remove(0);
System.out.println("又移除了另一个客户端");
}
} catch (IOException ex) {
ex.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
ChessBoard:
/*
* 1.变量值不变的问题
* 2.输入输出流先后顺序的问题(socket阻塞)
* 3.socket 短连接不关闭输入输出流,为何看起来就像长连接一样(长短连接的区别是什么)
* */
// todo 一个提示框
package view;
import Media.Music.PlayMusic;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
public class ChessBoard extends JFrame implements ActionListener {
private JButton PlayMusic = new JButton("播放音乐");
private ChessPanel chessPanel;
private Panel CommandPanel = new Panel();
private JButton reStart = new JButton("重新开始");
private JButton fail = new JButton("认输");
private JButton Regret = new JButton("悔棋");
private String command=null; // 触发按钮后发送的命令
private PlayMusic music = null;
private int count = 1;
// todo 静态语句块
{
try {
music = new PlayMusic("./src/Media/Music/bg3.wav");
} catch (LineUnavailableException e) {
e.printStackTrace();
} catch (UnsupportedAudioFileException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public ChessBoard() {
this.setTitle("欢乐五子棋");
chessPanel = new ChessPanel();
this.add(chessPanel,BorderLayout.CENTER);
reStart.addActionListener(this);
fail .addActionListener(this);
Regret.addActionListener(this);
PlayMusic.addActionListener(this);
CommandPanel.add(reStart);
CommandPanel.add(fail);
CommandPanel.add(Regret);
CommandPanel.add(PlayMusic);
this.add(CommandPanel,BorderLayout.SOUTH);
this.setBounds(10, 10, 800, 800);
this.setVisible(true);
this.setResizable(false);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
ChessBoard Board = new ChessBoard();
}
@Override
public void actionPerformed(ActionEvent e) {
if(e.getSource()==reStart){
command ="重新开始";
chessPanel.canPlay = true;
}else if(e.getSource()==fail){
command ="认输";
JOptionPane.showMessageDialog(chessPanel,"It's a pity,you have fail the game!");
chessPanel.canPlay = false;
}else if (e.getSource()==Regret){
command ="悔棋";
}else if (e.getSource()==PlayMusic){
// todo 播放音乐,单数次播放;
if (count%2==1){
music.play();
}else {
music.stop();
}
count++;
command = null;
}
if(command!=null){
Pieces pieces = new Pieces(command);
try {
this.chessPanel.client.send(pieces);
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
ChessPanel:
package view;
// 五子棋面板,就是在这里面画图。
// todo 背景图片 ,也许一个背景音乐
import net.TcpClient;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ConnectException;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Iterator;
public class ChessPanel extends JPanel implements MouseListener{
// TODO 从服务器接收来的棋子 ,值不变有问题
// Pieces accept_pieces = new Pieces();
// Pieces send_pieces = new Pieces();
whoWin ifWin =new whoWin() ; // 是否胜利
TcpClient client = null; // 客户端
boolean canPlay = true; // 是否能继续玩
boolean isBlack = true; // 是否黑子,黑1,白2
ArrayList<Pieces> allPieces = new ArrayList<>(); // 存储棋子对象,就是通过这个画图的
int [][] allChess = new int[19][19];
int PanelWidth;
int PanelHeight;
int width = 600;
int height = 600;
int temp = width / 18;
int xbase,ybase;
Image image = Toolkit.getDefaultToolkit().getImage("./src/Media/bg.jpeg"); // "./"表示当前项目下
public ChessPanel(){
this.addMouseListener(this);
try {
Socket socket = new Socket("172.27.29.190", 10086);
//TODO 构建输出输入流,输入输出流问题,先输出后输入
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
client = new TcpClient(socket,ois,oos);
new Thread(new getMessage()).start(); // 开启读取的线程
} catch (ConnectException e){
System.out.println("服务器拒绝连接!");
} catch (IOException e) {
e.printStackTrace();
}catch(Exception e ){
e.printStackTrace();
}
}
// 画图部分
public void paintComponent(Graphics g) {
super.paintComponent(g);
PanelWidth = this.getSize().width; // 这两步骤
PanelHeight = this.getSize().height;
xbase = (PanelWidth - width) / 2;
ybase = (PanelHeight - height) / 2;
Graphics2D g2d = (Graphics2D) g;
// this.setBackground(new Color(246, 186, 114));
g2d.drawImage(image,0,0,this.getWidth(),this.getHeight(),this);
int x1, y1, x2, y2;
// 画线
for (int i = 0; i < 19; i++) {
if (i == 0 || i == 18) {
g2d.setStroke(new BasicStroke(3.0f));
} else g2d.setStroke(new BasicStroke(1.0f));
x1 = xbase + temp * i;
y1 = ybase;
y2 = ybase + 18 * temp;
g2d.drawLine(x1, y1, x1, y2);
x1 = xbase;
y1 = ybase + temp * i;
x2 = xbase + temp * 18;
g2d.drawLine(x1, y1, x2, y1);
}
// 开始画棋子
int radius ;
int xPos,yPos;
Iterator it = allPieces.iterator(); // 迭代器遍历arraylist
while(it.hasNext()){
Pieces pieces = (Pieces) it.next();
radius = pieces.getRadius();
xPos = pieces.getxPos();
yPos = pieces.getyPos();
System.out.println(pieces.getColor()+","+pieces.getxPos()+","+pieces.getyPos());
if (pieces.getColor() == 1){
g2d.setColor(Color.black);
g2d.fillOval(xPos*temp+xbase-radius/2,yPos*temp+ybase-radius/2,radius,radius);
}
else if (pieces.getColor() == 2) {
g2d.setColor(Color.white);
g2d.fillOval(xPos * temp + xbase - radius / 2, yPos * temp + ybase - radius / 2, radius, radius);
}
}
}
@Override
public void mousePressed(MouseEvent e) {
int x ,y ;
if (canPlay) {
x = e.getX();
y = e.getY();
// 判断鼠标点击位置
if (x >= xbase & x <= (xbase + 18 * temp) & y >= ybase & y < (ybase + 18 * temp)) {
// 判断是不是下在空的位置
int tempX = (x - xbase) / temp, tempY = (y - ybase) / temp;
// todo 这里是关键判断这点坐标的数组下标是什么
if ((x - xbase) % temp > temp / 2) {
x = tempX + 1;
} else
x = tempX;
if ((y - ybase) % temp > temp / 2)
y = tempY + 1;
else
y = tempY;
// 先判断有没有棋子,处理没有棋子的情况
if (allChess[x][y] != 0) {
JOptionPane.showMessageDialog(this, "这里有棋子了");
} else {
Pieces send_pieces = new Pieces();
send_pieces.setxPos(x);
send_pieces.setyPos(y);
if (isBlack){
send_pieces.setColor(1);
allChess[x][y] = 1;
isBlack = false;
}
else{
send_pieces.setColor(2);
allChess[x][y]=2;
isBlack = true;
}
allPieces.add(send_pieces); // 向棋子队列加入当前棋子
canPlay = false;// canPlay在true情况下, 点击一次后不能点击了
this.repaint();
ifWin = new whoWin(allChess,x,y);
// 如果赢了,就不能玩了,并且给出提示消息,你赢了;
if(ifWin.isWin()){
canPlay = false;
JOptionPane.showMessageDialog(this,"Congratulations you have won tha game !");
}
try {
if (client!=null){
client.send(send_pieces);
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
}
// 读取来自服务器端的信息
class getMessage implements Runnable{
private boolean flag = true;
public void setFlag(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
// 循环读
while(flag){
if(client!=null){
try {
Pieces accept_pieces = client.accept();
String command = accept_pieces.getCommand();
int color = accept_pieces.getColor();
switch (command){
case "发送":{
canPlay = true;
if (color == 1){
isBlack = false;//对方为黑我为白
}else{
isBlack = true;
}
allPieces.add(accept_pieces);
allChess[accept_pieces.getxPos()][accept_pieces.getyPos()]= accept_pieces.getColor();
ChessPanel.this.repaint();
ifWin.setY(accept_pieces.getyPos());
ifWin.setX(accept_pieces.getxPos());
ifWin.setAllChess(allChess);
if(ifWin.isWin()){
canPlay = false;
JOptionPane.showMessageDialog(ChessPanel.this,"It's a pity you have fail the game!");
}
break;
}
case "悔棋":{
int i = JOptionPane.showConfirmDialog(ChessPanel.this,"对方请求悔棋,是否同意!");
// yes 0,no 1,cancel 2,closed -1
Pieces pieces = new Pieces();
if (i == 0){
// 同意悔棋:1.同意对方悔棋
pieces.setCommand("同意悔棋");
// arraylist 去除末尾的两个值,对应allChess置0
int size = allPieces.size();
for (int j = 1;j<=2;j++){
allChess[allPieces.get(size-j).getxPos()][allPieces.get(size-j).getyPos()]=0;
allPieces.remove(size-j);
}
ChessPanel.this.repaint();
}else if(i==1){
pieces.setCommand("不同意悔棋");
}
client.send(pieces);
break;
}
case "认输":{ // 不能继续玩下去,你已经胜利
JOptionPane.showMessageDialog(ChessPanel.this,"对方认输");
canPlay = false;
JOptionPane.showMessageDialog(ChessPanel.this,"Congratulations you have won tha game !");
break;
}
case "重新开始":{ // 是否同意重新开始
int i = JOptionPane.showConfirmDialog(ChessPanel.this,"对方请求重新开始,是否同意");
Pieces pieces = new Pieces();
if(i == 0){// allChess 和 allPieces全部置0;
pieces.setCommand("同意重新开始");
int size = allPieces.size();
System.out.println("arraylist 长度:"+size);
for (int j = 0;j<size;j++){// 移除队首元素
allChess[allPieces.get(0).getxPos()][allPieces.get(0).getyPos()] = 0;
allPieces.remove(0);
}
canPlay = true;
ChessPanel.this.repaint();
}else if (i ==1){
pieces.setCommand("不同意重新开始");
}
client.send(pieces);
break;
}
case "同意悔棋":{// allpieces 和 allchess 回退
JOptionPane.showMessageDialog(ChessPanel.this,"对方同意悔棋");
int size = allPieces.size();
for (int j = 1;j<=2;j++){
allChess[allPieces.get(size-j).getxPos()][allPieces.get(size-j).getyPos()]=0;
allPieces.remove(size-j);
}
ChessPanel.this.repaint();
break;
}
case "不同意悔棋":{
JOptionPane.showMessageDialog(ChessPanel.this,"对方不同意悔棋");
break;
}
case "同意重新开始":{ // 全部置0,调用repaint 方法
JOptionPane.showMessageDialog(ChessPanel.this,"对方同意重新开始");
int size = allPieces.size();
for (int j = 0;j<size;j++){ // todo 移除队首元素arraylist 长度改变;序列也发生改变
allChess[allPieces.get(0).getxPos()][allPieces.get(0).getyPos()] = 0;
allPieces.remove(0);
}
canPlay = true;
ChessPanel.this.repaint();
break;
}
case "不同意重新开始":{
JOptionPane.showMessageDialog(ChessPanel.this,"对方不同意重新开始");
break;
}
case "对方掉线":{ // 对方已经掉线
JOptionPane.showMessageDialog(ChessPanel.this,"不好意思,对方已经掉线!");
canPlay = false;
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
}
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
}
Pieces:
package view;
import java.io.Serializable;
// 存储棋子的相关信息
public class Pieces implements Serializable {
private int radius = 16;
private int color = 0;
private int xPos ;
private int yPos;
private String command = "发送";
public String getCommand() {
return command;
}
public void setCommand(String command) {
this.command = command;
}
public Pieces(int color, int xPos, int yPos){
this.color = color;
this.xPos = xPos;
this.yPos = yPos;
}
public Pieces(){
}
public Pieces(String command){
this.command = command;
}
public int getRadius() {
return radius;
}
public void setRadius(int radius) {
this.radius = radius;
}
public int getColor() {
return color;
}
public void setColor(int color) {
this.color = color;
}
public int getxPos() {
return xPos;
}
public void setxPos(int xPos) {
this.xPos = xPos;
}
public int getyPos() {
return yPos;
}
public void setyPos(int yPos) {
this.yPos = yPos;
}
}
WhoWin:
package view;
public class whoWin { // 判断是谁赢了
private int allChess[][] = new int[19][19];
private int x = 0;
private int y = 0;
public whoWin(){
}
public whoWin(int allChess[][],int x,int y){
this.allChess = allChess;
this.x = x;
this.y = y;
}
public void setAllChess(int allChess[][]){
this.allChess = allChess;
}
public void setX(int x){
this.x = x;
}
public void setY(int y ){
this.y = y;
}
public boolean isWin() {
int color = allChess[x][y];
int count;
count = this.Count(1, 0, color); // 对横方向的判断
if (count >= 5) {
return true;
} else {
count = this.Count(0, 1, color); // 对竖方向的判断
if (count >= 5) {
return true;
} else {
count = this.Count(1, 1, color); // 对左上方向的判断
if (count >= 5)
return true;
else {
count = this.Count(1, -1, color); // 对右上方向的判断
if (count >= 5)
return true;
}
}
}
return false;
}
private int Count(int xChange, int yChange, int color) {
int count = 1;
int tempX = xChange, tempY = yChange;
while (color == allChess[x + xChange][y + yChange]) {
count++;
if (xChange != 0) {
xChange++;
}
if (yChange != 0) {
if (yChange > 0)
yChange++;
else
yChange--;
}
}
xChange = tempX;
yChange = tempY;
while (color == allChess[x - xChange][y - yChange]) {
count++;
if (xChange != 0)
xChange++;
if (yChange != 0) {
if (yChange > 0)
yChange++;
else
yChange--;
}
}
return count;
}
}
最后,希望我的代码能对你有所帮助,大家一起加油呀!
以上就是本文的全部内容,希望对大家的学习有所帮助。
java版 联机五子棋游戏
- Author -
是阿砚没错- Original Sources -
声明:登载此文出于传递更多信息之目的,并不意味着赞同其观点或证实其描述。
Tags in this post...
Reply on: @reply_date@
@reply_contents@