1

我正在尝试在服务器的同一端口上处理多个连接。我通过实例化一个 Object 并将其传递给另一个实现 Runnable 的类的构造函数来做到这一点。然后我在 Runnable 类中设置一个套接字,并在客户端连接到端口后对传递的对象调用 notify()。这应该允许服务器重新启动它的循环,在收到通知后创建 Runnable 类的另一个实例。但是,目前直到客户端关闭后才到达 wait()。以下是我拥有的 3 个相关课程:

服务器类:

   package server;

import java.io.IOException;
import java.net.ServerSocket;
import java.util.HashMap;

public class Server {

    public static void main(String[] args){
        HashMap<String, PortDummy> portDummies = new HashMap<String, PortDummy>();
        ServerSocket serverSocket = null;
        try {
            serverSocket = new ServerSocket(8000);
        } catch (IOException e1) {
            e1.printStackTrace();
        }

        for(;;){
            Object block = new Object();
            PortDummy dummy = new PortDummy(serverSocket, block, portDummies);
            System.out.println("Running dummy port...");
            dummy.start();
            try {
                synchronized(block){
                    System.out.println("Waiting...");
                    block.wait();
                    System.out.println("Block notified.");
                }
            } catch (InterruptedException e) {
                System.out.println("Can't be interrupted!");
                e.printStackTrace();
            }
        }
    }
}

PortDummy(可运行)类:

   package server;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;

public class PortDummy extends Thread {

    private Object block;
    private HashMap<String, PortDummy> portDummies;
    private String clientName = null;
    ServerSocket serverSocket;
    BufferedReader socketIn;
    PrintWriter socketOut;

    public PortDummy(ServerSocket serverSocket, Object block, HashMap<String, PortDummy> portDummies){
        this.block = block;
        this.portDummies = portDummies;
        this.serverSocket = serverSocket;
    }

    @Override
    public void run() {
        try {
            System.out.println("Starting dummy port...");
            Socket clientSocket = serverSocket.accept();
            System.out.println("Connection made.");
            synchronized(block){
                System.out.print("Notifying...");
                block.notify();
                System.out.println("...done.");
            }

            socketIn = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
            socketOut = new PrintWriter(clientSocket.getOutputStream(), true);

            String inContent;

            boolean loggedIn = false;
            while((inContent = socketIn.readLine()) != null){   
                socketOut.println("Server Echo: " + inContent);
                if(inContent.startsWith("/exit")){
                    if(loggedIn){
                        portDummies.remove(clientName);
                        System.out.println(clientName + " signed out. Removed from portDummies.");
                    }
                    else{
                        System.out.println("Closing...");
                    }
                }
                else if(inContent.startsWith("/register")){
                    System.out.println("/register accepted");
                    if(!loggedIn){
                        if(registerUser(inContent)){
                            System.out.println("Successfully registered.");
                            socketOut.println(clientName + " successfully registered.");
                            loggedIn = true;
                        }else{
                            socketOut.print("That user already exists.");
                        }
                    }
                    else{
                        socketOut.print("Already logged in.");
                    }
                }
                else if(inContent.startsWith("/tell")){
                    if(!loggedIn){
                        socketOut.println("You need to log in.");
                    }
                    else{
                        String[] parts = inContent.split("\\w");
                        String[] withoutCommand = new String[parts.length-1];
                        for(int i = 1; i<parts.length-1; i++){
                            withoutCommand[i] = parts[i];
                        }
                        String[] messageParts = new String[withoutCommand.length-1];
                        String message = "";
                        for(int j = 1; j<withoutCommand.length-1; j++){
                            message += withoutCommand[j] + " ";
                        }

                        String recipient = withoutCommand[0];
                        sendMessage(recipient, message);
                    }
                }
                else if(inContent.startsWith("/help")){
                    socketOut.print("/help ~~~~~~~ List all commands. \n " +
                            "/register <username> ~~~~~~~ Register a new user with 'username'. \n " +
                            "/tell <username> <message> ~~~~~~~ Send 'username' text 'message'. \n " +
                            "/exit ~~~~~~~ Log out.");
                }
            }

            System.out.println("Shutting down client connections...");
            socketOut.close();
            socketIn.close();
            clientSocket.close();
            serverSocket.close();

        } catch (IOException e) {
            System.out.println("IOException!");
            e.printStackTrace();
        }       
    }

    private boolean registerUser(String text){
        System.out.println("Registering user...");
        String user = text.substring(10);
        if((user != null) && !(portDummies.containsKey(user))){
            portDummies.put(user, this);
            clientName = user;
            System.out.println(user + " registered.");
            return true;
        }
            return false;
    }

    private void sendMessage(String username, String message){
        if(portDummies.containsKey(username)){
            PortDummy recip = portDummies.get(username);
            recip.getSocketOutput().println(clientName + ": " + message);
        }
        else{
            socketOut.write("User " + username + " doesn't exist.");
        }
    }

    public PrintWriter getSocketOutput(){
        return socketOut;
    }
}

客户端类:

package client;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
import java.io.IOException;

public class Client {

    protected String username;

