简体   繁体   English

如何处理从大厅服务器到客户端的多个流

[英]How to handle multiple streams from a lobby server to clients

So, I'm having an issue with a project of mine. 所以,我的一个项目遇到了问题。 I'm writing a multiplayer lobby system which will enable multiple users to join a lobby, readying themselves by pressing a key. 我正在编写一个多人游戏大厅系统,该系统将允许多个用户加入一个大厅,并通过按下一个键进行准备。 The issue that I'm facing is when two players is readying themselves, the lobby is only printing out a message for the last player who readied themselves. 我面临的问题是,当两个玩家准备就绪时,大厅仅向最后准备就绪的玩家打印一条消息。 The system is built up in the following way. 该系统以以下方式构建。

Main Server 主服务器

package master;

import java.net.*;
import java.io.*;
import java.util.*;
import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;

import main.Lobby;

public class MainServer {

public static final int PORT = 4444;
public static final String HOST = "localhost";
public ArrayList<Lobby> serverList = new ArrayList<>();

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

    new MainServer().runServer();
}

public void runServer() throws IOException, ClassNotFoundException {

    // Creating the server

    ServerSocket serverSocket = new ServerSocket(PORT);
    System.out.println("Main Server initiated.");

    while (true) {

        Socket socket = serverSocket.accept();

        try {

            // Establishing the connection to the Lobby server and then adding it to its list
            ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
            objectOutputStream.writeObject("Server created successfully.");
            Lobby s = (Lobby) objectInputStream.readObject();
            this.serverList.add(s);
            System.out.println("Server \"" + s.name + "\" added to game list.");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

} }

The lobby 大堂

package main;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.concurrent.Semaphore;

import master.MainServer;

/**
 * The Class Server.
 */
public class Lobby implements Serializable {
    private static final long serialVersionUID = -21654L;
    public static final int PORT = 4445;
    public static final int MAX_USERS = 5000;
    public static final String HOST = "localhost";
    public String name = "Lobby Server";
    public int clientNumber;
    public int playerNumberReady = 0;
    public boolean allPlayersReady = false;
    public boolean OddurIsNice = false;

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Lobby s = new Lobby();
        s.runServer();
    }
    public void runServer() throws IOException, ClassNotFoundException {
        registerServer();
        new Thread( () -> {
            try {
                ServerSocket serverSocket = new ServerSocket(PORT);
                System.out.println("Server waiting for connections...");
                while (true) {
                    Socket socket = serverSocket.accept();
                    System.out.println("User 1 is now connected");
                    clientNumber++;             
 new ObjectOutputStream(socket.getOutputStream()).writeObject("You are connected man");
                        Socket socket2 = serverSocket.accept();
                        System.out.println("User 2 is now connected");
                        clientNumber++;
//                      ObjectOutputStream objectOutputStream2 = new ObjectOutputStream(socket2.getOutputStream());
//                      objectOutputStream2.writeObject("You are player number " + clientNumber + ". Waiting for other players to join");
                        new ServerThread(socket, socket2).start();

                    }
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }).start();
    }
    private void registerServer() throws UnknownHostException, IOException, ClassNotFoundException {
        // Method for establishing a connection to the MainServer 
        Socket socket = new Socket(MainServer.HOST, MainServer.PORT);

        ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
        ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
        objectOutputStream.writeObject(this);

        System.out.println((String) objectInputStream.readObject());
    }
    public class ServerThread extends Thread {
        public Socket socket = null;
        public Socket socket2 = null;
        ServerThread(Socket socket, Socket socket2) {
            this.socket = socket;
            this.socket2 = socket2;
        }
        public void run() {
            try {       



// This method is for when the client want's to connect to the lobby
                    ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
                    ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
                    System.out.println("User 1 is now connected");

                    ObjectInputStream objectInputStream2 = new ObjectInputStream(socket2.getInputStream());
                    ObjectOutputStream objectOutputStream2 = new ObjectOutputStream(socket2.getOutputStream());
                    System.out.println("User 2 is now connected");
                    BoardGameClient joined = (BoardGameClient) objectInputStream.readObject();
                    System.out.println(joined.name + " is now connected.");
                    while(true) {
                    objectOutputStream.writeObject("You joined the server.");
                    objectOutputStream.writeObject("You are player Number " + 1);

                    objectOutputStream.writeObject("Press '1' if you are ready");

                    objectOutputStream2.writeObject("You joined the server.");
                    objectOutputStream2.writeObject("You are player Number " + 2);

                    objectOutputStream2.writeObject("Press '1' if you are ready");

                if(objectInputStream.readObject().equals(1)) {
                    playerNumberReady++;
                }

                if(objectInputStream2.readObject().equals(1)) {
                    playerNumberReady++;
                }

                    if(playerNumberReady != 2) {
                        allPlayersReady = false;
                    } else {
                        allPlayersReady = true;
                    }



                    if (allPlayersReady == false) {
                        objectOutputStream.writeObject("Waiting...");
                        objectOutputStream2.writeObject("Waiting...");
                } 

                    if (allPlayersReady == true) {
                    objectOutputStream.writeObject("Lets GO");
                    objectOutputStream2.writeObject("Lets GO");
                }                           


                    while (true) {
                    System.out.println(objectInputStream.readObject());
                    }
                    }
                    } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

And the client 和客户

    package main;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
import java.util.concurrent.Semaphore;

import master.MainServer;

public class BoardGameClient implements Serializable {

    private int playerName;
    private static final long serialVersionUID = -6224L;
    public String name = "User";
    private transient Socket socket;
    public transient Scanner input = new Scanner(System.in);    

    public static void main(String[] args) {

        BoardGameClient c = new BoardGameClient();

        if (args.length > 0) {

            c.name = args[0];
        }

        try {

            c.joinServer();

        } catch (ClassNotFoundException | IOException e) {

            System.out.println("Failed to join server.");
            e.printStackTrace();
        }
    }

    public void joinServer() throws UnknownHostException, IOException, ClassNotFoundException {

        socket = new Socket(Lobby.HOST, Lobby.PORT);
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
        ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());

        while(true) {
        objectOutputStream.writeObject(this);

        BufferedReader inputReader = new BufferedReader(new InputStreamReader(System.in));

        System.out.println(objectInputStream.readObject());
        System.out.println(objectInputStream.readObject());
        System.out.println(objectInputStream.readObject());

        int ready = input.nextInt();
        objectOutputStream.writeObject(ready);

        System.out.println(objectInputStream.readObject());



            objectOutputStream.writeObject(name + ": " + inputReader.readLine());
        }
    }
}

I sincerely hope, that someone will be able to help me out <3 我衷心希望,有人能够帮助我<3

Firstly, there's a few things that bug me about this code. 首先,关于此代码,有些事情使我感到困惑。 Not to sound condescending but you need to avoid rewriting code as much as possible. 听起来并不屈尊,但是您需要尽可能避免重写代码。 What happens if you want 3 or more players in the future? 如果您将来需要3个或更多玩家,会发生什么? Currently you'd have to manually create a whole socket eg socket3, and then rewrite all the code you've already written. 当前,您必须手动创建一个完整的套接字,例如socket3,然后重写所有已经编写的代码。 This is bad. 这不好。 You've manually spent the time creating 2 sockets and then created 2 streams for both of these sockets etc etc. 您手动花费了时间创建2个套接字,然后为这两个套接字等创建了2个流。

This can be automated don't you think? 这可以自动化吗?

Secondly, you have a lot of public variables. 其次,您有很多公共变量。 Unless they are static and final, for the most part you should keep variables as private. 除非它们是静态的和最终的,否则在大多数情况下,应将变量保留为私有。

I've tinkered with your lobby class as seen below, which is more scalable. 我已经对您的大厅课进行了修改,如下所示,这更具可扩展性。 It's not perfect by any means, but I feel illustrates the direction of improvement you should be heading for. 无论如何,它都不是完美的,但我认为这说明了您应该追求的改进方向。 Look up SOLID OOP principles, they'll help you guaranteed. 查找SOLID OOP原则,它们将帮助您保证。

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.LinkedHashSet;
import java.util.Set;

/**
 * The Class Server.
 */
public class Lobby implements Serializable {
    private static final long serialVersionUID = -21654L;
    public static final int PORT = 4445;
    public static final int MAX_USERS = 5000;
    public static final String HOST = "localhost";
    private static final int MIN_USERS = 2;

    private String name = "Lobby Server";
    private int clientNumber;
    private boolean gameRunning = false;

    // set of client connections
    private final Set<ServerThread> clientConnectionThreads = new LinkedHashSet<>();

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Lobby s = new Lobby();
        s.createLobby();
    }

    public void createLobby() throws IOException, ClassNotFoundException {
        // waits for all players to ready up in a different thread
        new Thread(this::waitReady).start();

        registerServer();

        // Listens for clients
        runServer();
    }

    public void runServer() {
        // closes serverSocket automatically in this way
        try (ServerSocket serverSocket = new ServerSocket(PORT)) {
            System.out.println("Server waiting for connections...");
            long ids = 0;
            while (!gameRunning) {
                // accepts a new client connection
                Socket socket = serverSocket.accept();

                if (clientConnectionThreads.size() >= MAX_USERS) {
                    // tell user server is full and dont add the connection
                } else {
                    // calculates the new id of the incoming player and adds them to the lobby
                    ids++;
                    this.clientConnectionThreads.add(new ServerThread(ids, socket));
                    System.out.println("User " + ids + " is now connected");
                }
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    /*
     * loops until every player is ready and there is enough players and then starts
     * the game.
     */
    public void waitReady() {
        while (true) {
            try {
                if (areAllReady() && this.clientConnectionThreads.size() >= MIN_USERS) {
                    startGame();
                    return;
                }
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    // returns true if all users are ready
    public boolean areAllReady() {
        return clientConnectionThreads.stream().allMatch(ServerThread::isReady);
    }

    public void startGame() {
        System.out.println("Starting game...");
        this.gameRunning = true;
        clientConnectionThreads.forEach(ServerThread::startGame);

        // do game stuff
    }

    // i havent touched this function
    private void registerServer() throws UnknownHostException, IOException, ClassNotFoundException {
        // Method for establishing a connection to the MainServer
        Socket socket = new Socket(MainServer.HOST, MainServer.PORT);

        ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
        ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
        objectOutputStream.writeObject(this);

        System.out.println((String) objectInputStream.readObject());
    }

    public class ServerThread extends Thread {
        private final Socket socket;
        private final ObjectInputStream in;
        private final ObjectOutputStream out;
        private final long id;
        boolean ready = false;

        private ServerThread(long id, Socket socket) throws IOException {
            // does some basic initialization
            this.socket = socket;
            this.id = id;
            in = new ObjectInputStream(socket.getInputStream());
            out = new ObjectOutputStream(socket.getOutputStream());

            // starts this connection thread
            this.start();
        }

        public boolean isReady() {
            return ready;
        }

        public void run() {
            try {
                // sets up the client and waits for their input
                BoardGameClient joined = (BoardGameClient) in.readObject();
                System.out.println(joined.name + " is now connected.");
                out.writeObject("You joined the server.");
                out.writeObject("You are player Number " + id);
                out.writeObject("Press '1' if you are ready");
                out.flush();

                // waits for user to return ready
                while (!ready) {
                    try {
                        int input = in.readInt();
                        System.out.println("input: " + input);
                        ready = input == 1;
                    } catch (ClassCastException e) {
                        e.printStackTrace();
                    }
                }

                out.writeObject("Waiting for players...");

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

        public void startGame() {
            // send client message etc etc
        }
    }

    public String getName() {
        return name;
    }
}

I basically didn't change any of the other classes, except a few lines within the client class to make this work. 我基本上没有更改任何其他类,除了在客户端类中执行这一行的几行。 (I've changed the ready input type from writeObject() to writeInt()) I haven't tested this for problems, but I know it works at least on a basic level. (我已经将就绪输入类型从writeObject()更改为writeInt())我还没有对此问题进行过测试,但我知道它至少可以在基本级别上起作用。

I also suggest using writeUTS()/readUTS() instead of writeObject()/readObject() for sending and receiving Strings across streams as this will add extra complexity to the code. 我还建议使用writeUTS()/ readUTS()而不是writeObject()/ readObject()跨流发送和接收String,因为这会增加代码的复杂性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM