繁体   English   中英

关闭一个套接字会关闭所有套接字,Java

[英]Closing one socket closes all of them, Java

我有一个连接到一台服务器的多线程聊天室。 它们都彼此独立地连接,登录和消息,都很好,但是当我使用其中一个客户端注销(并且服务器为该客户端实例执行socket.close())时,所有客户端都会注销。 在发布此内容之前,我查看了关于stackoverflow的其他问题,但没有一个问题与我的问题相同(我发现)。 注意:所有客户端都在我的计算机上本地运行,其中有2个以上,这就是我遇到该错误的方式。 它们是否位于同一IP上(尽管一切都在我的本地主机上完成了。)导致这种情况发生? 对于导致此问题的原因以及如何解决此问题的任何帮助或见解都将是非常有用的。 发生问题时,它还会将其输出到控制台:

Socket: Socket[addr=localhost/127.0.0.1,port=4000,localport=55650]

这是代码(如何重新创建错误在底部):

服务器类:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashSet;

public class ChatServer {

private static final int PORT = 4000;
private static HashSet<String> names = new HashSet<String>();
private static HashSet<ObjectOutputStream> outputs = 
                               new HashSet<ObjectOutputStream>();

public static void main(String[] args) throws IOException{
    System.out.println("The chat server is running...");
    ServerSocket listener = new ServerSocket(PORT);

    while(true){
        new Handler(listener.accept()).start();
    }
}

private static class Handler extends Thread{
    private String name;
    private Socket socket;
    private ObjectInputStream in;
    private ObjectOutputStream out;
    private boolean loggedOut;

    public Handler(Socket socket){
        this.socket = socket;

    }