    public static void main(String[] args){
        try{
            Socket serverSocket = new Socket("localhost", 8000);
            BufferedReader socketIn = new BufferedReader(new InputStreamReader(serverSocket.getInputStream()));
            PrintWriter socketOut = new PrintWriter(serverSocket.getOutputStream(), true);

            Scanner keyboardInputScanner = new Scanner(System.in);
            String keyboardInput, serverInput;
            System.out.println("Welcome to Chris Volkernick's Server IM Client! \n" +
                    "Type '/register <username>' to register, '/list' to list connected users," +
                    "\n or '/tell <username> <message>' to send a user a message. '/help' lists these commands. (Type '/exit' to sign out.)");
            while((keyboardInput = keyboardInputScanner.nextLine()) != null){
                System.out.println("Input '" + keyboardInput + "' read on client side.");
                if(keyboardInput.equals("/exit")){
                    socketOut.println("/exit");
                    socketOut.close();
                    socketIn.close();
                    serverSocket.close();
                }else{
                    socketOut.println(keyboardInput);

                    while((serverInput = socketIn.readLine()) != null){
                        System.out.println(serverInput);
                    }
                }           
            }       
            keyboardInputScanner.close();

        }catch(IOException e){
            System.out.println("IOException!");
            e.printStackTrace();
        }       
    }
}

我在等待()和/或通知()方面做错了吗?

编辑:我还尝试将实现 Runnable 更改为扩展 Thread,然后将服务器中的 .run() 更改为 .start(),但这给了我这个错误:

java.net.BindException: Address already in use: JVM_Bind
    at java.net.PlainSocketImpl.socketBind(Native Method)
    at java.net.PlainSocketImpl.bind(PlainSocketImpl.java:365)
    at java.net.ServerSocket.bind(ServerSocket.java:319)
    at java.net.ServerSocket.<init>(ServerSocket.java:185)
    at java.net.ServerSocket.<init>(ServerSocket.java:97)
    at server.PortDummy.run(PortDummy.java:28)

编辑2:在启动新线程方面,它似乎正在按照现在应该的方式工作。但是,我现在遇到了另一个问题:在任何给定客户端的客户端输入命令后,我无法输入其他命令。第一个命令可以正常工作(减去/exit;还没有完全弄清楚它应该如何工作),之后就不能做任何事情了。例如,我可以注册(登录),但之后就没有别的了。我可以进入另一个客户端实例并列出所有当前用户(作品),但同样,之后我无法输入其他命令。知道可能会发生什么导致这种情况吗?

4

3 回答 3

2

问题是您的子线程正在尝试侦听端口 8000,但父线程已经在这样做了。您需要做的是从原始套接字传递接受连接,然后将其提供给子线程。我不完全确定如何在 Java 中做到这一点,但我怀疑它只是..

把它放在你的主线程中:

ServerSocket serverSocket = new ServerSocket(8000);
Socket clientSocket = serverSocket.accept();

然后一旦你得到它,传递clientSocket给你的Thread.

这样只有一个套接字在端口 8000 上侦听,但您可以让子线程处理每个连接。

于 2011-10-15T05:03:41.360 回答
0

使用 wait 和 notify 时,要意识到通知没有排队,所以如果通知发生在等待发生之前,您将永远不会退出等待。因此,您永远不应该执行裸等待,也就是说,您应该始终测试一些条件以查看您是否应该等待。

sychronized(block) {
   while (!available) {
       block.wait();
   }
}

synchronized(block) {
    available = true;
    block.notifyAll();
}

ETC

于 2011-10-15T05:03:06.103 回答
0
package so_7775790;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.logging.Logger;


/**
 * barebones server -- ctl-C to kill it ;)
 */
public class Server implements Runnable {

    final static Logger log = Logger.getLogger(Server.class.getSimpleName());


    public static void main(String[] args) {
        final int port = 8000;
        final String tname = "my-server-thread";
        final Server server = new Server(port);

        try {
            Thread tserver = new Thread(server, tname);
            tserver.start();
            tserver.join();
        } catch (Exception e) {
            log.severe(e.getMessage());
        }
    }


    // -------------------------------------------------
    // server
    // -------------------------------------------------

    final int port;
    public Server(int port) {
        this.port = port;
    }
    public void run() {
        try{
            final ServerSocket srvsocket = new ServerSocket(port);
            log.info(String.format("Server started @ %s\n", srvsocket));

            while(!Thread.currentThread().isInterrupted()){
                Socket newclient = srvsocket.accept();

                // spawn thread and hand off new client to handler
                new Thread(new ClientHandler(newclient)).start();
            }
        }
        catch (Exception e) {
            log.severe(e.getMessage());
        }
        log.info("server stopped");
    }

    // -------------------------------------------------
    // client handler
    // -------------------------------------------------
    static class ClientHandler implements Runnable {
        final Socket socket;
        public ClientHandler(final Socket socket) {
            assert socket != null : "serverthread is null";
            this.socket = socket;
        }
        @SuppressWarnings("unused")
        @Override final
        public void run() {
            log.info(String.format("new client @ %s\n", socket.getRemoteSocketAddress()));
            try {
                final InputStream in = socket.getInputStream();
                final OutputStream out = socket.getOutputStream();

                // NOTE: this is just a stub busy loop!
                for(;;) {
                    /* your protocol impl here .. */
                }

            } catch (Exception e) {
                log.severe(e.getMessage());
            }
            finally {
                try {
                    socket.close();
                }
                catch (Exception e) {
                    log.severe(e.getMessage());
                }
            }
        }
    }
}
于 2011-10-15T06:08:05.877 回答