用JavaSocket编程开发英语六级词汇学习对战游戏

这里写图片描述
这里写图片描述
首先是游戏的客户端,其中包含的类和函数如下:
1、客户端的GamePanel主类
继承了Runnable,ActionListener,KeyListener接口。 其中的函数有:
初始化整型life = 20,
搭建好用于与服务器收发消息的Socket、BufferedReader、PrintStream,
新建一个Timer,传入的参数(300,this)。
(1)、writeFile函数:
调用时传入两个字符串参数,第一个字符串表示需要写入的文件名,第二个字符串表示将写入的字符串,在这里用于写入判断正确或判断错误的词汇,分两个文件存储写入就好。
(2)、init函数:
不需要传入参数,用于初始化掉落的单词JLabel:lbMoveChar,然后在其中调用readLineFile函数,并且传入两个参数,一个是“filename”,另一个是在词汇文档中用于随机按行读取词汇的整型数据il,接下来,再将四个选项JLabel;lbA,lbB,lbC,lbD,初始化一遍,设置好初始位置。并且lbMoveChar设置好标题,即将readLineFile函数中改写的存有掉落六级词汇的全局变量word,设为它的标题。最后,将lbMoveChar设置在掉落的初始位置。
(3)、checkFail函数
无参数,由于程序框架结构,它用于在每次客户端实现全局变量生命值的整型数life改变后,重新设置一遍lbLife(即显示生命值的一个控件),并且在此函数中实现,若用户如果生命值变为零,则判输,游戏退出,并提示“生命值耗尽,游戏失败!”。
(4)、actionPerformed函数
函数的作用就是实现若词汇掉到用户界面底部,如果两个用户都还没做出选择,两个用户减1分,即若if(lbMoveChar.getY() >= this.getHeight())判断为true,则既将这个掉到用户界面底部的词汇写入wrong.txt,然后调用checkFail(),并且向服务端发送“ASKRN#”;若判断为false,则lbMoveChar控价继续以一定的高度下掉。
(5)、readLineFile函数:
这个函数用synchronized同步锁锁定,因为在我进行编程的过程中,出现过:下落的词汇没有正确选项的情况,经过测试后发现,原来是因为多线程使用重复更新lbA,lbB,lbC,lbD,把四个选项A、B、C、D的值多次重复更新,然后就将本在后面调用的读取词汇中文的更新为此次的选项。

解决方法:直接把synchronized关键字直接加在函数的定义上,使得整个函数都同步,原理为:当某一线程运行同步代码段时,在“同步锁对象”上置一标记,运行完这段代码,标记消除。其他线程要想抢占CPU运行这段代码,必须在“同步锁对象”上先检查该标记,只有标记处于消除状态,才能抢占CPU。函数传入两个参数,一个为读取文件的路径,二个为整型的由服务器产生的随机数il,表示此次读取第几行的词汇。

接下来一个while循环的判断条件为(br.readLine() != null && il >= 0),即表示若文档中仍有下一行,且il大于等于0就继续执行,每一个il–,直到读取到指定行,就把此行存入一字符串中,String的split方法支持正则表达式;正则表达式\s表示匹配任何空白字符,+表示匹配一次或多次。把分离了的词汇与翻译存入一个String数组中,把strs[0]赋给全局字符变量word中。

然后产生一个随机范围为[0,4)的整数,若为0,则将正确选项存入A中,若为1,则将正确选项存入B中,若为2,则将正确选项存入C中,若为3,则存入D中。
(6)、keyPressed函数:
因为主类GamePanel主类继承了KeyListener,所以重写keyPressed函数。先把按的键的值赋给字符keyChar,再使用valueOf将其转换成字符串keyStr。判定keyStr是否和Opt(正确选项)相等,就调用writeFile,把strSave存入路径名为”D:\right.txt”的文档中,且life值自加2,并向服务器发送“LIFE#-1”。否则将其写入路径名为”D:\wrong.txt”的文档中,且life值自减2,并向服务器发送“LIFE#1”。

(7)、GamePanel的构造函数:
布置游戏界面,加入控件:lbLife,lbA,lbB,lbC,lbD,lbMoveChar,并调用init函数初始化,加入addKeyListener。

