使用Java語言編寫一個五子棋UI界面並實現網絡對戰功能(非局域網)


使用Java語言編寫一個五子棋UI界面並實現網絡對戰功能(非局域網)

一,前期准備

1,Java IDE(Eclipse)與JDK的安裝與配置
jdk-15.0.1-免配置路徑版
提取碼:earu
免安裝版Eclipse 解壓即可使用
提取碼:5iyy

網絡上很多配置jdk的方法,我不再重復
這里提供一種便捷操作的方法(針對新手)
由於高版本jdk不需要手動配置路徑,將我上傳的jdk資源下載后一鍵安裝,路徑即可自動配置

2,一台雲主機

阿里雲,騰訊雲,華為雲的雲主機均可,我用的是windows系統
(window是自帶的遠程連接很方便),如果想用其他的也可,最好選擇一個有桌面的,這樣調試起來容易些
在雲主機上同樣需要安裝Eclipse與配置jdk,步驟同上
如果內存較大的可以安裝數據庫,這樣編寫的程序上可以加賬號登錄注冊功能

我的雲主機
我的雲主機
3,另一台可供測試可以聯網的電腦或虛擬機

建議方便的同學用另一台電腦,一台電腦用手機熱點,另一台用WiFi
這樣可以測試外網的連接情況
  • 1
  • 2

4,轉換Java Jar為exe文件的軟件(如exe4j)

網上很多關於轉換的教程(非必須,如果不需要可以忽略這一步)
  • 1

二,功能分析與效果展示
1,這個程序主要分為三部分,UI界面,單機落子部分,聯網落子部分,而UI界面又分為登錄界面和棋盤界面。在這篇文章中UI界面與聯網落子部分為講述重點。
2,登錄界面實現的功能有以下幾點,首先當啟動程序時,應自動檢測與服務器的連接,如果連接失敗,則不出現網絡登錄入口,如果連接成功,則出現網絡對戰登錄入口。

連接失敗效果展示
在這里插入圖片描述
在這里插入圖片描述
連接成功效果展示
在這里插入圖片描述
在這里插入圖片描述
3,棋盤界面應滿足的功能,黑白棋的落子,判斷勝利,重新開始
棋盤效果展示
在這里插入圖片描述
4,網絡對戰應滿足的功能,由於很多電腦使用路由器與外網訪問(有的通信服務提供商會隱藏真實ip,故兩台由不同路由器連接的電腦很難建立連接),同時增加編寫難度,采用下棋雙方與服務器連接的方式,A->服務器<-B,A<-服務器->B,程序應做到迅速響應服務器信息,減少延遲,雙方棋盤信息應一致。
在這里插入圖片描述
三,具體實現方法
1,棋盤UI的實現

 JPanel jpan1 = new JPanel() { //根據新棋盤信息作圖,覆蓋原有Panel private static final long serialVersionUID = 1L; public void paint(Graphics graphics){ //重構paint函數 int xst=20,yst=20,add=32; for(int t=0;t<15;t++) //畫豎線 { graphics.drawLine(xst,yst,xst,468); xst=xst+add; } xst=20;yst=20;add=32; for(int t=0;t<15;t++) //畫橫線 { graphics.drawLine(xst,yst,468,yst); yst=yst+add; } graphics.setColor(Color.BLACK); //畫棋盤上五個黑點 graphics.fillOval(113, 113, 6, 6); graphics.fillOval(369, 113, 6, 6); graphics.fillOval(113, 369, 6, 6); graphics.fillOval(369, 369, 6, 6); graphics.fillOval(241, 241, 6, 6); for(int t=0;t<15;t++) //根據棋盤數組里存儲的棋子信息畫黑白子 { for(int t1=0;t1<15;t1++) { if(node[t][t1]==1) { graphics.setColor(Color.BLACK); graphics.fillOval(t1*32+20-13,t*32+20-13,26,26); } if(node[t][t1]==-1) { graphics.setColor(Color.WHITE); graphics.fillOval(t1*32+20-13,t*32+20-13,26,26); } } } } }

由於每次落子棋盤都會發生變化,所以設置一個鼠標觸發事件,當每次觸發都將窗口重繪,根據棋盤信息數組里的內容更新到當前局面。
2,網絡對戰(服務器端編程)
網絡對戰的實質是socket編程,即客戶端A將落子信息傳給服務器,服務器將信息傳給客戶端B,接着客戶端B將落子信息傳給服務器,服務器傳給客戶端A,故在服務器端編程中應監聽兩個端口(我設置的是1075和1056)客戶端A將信息通過1075端口傳給服務器,服務器將A傳過來的信息通過1056傳給服務器B,默認先連接的是黑子,當黑子連接成功后,監聽白子連接。

package cilent; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.Scanner; public class test { public static void main(String[] args) { ServerSocket server,server1; try { server = new ServerSocket(1075); Socket socket=server.accept(); System.out.println("black is ok"); server1 = new ServerSocket(1056); Socket socket1=server1.accept(); System.out.println("white is ok"); while(true){ System.out.println("----------"); InputStream in,in1; try { in = socket.getInputStream(); byte [] b=new byte[1024]; StringBuffer sb=new StringBuffer(); String s; if(in.read(b) !=-1){ s=new String(b); sb.append(s); } OutputStream out1=socket1.getOutputStream(); System.out.println("黑發給白"+sb); out1.write(sb.toString().getBytes()); out1.flush(); in1 = socket1.getInputStream(); byte [] b1=new byte[1024]; StringBuffer sb1=new StringBuffer(); String s1; if(in1.read(b1) !=-1){ s1=new String(b1); sb1.append(s1); } OutputStream out=socket.getOutputStream(); System.out.println("白發給黑"+sb1); out.write(sb1.toString().getBytes()); out.flush(); } catch (IOException e) { // e.printStackTrace(); } } } catch (IOException e) { e.printStackTrace(); } } } 

 

3,網絡對戰(客戶端編程)
在客戶端這邊不僅要考慮數據的發送與接收,還要考慮接收或發送的數據在窗體上如何實時的顯示,為此我自己創立了一套編碼解碼方式,為方便每次發送的信息的格式為XX*YY,前兩位為二維數組行數,后兩位為二維數組列數,發送部分代碼如下

 if(xrec<=9) //確認發送數據格式 XX*XX XX指二維數組列,行 sent="0"+xrec; else sent=""+xrec; sent=sent+"*"; if(yrec<=9) sent=sent+"0"+yrec; else sent=sent+""+yrec; System.out.println("=========="); try { socket.getOutputStream().write(sent.getBytes()); System.out.println("MY sent:"+sent); } catch (IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } try { socket.getOutputStream().flush(); } catch (IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } //發送完畢 

 

由於socket的接收函數有阻塞性,當執行接收函數時,程序被阻塞,窗體無法及時更新,這樣就會出現無法更新落子信息,當接收到對方落子時一次更新兩個棋子的情況,為解決這個問題,將本機落子與接收落子分隔開,當鼠標按下時更新本機落子,當鼠標松開時接收服務器信息。

void jieshou(Socket socket, JFrame jFrame) { //temp=1; InputStream in = null; try { in = socket.getInputStream(); } catch (IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); JOptionPane.showMessageDialog(null,"對方未就緒!"); } byte[] b = new byte[1024]; StringBuffer sb = new StringBuffer(); try { if (in.read(b) != -1) { s = new String(b); System.out.println(s); sb.append(s); } } catch (IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); JOptionPane.showMessageDialog(null,"對方未就緒!"); } System.out.println("來自服務器的數據:" + sb); //收到對方落子信息 int xnew=(sb.charAt(0)-'0')*10+(sb.charAt(1)-'0');//解碼 int ynew=(sb.charAt(3)-'0')*10+(sb.charAt(4)-'0'); if(node[xnew][ynew]==0) { //更改對方落子 node[xnew][ynew]=-1; // m=1; } JPanel jpan1 = new JPanel() { //根據新棋盤信息作圖,覆蓋原有Panel private static final long serialVersionUID = 1L; public void paint(Graphics graphics){ super.paint(graphics); int xst=20,yst=20,add=32; for(int t=0;t<15;t++) { graphics.drawLine(xst,yst,xst,468); xst=xst+add; } xst=20;yst=20;add=32; for(int t=0;t<15;t++) { graphics.drawLine(xst,yst,468,yst); yst=yst+add; } graphics.setColor(Color.BLACK); graphics.fillOval(113, 113, 6, 6); graphics.fillOval(369, 113, 6, 6); graphics.fillOval(113, 369, 6, 6); graphics.fillOval(369, 369, 6, 6); graphics.fillOval(241, 241, 6, 6); for(int t=0;t<15;t++) { for(int t1=0;t1<15;t1++) { if(node[t][t1]==1) { graphics.setColor(Color.BLACK); graphics.fillOval(t1*32+20-13,t*32+20-13,26,26); } if(node[t][t1]==-1) { graphics.setColor(Color.WHITE); graphics.fillOval(t1*32+20-13,t*32+20-13,26,26); } } } } }; jFrame.add(b1); jFrame.add(jpan1); jFrame.setVisible(true); //temp=0; }

 

如果有興趣的同學也可以在服務器端加一個本地服務器(推薦SQL server)搭配jdbc實現一個客戶端登錄程序(類似QQ),具體如何實現我會在下節詳細敘述。
有需要的可以給我留言,分享源碼

很多人將我的文章不加修改就復制到CSDN去,還標明原創,這是極大的侵權,我已經將你們舉報,請你們立即刪除,並道歉!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM