当前位置 博文首页 > 使用Java语言编写一个五子棋UI界面并实现网络对战功能(非局域网

    使用Java语言编写一个五子棋UI界面并实现网络对战功能(非局域网

    作者:AIERSTOM 时间:2021-01-13 18:02

    使用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),具体如何实现我会在下节详细叙述。
    有需要的可以给我留言,分享源码

    下一篇:没有了