java - 用Java创建对象,将对象传递给对象的另一个构造函数,调用对象上的wait(),然后调用 notify()

  显示原文与译文双语对照的内容

正在尝试在服务器的同一端口上处理多个连接。 我通过实例化对象并将它的传递给另一个类的构造函数来实现,后者实现。 然后在Runnable类中设置一个套接字,并在客户端连接端口后调用所传递的对象上的notify() 。 这样,服务器就可以重新启动它的循环,在被通知之后创建可以运行类的另一个实例。 但是,当前 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();
 } 
 }
}

我的wait() 和/或者 notify() 有什么问题?

编辑:我还尝试更改 implements Runnable to扩展线程然后在服务器中更改. run(),但这将会使我的错误。


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)

:它似乎在开始新线程的时候正在工作。 但是,我现在遇到了另一个问题: 在客户端的客户端输入命令后,无法输入其他命令。 第一个命令将工作得很好( minus/exit; 还没有完全了解它应该如何工作),不能在那之后做任何事情。 例如我可以 register ( 登录),但之后没有其他。 我可以进入另一个客户端实例并列出所有当前用户的( 工时),但是以后,我不能再输入其他命令。 你知道什么可能会导致?

时间: 原作者:

问题在于,子线程正在尝试侦听端口 8000,但是父线程已经在执行该操作。 你需要做的是通过接受来自原始套接字的连接,然后将它交给子线程。 我并不确定如何在Java中执行这个操作,但是我觉得这只是个问题。

把这个放在你的主线上:


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

一旦你得到了这个,把 clientSocket 传给你的Thread

这样,端口 8000上只监听一个套接字,但是你可以让子线程处理每个连接。

原作者:

当使用等待和通知时,意识到通知没有排队,所以如果通知发生在等待发生之前,你将永远不会退出等待。 因这里,你永远不应该执行裸等等,这就是你应该测试的一些条件,看看你是否应该等待。


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


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

等等等

原作者:

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 @ %sn", 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 @ %sn", 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());
 }
 }
 }
 }
}

原作者:
...