简体   繁体   中英

Java - multithreaded server only works for one connection

I'm writing a multiplayer game and I'm writing my own server for it in Java using sockets.

I successfully was able to get a server up and running using the Java tutorials and some googling but when I tried to implement a multithreaded server to handle multiple clients, it's not working quite right.

The first client that connects will still work properly, but any other clients that connect will just sit there and do nothing after sending their input to the server. Here is my code:

//This class handled Server/client communication for one client.
public class GameRoom implements Runnable {
  public GameRoom(Socket socket) {
    this.socket = socket;
  }

  public void run() {
    logger.info("Game room has been created.");

    while(true) {
      try {
        in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        out = new PrintWriter(socket.getOutputStream());
        String clientResponse = in.readLine();
        out.println("You wrote " + clientResponse);
        out.flush();
      } catch (IOException e) {
        logger.severe(e.getMessage());
        throw new RuntimeException();
      }
    }
  }

  //Will eventually change the string to a GSON object and delegate the appropriate actions based on the gson object type.
  private String delegateClientInput(String clientInput) {
    return "I heard you say: " + clientInput + "\n";
  }

  private BufferedReader in;
  private PrintWriter out;
  private Socket socket;
  private static final Logger logger = LogUtil.initializeServerLog(GameRoom.class.toString());
}

/*
 * This class houses the server socket itself.  Handles connecting to multiple clients.
 */
public class ServerClientThreadPool extends Thread {
  public static void main(String[] args) {
    ServerClientThreadPool serverClientThreadPool = new ServerClientThreadPool();
    serverClientThreadPool.startServer();
  }

  ServerClientThreadPool() {
    try {
      serverListener = new ServerSocket(GAME_ROOM_PORT);
    } catch (IOException e) {
      logger.severe(e.getMessage());
      throw new RuntimeException();
    }
  }

  public void startServer() {
    for(int i = 0; i < MAX_CONNECTIONS; i++) {
      try {
        GameRoom gameRoom = new GameRoom(serverListener.accept());
        gameRoom.run();
      } catch (IOException e) {
        logger.severe(e.getMessage());
        throw new RuntimeException();
      }
    }
  }

  private static final int GAME_ROOM_PORT = 18000;
  private ServerSocket serverListener;
  private static final int MAX_CONNECTIONS = 100;
  private static final Logger logger = LogUtil.initializeServerLog(ServerClientThreadPool.class.getName());
}

And here's the client's main function, housed in a separate program of course:

ClientSocketWrapper clientSocketWrapper = ClientSocketWrapper.createSocketWrapperAndConnectTo("localhost", 18000);

/** MAIN ENTRY POINT FOR CLIENT */
while(true) {
  clientSocketWrapper.updateInputOutputStream();
  clientSocketWrapper.writeToOutputStream(executeClientInputProcessing());
  updateGameState(clientSocketWrapper.getServerResponse());
}

I realize you can't really see what's going on inside those methods, but it's basically just implementing a client side like the Java tutorials do and it works as expected. If you think I need to post the methods running here let me know, but I think the problem is on the server end.

BONUS QUESTION: I'm also wondering if I'm on the right track here. The game is fairly complex, but I was just going to use Gson to serialize and deserialize objects and delegate the appropriate actions based on the gson strings that get sent back and forth. After googling server/client architecture, it seems my approach is too simplistic. I'm having a hard time finding good resources to learn more advanced server/client architecture, so any links to great books or tutorials would be a big help.

Thanks all!

You don't want to call gameRoom.run() directly. That causes it to execute in the current thread, which ties up your server loop until that run() method completes. Instead, you should run it in a separate Thread. An ExecutorService obtained from the Executors factory class would be a simple way of handling that. For instance, create an ExecutorService in your ServerClientThreadPool:

public class ServerClientThreadPool {
    private ExecutorService executorService = Executors.newCachedThreadPool();

and then submit the gameRoom to the service after accepting the socket connection:

GameRoom gameRoom = new GameRoom(serverListener.accept());
executorService.submit(gameRoom);

Notice that I didn't write extends Thread on the ServerClientThreadPool above. You're not using it as a Thread in any way in the above code, and it probably wouldn't be right to do so unless you need the server to be accepting connections at the same time as other things are happening (besides the game rooms, which will already be handled in separate threads). If you did need that, then it should also be a Runnable , not a Thread , and also be submitted to an ExecutorService .

Based on the apparent confusion in your code about how threading works, I'd suggest you revisit the Java "Concurrency" tutorial , which includes a section on Executors .

Bonus: I think passing JSON back and forth is a reasonable way of commnunicating. Doing that implies that you're not overly concerned with performance, so I'd suggest you stop handling the sockets yourself and instead use an embedded HTTP server on the server side and a simple HTTP client on the client side. That'll handle all of the socket and threading nonsense for you, freeing you up to worry about the details of the game logic.

For example, embedding Jetty is a trivial undertaking, and there are a number of powerful and concise HTTP/REST clients out there like the Apache Http Client and the Jersey REST Client .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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