Java聊天服务器/客户端连接删除

时间:2015-04-20 15:07:54

标签: java socket.io network-programming

我有以下客户端和服务器用于聊天程序。它们都工作正常,我可以让多个客户端一次使用服务器,一切正常。然后,如果没有客户端向服务器发送消息一段时间,比如五到十分钟,那么发送另一条消息,他们得到java.net.Socket-TimeoutException:read timed out。

如果他们尝试发送另一条消息,则会收到java.net.SocketException:Connection Reset

有谁能告诉我为什么会这样,以及如何解决它?

Client code:
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.imageio.*;
import javax.swing.*;
import java.io.*;
import java.net.*;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

public class ChatClient extends JFrame {
    static final String DEFAULT_USERNAME = "Anonymous";
    static final String AVATAR_FILE_NAME = "avatar.png";
    static final String CONFIG_FILE_NAME = "config.ini";
    static final int AVATAR_SIZE = 48;
    static final String DEFAULT_SERVER = "localhost";
    static final int DEFAULT_DELAY = 1000;

    JLabel nameLabel;
    JPanel msgList;
    JScrollPane scroll;
    ChatAvatar avatar;
    String username;

    PrintWriter out;
    BufferedReader in;

    Timer timer = new Timer();

    public ChatClient(String server, int port) {
        super("Simple Chat Client");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        ChatAvatar a = ChatAvatar.fromFile(AVATAR_FILE_NAME);
        setAvatar(a);

        username = DEFAULT_USERNAME;
        try {
            BufferedReader in = new BufferedReader(new FileReader(CONFIG_FILE_NAME));
            String line = in.readLine();
            if(line != null) {
                username = line;
            }
            in.close();
        } catch(Exception e3) {

        }

        JPanel c = (JPanel)getContentPane();

        msgList = new JPanel();
        msgList.setLayout(new BoxLayout(msgList, BoxLayout.Y_AXIS));

        scroll = new JScrollPane(msgList);
        scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        c.add("Center", scroll);

        JPanel south = new JPanel(new BorderLayout());
        JTextField msgField = new JTextField("");
        JPanel iconPanel = new JPanel() {
            public void paint(Graphics g) {
                g.drawImage(avatar.getImage(), 0, 0, this);
            }
        };
        iconPanel.setPreferredSize(new Dimension(AVATAR_SIZE, AVATAR_SIZE));
        south.add("West", iconPanel);
        nameLabel = new JLabel(username);
        nameLabel.setForeground(Color.gray);
        JPanel south2 = new JPanel(new BorderLayout());
        south2.add("North", nameLabel);
        south2.add("Center", msgField);
        south.add("Center", south2);
        c.add("South", south);

        msgField.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                ChatMessage msg = new ChatMessage(avatar, username, msgField.getText());
                addMsg(msg);
                msgField.setText("");
                try {
                    sendMessageToServer(msg);
                } catch(Exception ex) {
                    ex.printStackTrace();
                }
            }
        });

        c.setPreferredSize(new Dimension(500, 150));

        JMenuBar b = new JMenuBar();
        JMenu fm = new JMenu("File");
        JMenuItem fmi1 = new JMenuItem("Choose Avatar...");
        fm.add(fmi1);
        JMenuItem fmi2 = new JMenuItem("Change Name...");
        fm.add(fmi2);
        b.add(fm);
        setJMenuBar(b);

        final JFrame t = this;

        fmi2.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                String name = JOptionPane.showInputDialog(t, "Enter new name:");
                if(name != null) setUsername(name);
            }
        });

        fmi1.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                JFileChooser fc = new JFileChooser(".");
                int result = fc.showOpenDialog(t);
                if (result == JFileChooser.APPROVE_OPTION) {
                    File file = fc.getSelectedFile();
                    setAvatar(ChatAvatar.fromFile(file));
                }
            }
        });

        pack();

        try {
            new Thread() {
                public void run() {
                    Socket s;

                    while(true) {
                        try {
                            s = new Socket(InetAddress.getByName(server), port);

                            System.out.println("Socket connected: "+s);
                            break;
                        } catch(Exception ex) {
                            try {
                                Thread.sleep(DEFAULT_DELAY);
                            } catch(Exception ex2) {}
                            continue;
                        }
                    }

                    try {
                        out = new PrintWriter(new OutputStreamWriter(s.getOutputStream()), true);
                        in = new BufferedReader(new InputStreamReader(s.getInputStream()));
                    } catch(Exception ex) {
                        System.err.println("Could not open streams");
                        ex.printStackTrace();
                    }

                    timer.scheduleAtFixedRate(new TimerTask() {
                        public void run() {
                            try {
                                List<ChatMessage> msgs = getNewMessagesFromServer();
                                if(msgs != null) {
                                    for(ChatMessage m : msgs) {
                                        addMsg(m);
                                    }
                                }
                            } catch(Exception ex) {
                                ex.printStackTrace();
                            }
                        }
                    }, 0, DEFAULT_DELAY);
                }
            }.start();
        } catch(Exception ex2) {
            ex2.printStackTrace();
        }
    }

    // TODO - this should ask the server for new messages, and return them in a list
    synchronized List<ChatMessage> getNewMessagesFromServer() throws IOException {
        out.println("TODO");

        return null;
    }

    // TODO - this should send our new message to the server
    synchronized void sendMessageToServer(ChatMessage msg) throws IOException {
        out.println("TODO: "+msg);
    }

    void addMsg(ChatMessage msg) {
        msgList.add(new MsgPanel(msg));
        scroll.revalidate();
        SwingUtilities.invokeLater(new Thread() {
            public void run() {
                JScrollBar v = scroll.getVerticalScrollBar();
                v.setValue(v.getMaximum());
            }
        });
    }

    void setUsername(String name) {
        username = name;
        nameLabel.setText(username);
        try {
            PrintWriter out = new PrintWriter(new FileWriter(CONFIG_FILE_NAME), true);
            out.println(username);
            out.close();
        } catch(IOException ex) {

        }
    }

    void setAvatar(ChatAvatar icon) {
        if(icon == null) {
            icon = ChatAvatar.fromIcon(UIManager.getIcon("OptionPane.informationIcon"));
        }

        avatar = icon.getScaled(AVATAR_SIZE, AVATAR_SIZE);
        repaint();

        avatar.toFile(AVATAR_FILE_NAME);
    }

    class MsgPanel extends JLabel {
        MsgPanel(ChatMessage msg) {
            super("<html><font size=\"+1\" color=\"#9a9a9a\">"+msg.username+"</font><br>"+msg.text+"</html>", msg.avatar, SwingConstants.LEFT);
            setBackground(Color.red);

            Dimension d = getMaximumSize();
            Dimension d2 = new Dimension(d.width, getMinimumSize().height);
            setMaximumSize(d2);

        }
    }

    public static void main(String[] argv) {
        String server = DEFAULT_SERVER;
        int port = ChatServer.DEFAULT_PORT;

        if(argv.length >= 2) {
            try {
                server = argv[0];
                port = Integer.parseInt(argv[1]);
            } catch(Exception ex) {

            }
        }

        ChatClient c = new ChatClient(server, port);
        c.setVisible(true);
    }
}

这里是chaserver

import java.io.*;
import java.net.*;
import java.util.*;

public class ChatServer {
    public static final int DEFAULT_PORT = 8080;

    public ChatServer(int port) {
        System.out.printf("Server starting on localhost, port: %d\n", port);

        List<Handler> handlers = new LinkedList<Handler>();

        try {
            ServerSocket server = new ServerSocket(port);
            System.out.println("Server started");
            while (true) {
                Socket clientSocket = server.accept();
                Handler h = new Handler(clientSocket.getInputStream(), clientSocket.getOutputStream(), handlers);
                handlers.add(h);
                h.start();
            }
        } catch(Exception ex) {
            System.err.println("Could not open socket");
            ex.printStackTrace();
        }
    }

    public static void main(String[] argv) {
        int port = DEFAULT_PORT;

        if(argv.length > 1) {
            try {
                port = Integer.parseInt(argv[0]);
            } catch(Exception ex) {

            }
        }

        new ChatServer(port);
    }

    class Handler extends Thread {
        BufferedReader in;
        PrintWriter out;
        List<Handler> handlers;

        Handler(InputStream in, OutputStream out, List<Handler> handlers) {
            this.in = new BufferedReader(new InputStreamReader(in));
            this.out = new PrintWriter(new OutputStreamWriter(out), true);
            this.handlers = handlers;
        }

        public void run() {
            try {
                while(true) {
                    String line = in.readLine();

                    // TODO - handle incoming commands from this client (for now we just print the command)
                    System.out.println(line);
                }
            } catch(Exception ex) {
                ex.printStackTrace();
            }
        }
    }
}

1 个答案:

答案 0 :(得分:0)

我注意到您在服务器中注释掉了以下行:

serverSock.setSoTimeout(10000);

理论上这应该禁用超时,但其他因素可能会影响超时和断开连接,例如客户端和服务器之间的防火墙和路由器。

您真正想要做的是从客户端处理超时异常。 在异常捕获中,您可以尝试重新创建连接(即再次调用setUpNetworking)并重试发送相同的消息(达到某个限制)。

例如:

public void actionPerformed(ActionEvent ev) {
    boolean sent = false;
    int tries = 0;
    int maxTries = 3;

    while(!sent && tries <= maxTries) {
        tries++;
        try {
            // print line to the PrintWriter writer
            writer.println(outgoing.getText());
            writer.flush();
            sent = true;
            outgoing.setText("");
            outgoing.requestFocus();
        } catch (Exception ex) {
            ex.printStackTrace();
            // re-establish connection
            this.setUpNetworking();
        }
    }
}