    public void run(){
        try {
            out = new ObjectOutputStream(socket.getOutputStream());
            in = new ObjectInputStream(socket.getInputStream());
            loggedOut = false;

            while(true){
                Message message = (Message) in.readObject();
                System.out.println("Server recieved login message!");
                if(message.getNumber() == 0){
                    name = message.getName();
                    synchronized(names){
                        if(!names.contains(name)){
                            names.add(name);
                            break;
                        }else{
                            Message nameTaken = new Message(null, 3);
                            sendMessage(out, nameTaken);
                        }
                    }
                }
            }

            synchronized(outputs){
                outputs.add(out);   
            }

            for(ObjectOutputStream output: outputs){
                Message response = new Message(name, name + " has logged on.", 0);
                sendMessage(output, response);
            }

            while(true){
                System.out.println("Waiting for message...");
                Message message = (Message) in.readObject();
                Message response = null;
                if(message.getNumber() == 1 && message.getMessage() != null){
                    response = new Message(message.getName(),
                                message.getMessage(), 1);
                }else if(message.getNumber() == 2){
                    response = new Message(message.getName(), message.getName() + 
                                         " has logged off.", 2);
                    loggedOut = true;
                }   
                for(ObjectOutputStream output: outputs){
                    sendMessage(output, response);
                }
                if(loggedOut) break;
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }finally {
            if(name != null) names.remove(name);
            if(out != null) outputs.remove(out);
            try{
                socket.close();
            }catch(IOException e){
                e.printStackTrace();
            }
        }
    }

    private void sendMessage(ObjectOutputStream out, Message message){
        ObjectOutputStream outPutMessage = out;
        Message response = message;
        try {
            outPutMessage.writeObject(response);
            outPutMessage.flush();
        } catch (IOException e1) {
            e1.printStackTrace();
        }
    }
}
}

客户类别:

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;

public class ChatClient extends JFrame {
private JTextField inputField;
private JTextField nameField;
private JTextArea textArea;
private JButton loginButton;
private JButton logoutButton;
private JButton connectButton;
private JScrollPane textScroll;
private JScrollPane listScroll;
private JPanel textPanel;
private JPanel connectPanel;
private JPanel listPanel;
private JPanel inputPanel;
private JPanel namePanel;
private JPanel inputAndDisplayPanel;
private JPanel listAndLogoutPanel;
private ArrayList<String> stringList;
private JList list;
private final int CELL_WIDTH = 100;
private ObjectOutputStream out;
private ObjectInputStream in;
private JFrame chatFrame;
private ChatClient client;
private String name;
private final int PORT = 4000;
private boolean needServerData;
private boolean loggedIn;
private boolean previouslyLoggedIn;
private String serverAddress = null;
private Socket socket = null;   

private void run() throws IOException, ClassNotFoundException{
    needServerData = true;
    loggedIn = true;

    while(true){
        //Loops before a user is logged in, verifies that the host is usable
        while (socket == null) {
            if (needServerData) {
                try {
                    connectButton.setEnabled(false);
                    serverAddress = JOptionPane.showInputDialog(client,
                            "Enter server address: ", "Server Address",
                            JOptionPane.QUESTION_MESSAGE);
                    if(serverAddress == null){
                        JOptionPane.showMessageDialog(client,
                                "Click 'Connect' to re-enter server address.",
                                    "Info", JOptionPane.PLAIN_MESSAGE);
                        needServerData = false;
                        connectButton.setEnabled(true);
                    }else if(serverAddress.equals("")){
                        JOptionPane.showMessageDialog(client,
                                "Invalid input! Enter a valid server address.", 
                               "Error", JOptionPane.ERROR_MESSAGE);
                    }else {
                        socket = new Socket(serverAddress, PORT);
                        out = new ObjectOutputStream(socket.getOutputStream());
                        in = new ObjectInputStream(socket.getInputStream());
                        loginButton.setEnabled(true);
                        logoutButton.setEnabled(false);
                        nameField.setEnabled(true); 
                        connectButton.setEnabled(false);
                    }
                } catch (UnknownHostException e1) {
                    JOptionPane.showMessageDialog(client,
                            "Error: Unknown host!", "Error",
                            JOptionPane.ERROR_MESSAGE);
                }
            }
        }

        /*Loops after the user is logged in (will output to the textarea that that user logged in), 
         * reads in messages from server to append to the textarea and also determines if a user is 
         * logging out. Changes editable/enabled of buttons and text fields accordingly.
        */
        if(loggedIn) {
            Message message = (Message) in.readObject();
            if (message.getNumber() == 1) {
                textArea.append(message.getName() + ": "
                        + message.getMessage() + "\n");
            } else if (message.getNumber() == 0) {
                inputField.setEditable(true);
                textArea.append(message.getMessage() + "\n");
                loginButton.setEnabled(false);
                logoutButton.setEnabled(true);
            } else if (message.getNumber() == 2) {
                inputField.setText(null);
                inputField.setEditable(false);
                nameField.setText(null);
                nameField.setEnabled(false);
                connectButton.setEnabled(true);
                logoutButton.setEnabled(false);
                loggedIn = false;
                previouslyLoggedIn = true;
                textArea.append(message.getMessage() + "\n");
                System.out.println("Socket: " + socket);
            } else {
                JOptionPane.showMessageDialog(client,
                        "Error: That name is taken!", "Error",
                        JOptionPane.ERROR_MESSAGE);
            }
        }
    }
}

private String getServerAddress(){
    return JOptionPane.showInputDialog(client, "Enter server address: ",
            "Server Address", JOptionPane.QUESTION_MESSAGE);
}

public static void main(String[] args) throws IOException, ClassNotFoundException{
        ChatClient client = new ChatClient();
        try {
            client.run();
        } catch (IOException e) {
            e.printStackTrace();
        }       
}

public ChatClient(){
    //GUI Formatting Stuff
    inputField = new JTextField(30);
    nameField = new JTextField(10);
    textArea = new JTextArea(25, 30);
    loginButton = new JButton("Login");
    logoutButton = new JButton("Logout");
    connectButton = new JButton("Connect");
    textScroll = new JScrollPane(textArea,
            JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
            JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);

    textPanel = new JPanel();
    listPanel = new JPanel();
    inputPanel = new JPanel();
    namePanel = new JPanel();
    connectPanel = new JPanel();

    textArea.setEditable(false);
    textPanel.add(textScroll);

    stringList = new ArrayList<String>();
    list = new JList(stringList.toArray());
    list.setFixedCellWidth(CELL_WIDTH);
    listScroll = new JScrollPane(list);
    listPanel.add(listScroll);
    listAndLogoutPanel = new JPanel(new BorderLayout());
    listAndLogoutPanel.add(listPanel, BorderLayout.CENTER);
    listAndLogoutPanel.add(logoutButton, BorderLayout.SOUTH);
    listAndLogoutPanel.add(loginButton, BorderLayout.NORTH);

    inputField.setEditable(false);
    inputPanel.add(inputField);

    inputAndDisplayPanel = new JPanel(new BorderLayout());
    inputAndDisplayPanel.add(textPanel, BorderLayout.CENTER);
    inputAndDisplayPanel.add(inputPanel, BorderLayout.SOUTH);

    nameField.setEnabled(false);
    loginButton.setEnabled(false);
    logoutButton.setEnabled(false);
    namePanel.setLayout(new FlowLayout(FlowLayout.LEFT));
    namePanel.add(new JLabel("Name: "));
    namePanel.add(nameField);

    connectPanel.add(connectButton);
    JPanel topPanel = new JPanel(new BorderLayout());
    topPanel.add(namePanel, BorderLayout.WEST);
    topPanel.add(connectPanel, BorderLayout.EAST);

    JPanel centerPanel = new JPanel(new BorderLayout());
    centerPanel.add(inputAndDisplayPanel, BorderLayout.CENTER);
    centerPanel.add(topPanel, BorderLayout.NORTH);
    JPanel eastPanel = new JPanel(new BorderLayout());
    eastPanel.add(listAndLogoutPanel, BorderLayout.CENTER);

    setLayout(new BorderLayout());
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setVisible(true);
    add(centerPanel, BorderLayout.CENTER);
    add(eastPanel, BorderLayout.EAST);
    pack();
    setLocationRelativeTo(null);

    //Listeners
    loginButton.addActionListener(new ActionListener(){ 
        @Override
        public void actionPerformed(ActionEvent e) {
            name = nameField.getText();
            if(name.trim().length() < 3 || name.length() > 15 || name == null){
                JOptionPane.showMessageDialog(client,
                        "Error: Name must be between 3 and 15 characters long.", "Error",
                        JOptionPane.ERROR_MESSAGE);
            }else{
                Message message = new Message(name, 0);
                try {
                    out.writeObject(message);
                    out.flush();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
                loggedIn = true;
            }
        }
    });

    connectButton.addActionListener(new ActionListener(){
        @Override
        public void actionPerformed(ActionEvent arg0) {
            needServerData = true;
            if(previouslyLoggedIn) socket = null;
        }       
    }); 

    inputField.addActionListener(new ActionListener(){
        @Override
        public void actionPerformed(ActionEvent arg0) {
            Message message = new Message(name, inputField.getText(), 1);

            try {
                out.writeObject(message);
                out.flush();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
            inputField.setText(null);
        }       
    }); 

    logoutButton.addActionListener(new ActionListener(){
        @Override
        public void actionPerformed(ActionEvent arg0) {
            Message message = new Message(name, 2);
            try {
                out.writeObject(message);
                out.flush();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
            inputField.setText(null);
        }
    });
}
}

讯息类别:

import java.io.Serializable;

public class Message implements Serializable{

private int number;
private String message;
private String name;

public Message(String name, int number){
    this.name = name;
    this.number = number; 
}

public Message(String name, String message, int number){
    this.message = message;
    this.number = number;
    this.name = name;
}

public boolean isLogin(){
    if(number == 0) return true;
    return false;
}

public boolean isMessage(){
    if(number == 1) return true;
    return false;
}

public boolean isLogout(){
    if(number == 2) return true;
    return false;
}

public int getNumber() {
    return number;
}

public String getMessage() {
    return message;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

}

我知道这是一个很大的代码块,并且没有整齐地记录/格式化,因此我对此表示歉意。 我只是发布整个内容,因此您可以通过完整运行代码来复制问题。 另外,请忽略JList,因为自从遇到以来我还没有做到这一点,并且无法解决注销/套接字问题。 要重新创建该错误,请执行以下步骤:

  1. 运行服务器类
  2. 运行客户端类,输入“ localhost”作为服务器地址,然后输入名称并单击“登录”
  3. 运行另一个客户端类,然后按照步骤2
  4. 单击其中一个客户端类上的注销,您将看到另一个也已注销。

在此先感谢您的帮助,不胜感激!

在您的ChatClient类中,您需要更改行

} else if (message.getNumber() == 2) {

} else if (message.getNumber() == 2 && message.getName().equals(name)) {

因为当前的方式,每个聊天客户端都会收到一条消息,指出一个客户端已注销,并且每个客户端都通过注销来响应。 之后,您将需要处理message.getNumber()== 2的其他情况,因为从现在开始,您将假定它表示名称已被使用。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM