我有以下客户端和服务器用于聊天程序。它们都工作正常,我可以让多个客户端一次使用服务器,一切正常。然后,如果没有客户端向服务器发送消息一段时间,比如五到十分钟,那么发送另一条消息,他们得到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();
}
}
}
}
答案 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();
}
}
}