在完成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()