英文:
Java socket chatting program (chatting member suddenly leaves the room without error)
问题
我们通过Java制作了一个套接字聊天程序。但是在有2个或更多的聊天成员时,出现了一个问题。如果有成员A、B和C(按字母顺序输入),当A(或B)离开房间时,最后一个成员C会跟随A(或B)一起离开房间,而不会出现错误。如果有成员D,先前的成员离开房间,最后一个成员(在本例中是成员D)会与先前的成员一起离开房间,而不会出现错误。我们该如何解决这个问题?
// 发生错误的过程 //
A进入 > B进入 > C进入 > D进入 > A或B或C离开(D也会离开,为什么?)只有最后一个成员会与先前的成员一起离开。
服务器代码:
public class Server {
// ... (原有代码)
}
客户端代码:
package chat.client;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import javax.swing.JOptionPane;
public class Client {
// ... (原有代码)
}
请注意,这是你提供的代码的翻译部分。如果你需要进一步的解释或讨论,请随时提问。
英文:
We made socket chatting program by Java. But we have a problem that happens when there are 2 or more chatting members. If there are members named A, B, and C(entered by alphabet order), when A (or B) left the room, the last member(C) left the room with A(or B) without error. If there is member D, the prior member leaves the room, the final member(this case member D) leaves the room with prior member without error. How can we solve it?
//Process that error happened//
A enters > B enters > C enters > D enters > A or B or C leaves(D leaves too why???) Only the last member leaves with prior member
Server
public class Server {
public static final int PORT = 7777;
private ServerSocket serverSocket;
private Socket socket;
private ServerGUI gui;
private String msg;
private int count = 0;
private int NMC=0;
private Map<String, DataOutputStream> clientsMap = new HashMap<String, DataOutputStream>();
public final void setGUI(ServerGUI gui) {
this.gui = gui;
}
public void setting() throws IOException {
Collections.synchronizedMap(clientsMap);
serverSocket = new ServerSocket(PORT);
String hostAddress = InetAddress.getLocalHost().getHostAddress();
while (true) {
System.out.println("waiting connection - " + hostAddress + ":" + PORT);
socket = serverSocket.accept();
System.out.println("connected from" + socket.getInetAddress());
Receiver receiver = new Receiver(socket);
receiver.start();
}
}
public void addClient(String name, DataOutputStream out) throws IOException {
clientsMap.put(name, out);
sendMessage(name + "entered\n");
gui.appendMsg(name + "entered\n");
count++;
gui.appendMsg("joining member : " + count + "\n");
}
public void sendMessage(String msg) {
Iterator<String> member = clientsMap.keySet().iterator();
String key = "";
while (member.hasNext()) {
key = member.next();
try {
clientsMap.get(key).writeUTF(msg);
} catch (IOException e) {
e.printStackTrace();
}
}
}
class Receiver extends Thread {
private DataInputStream in;
private DataOutputStream out;
private String name;
public Receiver(Socket socket) throws IOException {
out = new DataOutputStream(socket.getOutputStream());
in = new DataInputStream(socket.getInputStream());
name = in.readUTF();
if (count != 0) {
NMC=NameMatch(name);
}
if (NMC == 1 || count == 0) {
addClient(name, out);
} else {
out.writeUTF("not available");
socket.close();
out.close();
in.close();
}
}
public void run() {
try {
while (in != null) {
msg = in.readUTF();
sendMessage(msg);
gui.appendMsg(msg);
}
} catch (IOException e) {
try {
if (NMC == 0) {
socket.close();
in.close();
out.close();
NMC++;
} else {
socket.close();
in.close();
out.close();
clientsMap.remove(name);
count--;
gui.appendMsg(name + "has left.\n");
gui.appendMsg("joining member : " + count + "\n");
sendMessage(name + "has left.\n");
}
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
public int NameMatch(String name) {
Iterator<String> member = clientsMap.keySet().iterator();
String key = "";
while (member.hasNext()) {
int i = 0;
if (i == count) {
break;
}else{
key = member.next();
if (key.equals(name)) {
return 0;
}
}
i++;
}
return 1;
}
public static void main(String[] args) throws IOException {
Server server = new Server();
server.setting();
}
}
'''
Client
package chat.client;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import javax.swing.JOptionPane;
public class Client {
private Socket socket;
private DataInputStream in;
private DataOutputStream out;
private ClientGUI gui;
private String msg;
private String Name;
private int PORT = 0;
private String IP = "";
public final void setGUI(ClientGUI gui) {
this.gui = gui;
}
class connect extends Thread {
public connect() {
try {
socket = new Socket(IP, PORT);
System.out.println("Connected.");
out = new DataOutputStream(socket.getOutputStream());
in = new DataInputStream(socket.getInputStream());
out.writeUTF(Name);
if (in.readUTF().equals("not available")) {
out.close();
in.close();
socket.close();
JOptionPane.showMessageDialog(null, "not available");
System.exit(1);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void run() {
try {
while (in != null) {
msg = in.readUTF();
gui.appendMsg(msg);
}
} catch (IOException e) {
try {
out.close();
in.close();
socket.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
public static void main(String[] args) throws IOException {
Client client = new Client();
client.setConnect();
}
public void setConnect() {
connect c = new connect();
c.start();
}
public void sendMessage(String msg) {
try {
out.writeUTF(msg);
} catch (IOException e) {
e.printStackTrace();
}
}
public void setNickname(String Name) {
this.Name = Name;
}
public void Port(int port) {
this.PORT = port;
}
public void Ip(String ip) {
this.IP = ip;
}
}
答案1
得分: 0
在 Server.Receiver
类中,你没有保留连接的客户端套接字的引用。这意味着当 run
方法使用以下代码关闭套接字时:
socket.close();
实际上它正在使用包含的 Server
类中的 socket
变量,这恰好是最近连接的用户的连接。这意味着当 A、B 或 C 中的任何一个离开时,D 会断开连接。
你可以通过从 Receiver.run
方法中简单地删除对 socket.close()
的调用来修复这个问题。当关闭与其关联的输入或输出流时,套接字会被关闭。
避免将来出现类似错误的提示:
-
尽可能避免使用实例变量。如果
Server.socket
是一个局部变量,错误从一开始就会变得清晰,因为首先就不会有一个叫做 "socket" 的变量供你使用。Socket socket = serverSocket.accept(); System.out.println("connected from" + socket.getInetAddress()); Receiver receiver = new Receiver(socket);
-
当创建嵌套类时,最好一开始将它们定义为
static
:static class Receiver extends Thread {
这可以防止意外使用封闭对象实例。当你发现需要使用封闭对象实例时,总是可以去掉 "static" 修饰符。
英文:
In the Server.Receiver
class, you don't keep a reference to the client socket of the connection. This means that when the run
method closes the socket with:
socket.close();
it is actually using the socket
variable from the containing Server
class, which happens to be the connection of the latest user that has connected. This means that when any of A, B, or C leaves, D gets disconnected.
You can fix this by simply removing the call to socket.close()
from the Receiver.run
method. The socket is closed when you close the input or output stream associated with it.
Tips to avoid similar bugs in the future:
-
Avoid using instance variables whenever you can. If
Server.socket
was a local variable, the error would have been clear from the start because there wouldn't have been a "socket" variable for you to use in the first place.Socket socket = serverSocket.accept(); System.out.println("connected from" + socket.getInetAddress()); Receiver receiver = new Receiver(socket);
-
When you create nested classes, prefer make them
static
at first:static class Receiver extends Thread {
This prevents accidentally using the enclosing object instance. You can always remove the "static" modifier when you find that you need to use the enclosing object instance.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论