Java - 多线程服务器,用于服务多个并发客户端

时间:2017-02-26 20:31:10

标签: java multithreading

我一直在努力使下面的代码让多个客户端与同一台服务器进行通信。 目前,它一次与服务器一起工作一个客户端,但似乎是当第二个客户端打开时,代码停在新的ObjectInputStream(connection.getInputStream());在第3类(客户) - 见下文。

我试过让输入流对象瞬态在不同的线程中共享,但它不起作用,也没有使runClient方法同步。

如果我使用serialVersionUID在客户端类中实现Serializable,我如何使用相同的服务器进行多线程处理,或者有更好的方法..?

第1类 - 服务器主

public class EchoServer {

private ServerSocket server;
private int portNum;
public static final int DEFAULT_PORT = 8081;

public EchoServer(int portNum) {
    this.portNum = portNum;
} 

public void runServer() {
    System.out.println("Echo Server started...");

    try {
        server = new ServerSocket(portNum);
        Socket connection = server.accept();
        new Thread(new ClientHandler(connection)).run();
    } catch(IOException ex) {
        System.err.println("Error encountered! Port is likely already in use! Exiting program...");
        ex.printStackTrace();
    }
}

public static void main(String[] args) {
    if (args.length > 0) {
        (new EchoServer(Integer.parseInt(args[0]))).runServer();

    } else {
        (new EchoServer(DEFAULT_PORT)).runServer(); 
    }
  } 
}

第2类

public class ClientHandler implements Runnable {

private ObjectOutputStream output;
private ObjectInputStream input;
private String message;

/** Integer to hold the message number. */
private int messagenum;
private Socket connection;

public ClientHandler(Socket connection) {
    this.connection = connection;
}

@Override
public void run() {
    do{
        handleRequest();
    } while (true);
}

public void handleRequest() {
    try {
        output = new ObjectOutputStream(this.connection.getOutputStream());
        input = new ObjectInputStream(this.connection.getInputStream());
        do { 
            try {
                message = (String) input.readObject();
                System.out.println(messagenum +" Output> " +message);
            } catch (EOFException | SocketException e) {
                message = null;
            }

            if (message != null) {
                output.writeObject(messagenum +" FromServer> " +message);
                output.flush();
                ++messagenum;
            }
        } while (message != null);
        input.close();
        output.close();
        this.connection.close();

    }  catch (IOException | ClassNotFoundException ex) {
        System.err.println("Error encountered! Exiting program...");
        ex.printStackTrace();           
    }
  }
}

第3类 - 客户主要

public class EchoClient implements Serializable {
   private static final long serialVersionUID = 1L;
   private Socket connection;
   private ObjectOutputStream output;
   private transient ObjectInputStream input;
   private String message = "";
   private static String serverName;
   public static final String DEFAULT_SERVER_NAME = "localhost";
   private static int portNum;
   BufferedReader keyboard = new BufferedReader(new InputStreamReader(System.in));

public EchoClient(String serverName, int portNum) {
    this.serverName = serverName;
    this.portNum = portNum;
}


public synchronized void runClient() {
    try {           
        connection = new Socket(InetAddress.getByName(serverName), portNum);
        output = new ObjectOutputStream(connection.getOutputStream());
        input = new ObjectInputStream(connection.getInputStream());

        do {
            System.out.print("Input> ");
            message = keyboard.readLine();

            if (message != null){
                output.writeObject(message);    
                output.flush();
                message = (String) input.readObject();
                System.out.println(message);
            }
        } while (message != null);
        input.close();
        output.close();
        connection.close();
    } catch (IOException ioException) {
        ioException.printStackTrace();
    } catch (ClassNotFoundException exception) {
        exception.printStackTrace();
    }
} 


public static void main(String[] args) {
    switch (args.length) {
    case 2:
        (new EchoClient(args[0], Integer.parseInt(args[1]))).runClient();
        break;
    case 1:
        (new EchoClient(DEFAULT_SERVER_NAME, Integer.parseInt(args[0]))).runClient();
        break;
    default:
        (new EchoClient(DEFAULT_SERVER_NAME, server.EchoServer.DEFAULT_PORT)).runClient();
    }
  } 
} 

3 个答案:

答案 0 :(得分:1)

运行服务器需要在循环中等待连接,否则它将连接一次,就是这样。它也需要关闭它的连接。清理它的线程。这只是在服务器主要。我很确定这是重复的。所以继续研究

答案 1 :(得分:1)

在循环中调用server.accept()以接受其他答案中提到的多个客户端连接。使用Thread.start方法而不是Thread.run - What's the difference between Thread start() and Runnable run()开始新主题。

volatile boolean isRunning = true;
public void runServer() {
    System.out.println("Echo Server started...");

    try {
        server = new ServerSocket(portNum);
        while(isRunning) {
           Socket connection = server.accept();
           new Thread(new ClientHandler(connection)).start();
        }
    } catch(IOException ex) {
        System.err.println("Error encountered! Port is likely already in use! Exiting program...");
        ex.printStackTrace();
    }
}

答案 2 :(得分:0)

正如efekctive所说,你需要循环中的server.accept(),否则它会接受第一个客户端并退出程序。所以将这两行放在runServer()中,如下所示:

boolean isRunning = true;
while(isRunning){
    Socket connection = server.accept();
    new Thread(new ClientHandler(connection)).run();
}