16 Java 开发实例网络五子棋游戏.docx
16 Java 开发实例网络五子棋游戏16 章 Java 开发实例网络五子棋游戏 使用Eclipse 集成开发工具可以方便地开发Java 应用程序,本节就以一个综合实例对前面章节学过的内容进行总结与提升。读完本章后,读者能够清楚地知道使用Eclipse 开发Java应用程序的完整流程。 16.1 Java Socket 编程基础 由于本章采用的实例需要用到 Java的 Socket 类实现网络通信,首先来介绍一下 JavaSocket 编程的基础知识,以便读者能够相对轻松地阅读后面的章节。 16.1.1 Socket 简介 Socket 通常称为“套接字”,它是一个封装了IP 地址和端口通信链的句柄,应用程序通 常通过 “套接字”向网络发出请求或者应答网络请求。Socket 可以视为两个程序进行的通信 连接的端点,一个程序将信息写入Socket 中,该Socket 将信息发送到对方 Socket 中,通信 另一方的应用程序通过读取Socket 来获取相应得信息。 Socket 的通信过程如图16-1 所示。 由图16-1 可以看到,Socket 通信的过程如下。 主机 A 上的程序A 将一段信息写入Socket 中,Socket 的内容被主机A 的网络管理软件访问后通过主机A 的网络接口卡发送到主机 B 。 主机 B 的网络接口卡将接收到的信息传送给主机 B 的网络管理软件,网络管理软件将这段信息保存在主机 B 的Socket 中,然后程序B 在自己的Socket 中阅读这段信息。 16.1.2 Java Socket 的基本类 网络编程的基本模型是客户机/服务器模型,即通常所说的C/S 结构。Java SDK 提供一些API 类来完成客户机Socket 和服务器Socket 的构建与通信,这些类存在于J 包中。常用的类有Socket 和ServerSocket 类。 Socket 类。当客户程序需要与服务器程序通信的时候,客户程序要在客户机创建一个 Socket 对象。Socket 类常用的构造函数是 Socket(String host, int port) ,它创建一个基于Socket 的连接服务器 流套接字的客户 流套接字。如果创建了一个Socket 对象,它可以通过调用 Socket 的 getInputStream方法从服务程序获得输入流传送来的信息,也可以通过调用Socket 的 getOutputStream方法获得输出流,来发送消息。在读写活动完成之后,客户程序必须调用close方法关闭流和流套接字。 ServerSocket 类。服务器端有一个专门的ServerSocket 对象,ServerSocket 有几个构造函数,最简单的是ServerSocket(int port) 。服务 首先构造一个ServerSocket 对象,创建完成后就开始准备接收连接请求。接下来服务程序进入循环,循环从调用 ServerSocket 的accept方法开始 ,建立连接后accept返回一个绑定了客户程序的IP 地址或端口 号的Socket 对象。由1 于存在单个服务程序与多个客户程序通信的可能,解决方法是 服务器 主机运行一个处理服务程序和客户程序的通信后台线程。 16.2 网络五子棋程序的代码结构详解 本实例是一个简单的网络五子棋程序。程序由两个部分组成:一部分为服务器 ,另一部分为五子棋客户 。其中服务器 以消息方式完成客户 的管理,客户 支持对弈和聊天。实例源代码由com.fivechess.chessface、com.fivechess.client 和com.fivechess.server 3 个包组成。 注意 详细注释都写在代码中。 16.2.1 com.fivechess.chessface 此包主要包含与游戏客户 界面有关的一些类文件,如 chessPad、chatPad、controlPad和inputPad 等。下面分别介绍这几个文件。 1chatPad chatPad 类是一个用户界面组件,其本身是一个具有带有滚动条的文本域的Panel,用户的聊天信息和命令的执行结果都将在此文本域上显示。chatPad 的实现代码如实例16-1 所示。 chatPad package com.fivechess.chessface; import java.awt.BorderLayout; import java.awt.Panel; import java.awt.TextArea; /* * author wufenghanren * 聊天信息Panel。Panel 上的文本域可以显示用户聊天信息 */ public class chatPad extends Panel public TextArea chatLineArea = new TextArea ("", 18, 30, TextArea.SCROLLBARS_VERTICAL_ONLY); public chatPad setLayout (new BorderLayout ); add (chatLineArea, BorderLayout.CENTER); 2userPad chatPad 类也是一个用户界面组件,它是一个带有列表框的Panel,所有的用户名都将显 示在列表中。userPad 的实现代码如实例16-2 所示。 userPad package com.fivechess.chessface; import java.awt.*; /* * author wufenghanren * 用户列表Panel。此Panel 维护一个服务器的当前用户列表 * 所有的用户名都将显示在列表中 2 */ public class userPad extends Panel public List userList = new List (10); public userPad setLayout (new BorderLayout ); for (int i = 0; i < 30; i+) userList.add (i + "." + "当前暂无用户"); add (userList, BorderLayout.CENTER); 3inputPad inputPad 类的 Panel 左边的下拉列表中可以列出所有用户的名字,右边的文本框中可以 输入想要发送的消息,点击回车,信息被发送。此外,在文本框中还可以使用简单的命令, 如 changename 、list 。inputPad 的实现代码如实例16-3 所示。 inputPad package com.fivechess.chessface; import java.awt.Choice; import java.awt.Color; import java.awt.FlowLayout; import java.awt.Label; import java.awt.Panel; import java.awt.TextField; /* * author wufenghanren * 输入信息Panel。Panel 左边的下拉列表中列出了所有用户的名字 * 右边的文本框可以输入聊天信息,点击回车信息被发送 * 此外还可以 在文本框中输入命令如changename、list等 */ public class inputPad extends Panel public TextField inputWords = new TextField("点击回车发送信息", 20); public Choice userChoice = new Choice ; public Label chatLabel = new Label("输入发送信息:"); public inputPad setLayout (new FlowLayout (FlowLayout.LEFT); setBackground (new Color (204, 204, 204); for (int i = 0; i < 30; i+) userChoice.addItem (i + "." + "当前暂无用户"); userChoice.setSize (60, 24); add (userChoice); add (chatLabel); add (inputWords); 3 4controlPad controlPad 类的Panel 上包含整个客户 的控制按钮,如 “连接主机”、“建立游戏”、“加入游戏”和“选择界面颜色”等。点击这些按钮会完成相应的 能。controlPad的实现代码如实例16-4 所示。 controlPad package com.fivechess.chessface; import java.awt.Button; import java.awt.Color; import java.awt.FlowLayout; import java.awt.Label; import java.awt.Panel; import java.awt.TextField; /* * author wufenghanren * 控制Panel。此Panel 上的按钮如其名,完成相应的 能 */ public class controlPad extends Panel public Label IPlabel = new Label("服务器IP:", Label.LEFT); public TextField inputIP = new TextField ("localhost", 10); public Button connectButton = new Button ("连接主机"); public Button creatGameButton = new Button ("建立游戏"); public Button joinGameButton = new Button ("加入游戏"); public Button cancelGameButton = new Button("放弃游戏"); public Button colorChangeButton = new Button ("选择界面颜色"); public Button exitGameButton = new Button ("关闭程序"); /构造函数,负责Panel 的初始布局 public controlPad setLayout (new FlowLayout (FlowLayout.LEFT); setBackground (new Color (204,204,204); add (IPlabel); add (inputIP); add (connectButton); add (creatGameButton); add (joinGameButton); add (cancelGameButton); add (colorChangeButton); add (exitGameButton); 5chessPad 几个界面组件中最复杂的是chessPad,它不仅仅是一个包含棋盘和棋子的Panel,除此之 外还4 要完成绘制棋子、保存处理落子后棋子的位置信息、判断棋局胜负和对棋局终了时进行 棋盘的重新清理和初始化等操作。chessPad 包含了3 个类:一个是绘制棋盘的Panel、一个表 示黑子的类,一个表示白子的类。chessPad 的实现代码如实例16-5 所示。 chessPad package com.fivechess.chessface; import java.awt.*; import java.awt.event.*; import java.io.*; import .*; /* * author wufenghanren * 显示棋盘的Panel。此Panel 实现了鼠标监听器 */ public class chessPad extends Panel implements MouseListener public int chessPoint_x = -1, chessPoint_y = -1, chessColor = 1; int chessBlack_x = new int200;/黑子的x 坐标 int chessBlack_y = new int200;/黑子的y 坐标 int chessWhite_x = new int200;/白子的x 坐标 int chessWhite_y = new int200;/白子的y 坐标 int chessBlackCount = 0, chessWhiteCount = 0; int chessBlackWin = 0, chessWhiteWin = 0; public boolean isMouseEnabled = false, isWin = false, isInGame = false; public Label statusLabel = new Label("客户 状态"); public TextField statusText = new TextField("请先连接服务器"); /显示客户 状态的文本框 public Socket chessSocket; DataInputStream inData; DataOutputStream outData; public String chessSelfName = null;/己方的名字 public String chessPeerName = null;/对方的名字 public String host = null; public int port = 4331; public chessThread chessthread = new chessThread (this); /* * 棋盘Panel 的构造函数 */ public chessPad setSize (440, 440); setLayout (null); setBackground (new Color (204, 204, 204); addMouseListener(this); add (statusLabel); statusLabel.setBounds(30, 5, 70, 24); add (statusText); 5 statusText.setBounds(100, 5, 300, 24); statusText.setEditable (false); /* * 和服务器通信的函数 */ public boolean connectServer(String ServerIP, int ServerPort) throws Exception try /利用参数创建一个Socket 的实例来完成和服务器之间的信息交换 chessSocket = new Socket (ServerIP, ServerPort); inData = new DataInputStream (chessSocket.getInputStream ); outData = new DataOutputStream (chessSocket.getOutputStream ); chessthread.start ; return true; catch (IOException ex) statusText.setText ("chessPad:connectServer:无法连接 n"); return false; /* * 一方获胜时对棋局的处理 */ public void chessVictory (int chessColorWin) /清除所有的棋子 this.removeAll; /将保存所有黑棋和白棋的位置坐标的数组清空,为下一盘棋做准备 for (int i = 0; i <= chessBlackCount; i+) chessBlack_x i = 0; chessBlack_yi = 0; for (int i = 0; i <= chessWhiteCount; i+) chessWhite_x i = 0; chessWhite_yi = 0; chessBlackCount = 0; chessWhiteCount = 0; add (statusText); statusText.setBounds(40, 5, 360, 24); /如果黑棋获胜,计算双方获胜盘数,将双方的战绩在状态文本框中显示出来 if (chessColorWin = 1) chessBlackWin+; statusText.setText ("黑棋胜,黑:白为" + chessBlackWin + ":" + chessWhiteWin + ",重新开局,等待白棋下子."); /白棋获胜,同上 6 else if (chessColorWin = -1) chessWhiteWin+; statusText.setText ("白棋胜,黑:白为" + chessBlackWin + ":" + chessWhiteWin + ",重新开局,等待黑棋下子."); /* * 将各个棋子的坐标保存在数组里 */ public void getLocation (int a, int b, int color) if (color = 1) chessBlack_x chessBlackCount = a * 20; chessBlack_ychessBlackCount = b * 20; chessBlackCount+; else if (color = -1) chessWhite_x chessWhiteCount = a * 20; chessWhite_ychessWhiteCount = b * 20; chessWhiteCount+; /* * 依据五子棋的行棋规则判断某方获胜 */ public boolean checkWin (int a, int b, int checkColor) int step = 1, chessLink = 1, chessLinkTest = 1, chessCompare = 0; if (checkColor = 1) chessLink = 1; for (step = 1; step <= 4; step+) for (chessCompare = 0; chessCompare <= chessBlackCount; chessCompare+) if (a + step) * 20 = chessBlack_x chessCompare) && (b * 20) = chessBlack_y chessCompare) chessLink = chessLink + 1; if (chessLink = 5) return (true); if (chessLink = (chessLinkTest + 1) chessLinkTest+; else break; for (step = 1; step <= 4; step+) for (chessCompare = 0; chessCompare <= chessBlackCount; chessCompare+) 7 if (a - step) * 20 = chessBlack_x chessCompare) && (b * 20 = chessBlack_y chessCompare) chessLink+; if (chessLink = 5) return (true); if (chessLink = (chessLinkTest + 1) chessLinkTest+; else break; chessLink = 1; chessLinkTest = 1; for (step = 1; step <= 4; step+) for (chessCompare = 0; chessCompare <= chessBlackCount; chessCompare+) if (a * 20 = chessBlack_x chessCompare) && (b + step) * 20 = chessBlack_y chessCompare) chessLink+; if (chessLink = 5) return (true); if (chessLink = (chessLinkTest + 1) chessLinkTest+; else break; for (step = 1; step <= 4; step+) for (chessCompare = 0; chessCompare <= chessBlackCount; chessCompare+) if (a * 20 = chessBlack_x chessCompare) && (b - step) * 20 = chessBlack_y chessCompare) chessLink+; if (chessLink = 5) return (true); if (chessLink = (chessLinkTest + 1) chessLinkTest+; else 8 break; chessLink = 1; chessLinkTest = 1; for (step = 1; step <= 4; step+) for (chessCompare = 0; chessCompare <= chessBlackCount; chessCompare+) if (a - step) * 20 = chessBlack_x chessCompare) && (b + step) * 20 = chessBlack_ychessCompare) chessLink+; if (chessLink = 5) return (true); if (chessLink = (chessLinkTest + 1) chessLinkTest+; else break; for (step = 1; step <= 4; step+) for (chessCompare = 0; chessCompare <= chessBlackCount; chessCompare+) if (a + step) * 20 = chessBlack_x chessCompare) && (b - step) * 20 = chessBlack_y chessCompare) chessLink+; if (chessLink = 5) return (true); if (chessLink = (chessLinkTest + 1) chessLinkTest+; else break; chessLink = 1; chessLinkTest = 1; for (step = 1; step <= 4; step+) for (chessCompare = 0; chessCompare <= chessBlackCount; chessCompare+) if (a + step) * 20 = chessBlack_x chessCompare) && (b + step) * 20 = chessBlack_y chessCompare) chessLink+; if (chessLink = 5) return (true); 9 if (chessLink = (chessLinkTest + 1) chessLinkTest+; else break; for (step = 1; step <= 4; step+) for (chessCompare = 0; chessCompare <= chessBlackCount; chessCompare+) if (a - step) * 20 = chessBlack_x chessCompare) && (b - step) * 20 = chessBlack_y chessCompare) chessLink+; if (chessLink = 5) return (true); if (chessLink = (chessLinkTest + 1) chessLinkTest+; else break; else if (checkColor = -1) chessLink = 1; for (step = 1; step <= 4; step+) for (chessCompare = 0; chessCompare <= chessWhiteCount; chessCompare+) if (a + step) * 20 = chessWhite_x chessCompare) && (b * 20 = chessWhite_y chessCompare) chessLink+; if (chessLink = 5) return (true); if (chessLink = (chessLinkTest + 1) chessLinkTest+; else break; for (step = 1; step <= 4; step+) for (chessCompare = 0; chessCompare <= chessWhiteCount; chessCompare+) if (a - step) * 20 = chessWhite_x chessCompare) 10 && (b * 20 = chessWhite_ychessCompare) chessLink+; if (chessLink = 5) return (true);