Write a simple online tic-tac-toe game using java Socket

Write a simple online tic-tac-toe game using java Socket

Introduction to Socket

The Socket socket is an illusion layer between the application layer and the transport layer (it can be understood), it wraps the operations of the other four layers downwards, and is transparent to the programmer, that is to say, in fact C/S communicates on the socket layer.

1.Socket establishes a connection

Socket in java divides objects into two types: ServerSocket and Socket correspond to server and client respectively. If you are interested, you can read the following article link: C++ knowledge sharing: Socket programming detailed explanation, ten thousand words
long article
next Let's establish a simple connection:
client code:

//8080表明我们将服务器放在了8080端口
ServerSocket serverSocket=new ServerSocket(8080);
//这里阻塞的接收一个连接
Socket socket=serverSocket.accept();

Server code:

//向服务端发送连接请求返回一个socket对象
Socket socket = new Socket("localhost", 8080);

The code to establish a connection is very simple, but there is nothing to simply establish a connection. We establish a connection for interaction, so we need a little logic code: the simplest input and output

//输入流从另一端的输入读入
 BufferedReader bufferedReader =  new BufferedReader(new InputStreamReader(socket.getInputStream()));
 //输出流输出到另一端
 PrintWriter printWriter = new PrintWriter(socket.getOutputStream(), true);
 
 //读入使用bufferedReader.readline();
 //输出则使用printWriter.println();
 //这其中还有很多细节,譬如bufferedReader.readline();是阻塞函数等等,这是需要根据实现场景改变的不进行讨论

At this point, we can simply use Socket. If you try to communicate, you will find that you can only enter once, and then the connection will be disconnected. This is because if the socket connection is not active, the connection will be disconnected. So we need to keep this connection active all the time, we don't need "heartbeat packets", we need to add a while loop.

2. Main logic


//Server中的主要逻辑
 while (true) {
    
    
 				//为线程添加锁
                synchronized (lock) {
    
    
                    if(win.getWin()) {
    
    
                        printWriter.println("win");
                        printWriter.println("游戏结束");
                        printWriter.println(toString.ArrayToString(map));

                        //如果在此直接break,那么输出流就会关闭,连接断开,此时客户端就收不到信息
                        //所以在此接收一个bufferedReader
                        message=bufferedReader.readLine();
                        System.out.println(message);
                        break;
                    }
                    //如果已经赢了那么就退出


                    printWriter.println(toString.ArrayToString(map));




                    message = bufferedReader.readLine();
//                System.out.println(message);
                    //只有输入合法位置时才能往下进行
                    while (!go(message, model)) {
    
    
                        printWriter.println("error");
                        message = bufferedReader.readLine();
                    }
                    //下棋之后进行判断
                    if (judge())
                        win.setWin(true);
                    printWriter.println(toString.ArrayToString(map));

                }


                    //在释放锁后让线程睡眠100毫秒,确保下一次拿到锁的是另一个线程
                Thread.sleep(100);


            }
       bufferedReader.close();
        printWriter.close();
        socket.close();
        } catch (IOException e) {
    
    
            e.printStackTrace();
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
    }
//服务端主要逻辑
while ((message=bufferedReader.readLine())!=null) {
    
    


            // 从服务器接收消息
            //如果收到的是错误,说明下的位置不对
            //在此处继续输入
            if(message.equals("error")) {
    
    
                System.out.println("该位置已有棋子,请换位置");
                String send = input.readLine();
                printWriter.println(send);
            }

            //收到胜利信息后
            else if(message.equals("win")) {
    
    
                message=bufferedReader.readLine();
                System.out.println(message);
                message=bufferedReader.readLine();
                client.map = toString.StringToArray(message);
                client.picture();


                printWriter.println("确认收到");

                break;
            }
            //渲染画面
            else {
    
    
                System.out.flush();
                client.map = toString.StringToArray(message);
                client.picture();

                String send = input.readLine();
                printWriter.println(send);

                message= bufferedReader.readLine();
                client.map = toString.StringToArray(message);
                client.picture();
            }



        }

The next step is to run the App

public static void main(String[] args) throws IOException {
    
    

        Object lock=new Object();
        ServerSocket serverSocket=new ServerSocket(8080);
        Socket socket=serverSocket.accept();
//        System.out.println("客户端已连接,地址:" + socket.getRemoteSocketAddress());
//        Thread thread1=new Thread(new ServerThread(socket,lock),"1");
//        Socket socket1=serverSocket.accept();
//        System.out.println("客户端已连接,地址:" + socket1.getRemoteSocketAddress());
//        Thread thread2=new Thread(new ServerThread(socket1,lock),"2");

        //错误:这里的ServerThread不是同一个对象,所以里面的map是不共享的


        //共享map
        //将基本类型包装成对象进行共享
        Win win=new Win();
        ArrayList<Integer> map=new ArrayList<>();
        map=new ArrayList<>();
        for (int i=0;i<10;i++)
        {
    
    
            map.add(0);
        }
        System.out.println("客户端已连接,地址:" + socket.getRemoteSocketAddress());
        Thread thread1=new Thread(new ServerThread(socket,map,win,lock),"1");
        Socket socket1=serverSocket.accept();
        System.out.println("客户端已连接,地址:" + socket1.getRemoteSocketAddress());
        Thread thread2=new Thread(new ServerThread(socket1,map,win,lock),"2");


        thread1.start();
        thread2.start();

//

    }

Notes are the problems and solutions encountered in the writing process

2.1 Operation process

insert image description here

When GameServer starts, it will only accept connection requests from two clients. After receiving two requests, it will open a different thread ServerThread for the two requests, and pass in the same obj as the thread lock and the same map object. To ensure that the order of operation is as we expect,
then it is ordinary data transmission and data processing.
insert image description here
insert image description here
insert image description here
Every chess move, the client will send information to the server, and the server will parse it and update the value in the map, because the map is a shared object. So the updated results will be synchronized to the two threads

insert image description here

2e6d8b942a784f3de9f4875fed5.png)
Every time the data is updated, information will be sent to the client, and then the client will re-render and output until one side wins
insert image description here

3. Unresolved issues

(1) At the beginning, I wanted to use the direct transfer of map objects to realize the interaction of map values ​​in C/S, but later because of serialization (probably) it was not realized

(2) In the logic code of the client, there is System.out.flush() to refresh the console, but it is useless for some reason

(3) There is no solution. When a thread is blocked, if its corresponding client is still inputting, its input will still be read when the thread gets the lock, which may cause reading errors or read illegal data. , may lead to system confusion so the system is very unrobust

Guess you like

Origin blog.csdn.net/BeiWoFW/article/details/130176248
Recommended