使用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
這樣可以測試外網的連接情況

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

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

二,功能分析與效果展示
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),具體如何實現我會在下節詳細敘述。
有需要的可以給我留言,分享源碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章