(8)、run函数:
设置好canRun标识符,控制while循环,初设为true,直到运行时抛出异常,则设置canRun为false,结束循环,并且弹出提示为“游戏异常退出!”的窗口,点确定后退出。
while循环中,一直读取来自服务器端的消息br,并且存入字符串str中,将str用split将“#”分离开,存入strs字符串数组中。若(strs[0].equals(“START”))判断为真,即传来的消息开头为“START”,则把消息中服务器随机产生的数赋值给读取文档用的il,否则若(strs[0].equals(“LIFE”))判断为真,则此消息代表为生命值的增减,将strs[1]使用Integer.parseInt转换为整型,且存入score中。
接下来将生命值的增减加入life中,再调用checkFail()函数,将这个界面面板更新。
且若(strs[2].equals(“START”))判断为真,即代表此时strs[]的格式为“LIFE#-1#START#srn”,则将strs[3]存入il中,否则若(strs[0].equals(“UWIN”))判断为真,则timer.stop(),弹出标题为“游戏结束,你赢了!”,且退出。
(9)、main函数:
调用 GameFrame函数。

2、客户端的GameFrame类:
设置好进入时的一个窗口:标题是输入名称,然后把输入的字符串设置为客户端的标题。
3、服务器端的Server主类
(1)Server的构造函数:
设置“服务器端”为窗口标题,设置好界面,具体见源码。

(2)run函数:
首先调用sendMessage向所有的客户端发送“START#”+wn(随机生成的属于(0,640)的整型数)。然后一个while死循环,条件canRun初始值为true,循环中一直执行读取来自客户端发来的消息,存入字符串str中,然后用split把“#”分离开存入strs数组中。
若(strs[0].equals(“LIFE”))判断为真,即表示此为生命值消息,将生命值转发给所有的客户端,并且产生一个随机数,调用sendMessage把“LIFE#”+strs[1]+“#”+srn发送给所有的客户端。
否则若(strs[0].equals(“WIN”))为真,则说明有一方生命值已经归为0,服务器端将这个消息发送给所有的客户端,因为生命值先减为0的客户端,即发出此消息的客户端先已退出,所以此处不影响它的判断。

客户端运行结果
这里写图片描述
这里写图片描述
这里写图片描述

(1)、客户端代码:
这里写图片描述

package client;

import java.awt.*;
import java.awt.event.*;
import java.awt.event.ActionListener;
import java.awt.event.KeyListener;
import java.io.*;
import java.net.*;
import java.util.Random;
import javax.swing.*;

public class GamePanel extends JPanel implements Runnable, ActionListener,KeyListener {
    private int life = 20;
    private char keyChar;
    private JLabel lbMoveChar = new JLabel();
    private JLabel lbLife = new JLabel();
    //选项ABCD
    private JLabel lbA = new JLabel();
    private JLabel lbB = new JLabel();
    private JLabel lbC = new JLabel();
    private JLabel lbD = new JLabel();

    private Socket  s = null;
    private Timer timer = new Timer(300,this);

    private Random rnd = new Random();
    private BufferedReader br = null;
    private PrintStream ps = null;

    private String word = null;
    private String Opt = null;
    private int il;
    String strSave = null;
    String keyStr = null;

    //int in = 0;
    //int ij = 0;
    //int ic = 0;

    //canRun用来在异常或退出游戏时退出死循环
    private boolean canRun = true;

    public GamePanel(){
        this.setLayout(null);
        this.setBackground(Color.DARK_GRAY);
        this.setSize(1200,500);

        this.add(lbLife);
        lbLife.setFont(new Font("黑体",Font.BOLD,20));
        lbLife.setBackground(Color.yellow);
        lbLife.setForeground(Color.PINK);
        lbLife.setBounds(0,0,500,20);

        this.add(lbA);
        this.add(lbB);
        this.add(lbC);
        this.add(lbD);

        lbA.setForeground(Color.PINK);
        lbB.setForeground(Color.PINK);
        lbC.setForeground(Color.PINK);
        lbD.setForeground(Color.PINK);
        lbA.setBackground(Color.yellow);
        lbB.setBackground(Color.yellow);
        lbC.setBackground(Color.yellow);
        lbD.setBackground(Color.yellow);

        lbA.setFont(new Font("黑体",Font.BOLD,15));
        lbB.setFont(new Font("黑体",Font.BOLD,15));
        lbC.setFont(new Font("黑体",Font.BOLD,15));
        lbD.setFont(new Font("黑体",Font.BOLD,15));

        lbA.setBounds(0,400,300,50);
        lbB.setBounds(300,400,300,50);
        lbC.setBounds(600,400,300,50);
        lbD.setBounds(900,400,300,50);
        //lbA.setText("选项A");
        //lbB.setText("选项B");
        //lbC.setText("选项C");
        //lbD.setText("选项D");


        this.add(lbMoveChar);
        lbMoveChar.setFont(new Font("黑体",Font.BOLD,20));
        lbMoveChar.setForeground(Color.yellow);

        this.init();
        this.addKeyListener( this);
        try{
            s = new Socket("127.0.0.1",9999);

            //JOptionPane.showMessageDialog(this,"连接成功");
            InputStream is = s.getInputStream();
            br = new BufferedReader(new InputStreamReader(is));
            OutputStream os = s.getOutputStream();
            ps = new PrintStream(os);
            new Thread(this).start();

        }catch (Exception ex){
            javax.swing.JOptionPane.showMessageDialog(this,"游戏异常退出!");
            System.exit(0);

        }
        timer.start();

    }



    public void writeFile(String filename, String str){


        try {
            FileOutputStream fos = new FileOutputStream(filename,true);
            byte[] b = str.getBytes();
            fos.write(b);
            fos.close();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }



    public synchronized void readLineFile(String filename, int il){
        try{
            FileInputStream fi = new FileInputStream(filename);
            InputStreamReader isr = new InputStreamReader(fi, "UTF-8");
            BufferedReader br = new BufferedReader(isr);
            //br此处用来按行读取文档
            //in++;
            //System.out.println("readLineFile开始执行:"+in);
            while(br.readLine() != null && il >= 0){
                il--;
                //String str;
                if(il < 0){
                    //用来按行读取,并分割出单词和解释

                    //String的split方法支持正则表达式;
                    //正则表达式\s表示匹配任何空白字符,+表示匹配一次或多次。

                    String str1 = br.readLine();
                    //保存的字符串
                    strSave = str1 + "\r\n";

                    String[] strs1 = str1.split("\\s+");
                    word = strs1[0];

                    System.out.println("1单词:"+word);

                    //将翻译分配给选项
                    System.out.println("1单词源:"+strs1[1]);
                    Random rnd1 = new Random();


                    //rnd.nextInt(300-10+1)+10-->[10,300)
                    //这里的4不能改!!!!
                    int swch = rnd1.nextInt(4);

                    switch (swch) {
                        case 0: {
                            lbA.setText("A:"+strs1[1]);
                            String str2 = br.readLine();
                            String[] strs2 = str2.split("\\s+");
                            lbB.setText("B:"+strs2[1]);
                            String str3 = br.readLine();
                            String[] strs3 = str3.split("\\s+");
                            lbC.setText("C:"+strs3[1]);
                            String str4 = br.readLine();
                            String[] strs4 = str4.split("\\s+");
                            lbD.setText("D:"+strs4[1]);
                            Opt = "A";

                            System.out.println(lbA.getText());
                            System.out.println(lbB.getText());
                            System.out.println(lbC.getText());
                            System.out.println(lbD.getText());

                            break;
                        }
                        case 1:{
                            lbB.setText("B:"+strs1[1]);
                            String str2 = br.readLine();
                            String[] strs2 = str2.split("\\s+");
                            lbA.setText("A:"+strs2[1]);
                            String str3 = br.readLine();
                            String[] strs3 = str3.split("\\s+");
                            lbC.setText("C:"+strs3[1]);
                            String str4 = br.readLine();
                            String[] strs4 = str4.split("\\s+");
                            lbD.setText("D:"+strs4[1]);
                            Opt = "B";

                            System.out.println(lbA.getText());
                            System.out.println(lbB.getText());
                            System.out.println(lbC.getText());
                            System.out.println(lbD.getText());
                            break;
                        }
                        case 2:{
                            lbC.setText("C:"+strs1[1]);
                            String str2 = br.readLine();
                            String[] strs2 = str2.split("\\s+");
                            lbA.setText("A:"+strs2[1]);
                            String str3 = br.readLine();
                            String[] strs3 = str3.split("\\s+");
                            lbB.setText("B:"+strs3[1]);
                            String str4 = br.readLine();
                            String[] strs4 = str4.split("\\s+");
                            lbD.setText("D:"+strs4[1]);
                            Opt = "C";

                            System.out.println(lbA.getText());
                            System.out.println(lbB.getText());
                            System.out.println(lbC.getText());
                            System.out.println(lbD.getText());
                            break;
                        }
                        case 3:{
                            lbD.setText("D:"+strs1[1]);
                            String str2 = br.readLine();
                            String[] strs2 = str2.split("\\s+");
                            lbA.setText("A:"+strs2[1]);
                            String str3 = br.readLine();
                            String[] strs3 = str3.split("\\s+");
                            lbB.setText("B:"+strs3[1]);
                            String str4 = br.readLine();
                            String[] strs4 = str4.split("\\s+");
                            lbC.setText("C:"+strs4[1]);
                            Opt = "D";

                            System.out.println(lbA.getText());
                            System.out.println(lbB.getText());
                            System.out.println(lbC.getText());
                            System.out.println(lbD.getText());

                            break;
                        }
                        default:{
                            System.out.println("Switch语句有误");
                            break;
                        }

                    }
                    System.out.println("正确选项是"+Opt);

                    break;
                }
                //System.out.println("readLineFile结束:"+in);
            }

        }catch (Exception e){
            e.printStackTrace();

        }
    }

    public void init() {
        //System.out.println("init开始执行:"+ij);

        //ij++;
        lbLife.setText("当前生命值:" + life);
        //System.out.println("设置生命值");

        //String str = String.valueOf((char)('A'+rnd.nextInt(26)));
        //lbMoveChar.setText(str);
        //不需要客户端产生随机数了,服务器产生随机数
        //int il = rnd.nextInt(10);
        //ps.println("RND#"+il);


        readLineFile("D:\\word.txt", il);

        lbA.setBounds(0,400,300,50);
        lbB.setBounds(300,400,300,50);
        lbC.setBounds(600,400,300,50);
        lbD.setBounds(900,400,300,50);

        lbMoveChar.setText(word);

        lbMoveChar.setBounds(500,0,200,50);

        //System.out.println("init执行完成:"+ij);
    }

    //读取服务器发来的消息,操作生命值
    public void run(){
        try{
            while(canRun){
                String str = br.readLine();
                String[] strs = str.split("#");
                //判断从服务器接收到的消息是初始化number(用作随机读词)
                if(strs[0].equals("START")){
                    il = Integer.parseInt(strs[1]);
                //判断是减生命值的消息
                }else if(strs[0].equals("LIFE")) {
                    //若消息是既包含LIFE又包含RND
                    int score = Integer.parseInt(strs[1]);

                    //实现生命值的减少

                    life+=score;
                    checkFail();
                    //System.out.println("life+=score;语句调用checkFail");
                    //如果strs[]的格式为“LIFE#-1#START#srn"
                    if(strs[2].equals("START")){
                        il = Integer.parseInt(strs[3]);

                    }
                }else if(strs[0].equals("UWIN")){
                    //你赢了并退出游戏
                    timer.stop();
                    javax.swing.JOptionPane.showMessageDialog(this,"游戏结束,你赢了!");
                    System.exit(0);

                }
                init();
                //checkFail();
                //System.out.println("执行完服务器来的任务后调用init");
            }
        }catch (Exception ex){
        canRun = false;
        javax.swing.JOptionPane.showMessageDialog(this,"游戏异常退出!");
        System.exit(0);
        }
    }

    //某用户如果生命值率先变为0分,则判输,游戏退出。
    public void checkFail(){
        //init();
        //System.out.println("开始执行checkFail:"+ic);
        lbLife.setText("当前生命值:" + life);

        //ic++;

        //System.out.println("结束ccheckFail:"+ic);
        if(life <= 0){
            ps.println("WIN#");
            timer.stop();
            javax.swing.JOptionPane.showMessageDialog(this,"生命值耗尽,游戏失败!");
            System.exit(0);
        }
    }
   //若词汇掉到用户界面底部,如果两个用户都还没做出选择,两个用户减1分
    public void actionPerformed(ActionEvent e){
        if(lbMoveChar.getY() >= this.getHeight()){
            writeFile("D:\\wrong.txt",strSave);
            life--;
            checkFail();
            //随机数由服务器产生
            ps.println("ASKRN#");
            //System.out.println("底部调用checkFail");
        }
        lbMoveChar.setLocation(lbMoveChar.getX(),lbMoveChar.getY()+10);
    }

    @Override
    public void keyTyped(KeyEvent e) {
    }

    @Override
    public void keyPressed(KeyEvent e) {
         keyChar = e.getKeyChar();
         keyStr = String.valueOf(keyChar);
        try{
            //判断是否选对选项
            if(keyStr.equalsIgnoreCase(Opt)) {

                writeFile("D:\\right.txt",strSave);

                life+=2;
                //System.out.println("选正确了");
                ps.println("LIFE#-1");
            }
            else{

                writeFile("D:\\wrong.txt",strSave);

                life-=2;
                //用于向服务器标识需要随机数,对方生命值加一
                ps.println("LIFE#1");
            }

            //实现选项射向lbMoveChar
            //ProLocation();
            //(Timer.scheduleAtFixedRate(TimerTask task,long delay,long period)
            //安排指定的任务在指定的延迟后开始进行重复的固定速率执行.
            /*timer.stop();
            Timer timer2 = new Timer();
            timer2.scheduleAtFixedRate()*/


            init();
            checkFail();

            //System.out.println("向服务器发消息后checkFail");

        }catch (Exception ex){
            canRun = false;
            javax.swing.JOptionPane.showMessageDialog(this,"游戏异常退出!");
            System.exit(0);
        }
    }

    @Override
    public void keyReleased(KeyEvent e) {
    }

    public static void main(String[] args){
         new GameFrame();
    }

}

package client;

import javax.swing.*;
import java.awt.*;

public class GameFrame extends JFrame{
    private GamePanel gp;
    public GameFrame(){
        this.setLayout(new BorderLayout());

        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //setAlwaysOnTop(true);//设置窗体的属性,总是在最上面显示
        this.setAlwaysOnTop(true);
        String nickName = JOptionPane.showInputDialog("输入昵称");
        this.setTitle(nickName);
        gp = new GamePanel();
        this.add(gp);

        gp.setFocusable(true);
        this.setSize(gp.getWidth(),gp.getHeight());
        this.setResizable(false);
        this.setVisible(true);

    }
}

(2)、服务器端代码:
这里写图片描述

import javax.swing.*;
import java.awt.*;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.*;
import java.util.ArrayList;
import java.util.Random;

public class Server extends JFrame implements Runnable{
    private Socket s = null;
    private ServerSocket ss = null;
    //private JTextField jtf = new JTextField();
    private JTextArea jta = new JTextArea();

    private Random rnd = new Random();

    private ArrayList<ChatThread>clients = new ArrayList<ChatThread>();
    public Server() throws Exception{
        this.setTitle("服务器端");
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.add(jta,BorderLayout.CENTER);
        jta.setBackground(Color.yellow);
        this.setSize(400,200);
        this.setVisible(true);
        ss = new ServerSocket(9999);
        new Thread(this).start();
    }
    public void run(){
        try{
            while(true){
                s = ss.accept();
                ChatThread ct = new ChatThread(s);
                clients.add(ct);
                ct.start();
            }
        }catch (Exception ex){
            ex.printStackTrace();
            javax.swing.JOptionPane.showMessageDialog(this,"游戏异常退出!");
            System.exit(0);

        }
    }

    class ChatThread extends Thread{
        private Socket s = null;
        private BufferedReader br = null;
        private PrintStream ps = null;
        private boolean canRun = true;
        public ChatThread(Socket s)throws Exception{
            this.s = s;
            br = new BufferedReader(new InputStreamReader(s.getInputStream()));
            ps = new PrintStream(s.getOutputStream());
        }
        public void run(){
            try{
                //开始时,服务器首先生成一个大小有限制的随机数,并发给所有的客户端
                int wn = rnd.nextInt(640);
                System.out.println("Start:"+wn);
                String swn = "START#" + Integer.toString(wn);
                sendMessage(swn);
                while (canRun){
                    //在这里
                    String str = br.readLine();

                    String[] strs = str.split("#");
                    if(strs[0].equals("LIFE")){
                        //将生命值转发给所有的客户端
                        //sendMessage(strs[1]);
                        int rn = rnd.nextInt(640);

                        System.out.println("RdNumber:"+rn);
                        System.out.println("减或加生命值");

                        String srn = "START#" + Integer.toString(rn);
                        sendMessage("LIFE#"+strs[1]+"#"+srn);
                    }else if(strs[0].equals("WIN")){
                        //有一方生命值已经归为0,另一方胜利
                        String msgWIN = "UWIN#";
                        sendMessage(msgWIN);
                    }else if(strs[0].equals("ASKRN")){
                        int rn1 = rnd.nextInt(640);
                        String swn1 = "START#" + Integer.toString(rn1);
                        System.out.println("仅用于同步");
                        sendMessage(swn1);
                    }
                    //将str转发给所有的客户端
                    //sendMessage(str);

                }
        }catch (Exception ex){
                canRun = false;
                clients.remove(this);
            }
        }
    }

    //将信息转发给所有的客户端
    public void sendMessage(String msg){
        for(ChatThread ct:clients){
            ct.ps.println(msg);
        }
    }

    public static void main(String[] args)throws Exception{
        new Server();
    }

}

参考资料
《Java语言程序设计》(清华大学出版社)主编:郭克华

  • 2
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值