Создайте Object, передайте Object другому конструктору Object, вызовите wait () для Object, затем notify () в Java - PullRequest
1 голос
/ 15 октября 2011

Я пытаюсь обработать несколько подключений к одному и тому же порту моего сервера.Я делаю это путем создания экземпляра Object и передачи его в конструктор для другого класса, который реализует Runnable.Затем я устанавливаю сокет в классе Runnable и вызываю notify () для переданного объекта после подключения клиента к порту.Это должно позволить серверу перезапустить свой цикл, создав другой экземпляр класса Runnable после получения уведомления.Однако в настоящее время wait () не достигается до тех пор, пока клиент не будет закрыт.Вот три соответствующих класса, которые у меня есть:

Класс сервера:

   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 (Runnable):

   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();
        }       
    }
}

Я что-то не так делаю с wait () и / или notify ()?

EDIT: я также пытался изменить навесное оборудование 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: Кажется, что он работает так, как сейчас, с точки зрения запуска новых потоков.Однако теперь я сталкиваюсь с другой проблемой: после ввода команды на стороне клиента любого данного клиента я не могу вводить дополнительные команды.Первая команда будет работать нормально (минус / выход; пока не совсем понял, как это должно работать), просто ничего не могу сделать после этого.Например, я могу зарегистрироваться (войти), но после этого больше ничего.Я могу зайти в другой экземпляр Client и перечислить всех текущих пользователей (работает), но опять же, после этого я не могу вводить дополнительные команды.Есть идеи, что может быть причиной этого?

Ответы [ 3 ]

2 голосов
/ 15 октября 2011

Проблема в том, что ваши дочерние потоки пытаются прослушивать порт 8000, но родительский поток уже делает это. То, что вам нужно сделать, это передать принять соединение из исходного сокета, а затем передать его дочернему потоку. Я не совсем уверен, как это сделать на Java, но я подозреваю, что это просто ..

Поместите это в свою основную ветку:

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

А потом, как только вы это получите, передайте clientSocket своему Thread.

Таким образом, порт 8000 прослушивает только один сокет, но вы можете заставить дочерние потоки обрабатывать каждое соединение.

0 голосов
/ 15 октября 2011
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());
                }
            }
        }
    }
}
0 голосов
/ 15 октября 2011

При использовании wait и notify следует понимать, что уведомления не помещаются в очередь, поэтому, если уведомление происходит до того, как происходит ожидание, вы никогда не выйдете из режима ожидания.Поэтому вы никогда не должны выполнять голое ожидание, то есть всегда должно быть какое-то условие, которое вы проверяете, чтобы увидеть, стоит ли ждать.

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

и

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

и т. Д.

...