java服务器Socket发送数据到错误的客户端

java server Socket sending data to wrong Client

如标题所述,如果您尝试执行此程序,启动 2 个客户端,并尝试向第一个客户端发送消息 'login' 或 'register',服务器将接收输入但重定向对第二个套接字的响应(最后一个连接的套接字)。当服务器尝试向客户端发送响应时,您可以通过查看打印在服务器控制台上的端口号来看到这一点

public class Server {
    private ServerSocket server;

    public Server() {
        try {
            server = new ServerSocket(10000);
            System.out.println("[INFO] server running");
        } catch (IOException e) {
            System.out.println(e);
        }
    }

    public static void main(String[] args) {
        Server server = new Server();
        server.run();
    }

    public void run() {
        try {
            while (true) {
                Socket clientRequest = server.accept();
                new Thread(new ServerThread(clientRequest)).start();
            }
        } catch (IOException e) {
            System.out.println(e);
        }
    }
}
class ServerThread implements Runnable {
    private static Socket socket;

    private static Connection dbConnection = null;
    private static OutputStream outputStream;
    private static ObjectOutputStream objectOutputStream;
    private static InputStream inputStream;
    private static ObjectInputStream objectInputStream;

    private static List<String> messages = new ArrayList<String>();
    private static MessageDigest messageDigest;

    private static String username = "";
    private static boolean invalidUsername;

    public ServerThread(Socket richiestaClient) {
        try {
            socket = richiestaClient;
            System.out.println("[INFO] " + socket + " connected ");
            outputStream = socket.getOutputStream();
            objectOutputStream = new ObjectOutputStream(outputStream);
            inputStream = socket.getInputStream();
            objectInputStream = new ObjectInputStream(inputStream);
        } catch (IOException e) {
            System.out.println("[ERROR] errore di i/o");
        }
    }

    public void run() {
        // conversazione lato server
        try {
            boolean active = true;
            while (active) {
                System.out.println("[THREAD] " + Thread.currentThread().getName());
                System.out.println("[DEBUG] current socket: " + socket);
                String msg = (String) objectInputStream.readObject();
                System.out.println("[CLIENT] " + msg);
                // -- SELECT CASE FOR USER LOGIN/REGISTER --
                switch (msg) {
                    case "login":
                        login(dbConnection);
                        break;
                    case "register":
                        register(dbConnection);
                        break;
                    default:
                        break;
                }
            }
        } catch (IOException | ClassNotFoundException e) {
            System.out.println("[ERROR] errore nello switch azioni ioexception " + e);
        }
    }

    private static void register(Connection dbConnection) {
        System.out.println("[THREAD] " + Thread.currentThread().getName());
        System.out.println("[DEBUG] client selected register " + socket);
        messages.add("username");
        messages.add("You selected register");
        invalidUsername = true;
        while (invalidUsername) {
            messages.add("input the username you want");
            send(messages);
            // getting username (assuming not taken for testing purpose)
            boolean usernameExists = false;
            if (usernameExists) {
                System.out.println("[DEBUG] username exists, not available for the registration");
                messages.add("username");
                messages.add("sorry, username is taken :(");
            } else {
                System.out.println("[DEBUG] username does not exists, available for the registration");
                messages.add("password");
                messages.add("username is not taken yet :)");
                invalidUsername = false;
            }
        }
        System.out.println("[DEBUG] username not taken, sending result to " + socket);
    }

    private static void login(Connection dbConnection) {
        System.out.println("[THREAD] " + Thread.currentThread().getName());
        System.out.println("[DEBUG] client selected login " + socket);
        messages.add("username");
        messages.add("You selected login");
        messages.add("Input your username");
        send(messages);
        try {
            username = (String) objectInputStream.readObject();
            System.out.println("[INFO] received " + username + " from " + socket);
        } catch (ClassNotFoundException | IOException e) {
            System.out.println("[DEBUG] error while waiting for client login username");
        }
    }

    // sending messages, flushing stream and clearing messages list
    private static void send(List<String> messagesToSend) {
        System.out.println("[THREAD] " + Thread.currentThread().getName());
        System.out.println("[DEBUG] Sending data to " + socket);
        try {
            objectOutputStream.writeObject(messagesToSend);
            objectOutputStream.flush();
            messages.clear();
        } catch (IOException e) {
            System.out.println("[ERROR] error occurred while sending message");
        }
    }
}
public class Client {
    private static Socket socket;

    private static OutputStream outputStream;
    private static ObjectOutputStream objectOutputStream;
    private static InputStream inputStream;
    private static ObjectInputStream objectInputStream;

    public Client() {
        try {
            socket = new Socket("127.0.0.1", 10000);
            outputStream = socket.getOutputStream();
            objectOutputStream = new ObjectOutputStream(outputStream);
            inputStream = socket.getInputStream();
            objectInputStream = new ObjectInputStream(inputStream);
        } catch (IOException e) {
            System.out.println(e);
        }
    }

    public static void main(String[] args) {
        Client client = new Client();
        client.conversazione();
    }

    public void conversazione() {
        // conversazione lato client
        Scanner scan = new Scanner(System.in);
        String command = "default";
        String message = "";
        String username = "";
        List<String> messages = new ArrayList<String>();
        System.out.println("what do you want to do? (login/register)");
        while (true) {
            try {
                switch (command) {
                    case "default":
                        System.out.println("[DEBUG] switch option: default");
                        message = scan.nextLine();
                        send(message);
                        break;
                    case "username":
                        System.out.println("[DEBUG] switch option: username");
                        username = scan.nextLine();
                        send(username);
                        break;
                    case "password":
                        System.out.println("[DEBUG] switch option: password");
            // not implemented yet
                        break;
                    default:
                        break;
                }
        // getting messages from the server, using the first one as "header" to know what to do
                System.out.println("[DEBUG] waiting for message " + socket);
                messages = (List<String>) objectInputStream.readObject();
                System.out.println("Received [" + (messages.size() - 1) + "] messages from: " + socket);
                command = messages.get(0);
                messages.remove(0);
                for (String msg : messages) {
                    System.out.println(msg);
                }
                messages.clear();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    // send message to the server and reset the stream
    public static void send(String message) {
        try {
            System.out.println("[DEBUG] sending data as " + socket);
            objectOutputStream.writeObject(message);
            objectOutputStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

attributes中的“static”关键字表示属性的值分布在class的所有对象中,因为该值不存储在对象中,而是简单地说,存储在class.

如果您不知道“静态”到底是什么意思,请查看以下页面:Static keyword in java

public class Test {
  // static attribute
  private static String hello = "world";

  public void setHello(String newValue) {
    Test.hello = newValue;
  }

  public void printHello() {
    System.out.println(Test.hello);
  }
}

public class Main {
  public static void main(String[] args) {
    Test test1 = new Test();
    Test test2 = new Test();
    test1.printHello(); // -> "world"
    test2.printHello(); // -> "world"

    // Note that we seem to change only the value of "hello" in object "test1". 
    // However, since the attribute "test1" is static, the value "changes" for all objects of the class.
    test1.setHello("Whosebug");

    test1.printHello(); // "Whosebug"
    test2.printHello(); // "Whosebug" <- same as in "test1"
  }
}

问题是在您的“ServerThread”中 class 所有属性都是静态的,因此它们在不同的对象之间共享。因此,如果您覆盖客户端 2 中的变量“socket”,您将覆盖客户端 1 的“socket”。

因此从 Client 和 ServerThread 的属性和方法中删除“static”关键字class,这应该可以解决问题。