Есть два класса Client
и ChatWindow
, у клиента есть поля DatagramSocket
, InetAddress
и порта, а также методы для отправки, получения и закрытия сокета. Чтобы закрыть сокет, я использую анонимный поток socketCLOSE
Класс клиента
public class Client {
private static final long serialVersionUID = 1L;
private DatagramSocket socket;
private String name, address;
private int port;
private InetAddress ip;
private Thread send;
private int ID = -1;
private boolean flag = false;
public Client(String name, String address, int port) {
this.name = name;
this.address = address;
this.port = port;
}
public String receive() {
byte[] data = new byte[1024];
DatagramPacket packet = new DatagramPacket(data, data.length);
try {
socket.receive(packet);
} catch (IOException e) {
e.printStackTrace();
}
String message = new String(packet.getData());
return message;
}
public void send(final byte[] data) {
send = new Thread("Send") {
public void run() {
DatagramPacket packet = new DatagramPacket(data, data.length, ip, port);
try {
socket.send(packet);
} catch (IOException e) {
e.printStackTrace();
}
}
};
send.start();
}
public int close() {
System.err.println("close function called");
new Thread("socketClOSE") {
public void run() {
synchronized (socket) {
socket.close();
System.err.println("is socket closed "+socket.isClosed());
}
}
}.start();
return 0;
}
Класс ChatWindow
— это своего рода графический интерфейс, который расширяет JPanel
и реализует Runnable
. Внутри класса есть два потока — run
и Listen
.
public class ClientWindow extends JFrame implements Runnable {
private static final long serialVersionUID = 1L;
private Thread run, listen;
private Client client;
private boolean running = false;
public ClientWindow(String name, String address, int port) {
client = new Client(name, address, port);
createWindow();
console("Attempting a connection to " + address + ":" + port + ", user: " + name);
String connection = "/c/" + name + "/e/";
client.send(connection.getBytes());
running = true;
run = new Thread(this, "Running");
run.start();
}
private void createWindow() {
{
//Jcomponents and Layouts here
}
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
String disconnect = "/d/" + client.getID() + "/e/";
send(disconnect, false);
running = false;
client.close();
dispose();
}
});
setVisible(true);
txtMessage.requestFocusInWindow();
}
public void run() {
listen();
}
private void send(String message, boolean text) {
if (message.equals("")) return;
if (text) {
message = client.getName() + ": " + message;
message = "/m/" + message + "/e/";
txtMessage.setText("");
}
client.send(message.getBytes());
}
public void listen() {
listen = new Thread("Listen") {
public void run() {
while (running) {
String message = client.receive();
if (message.startsWith("/c/")) {
client.setID(Integer.parseInt(message.split("/c/|/e/")[1]));
console("Successfully connected to server! ID: " + client.getID());
} else if (message.startsWith("/m/")) {
String text = message.substring(3);
text = text.split("/e/")[0];
console(text);
} else if (message.startsWith("/i/")) {
String text = "/i/" + client.getID() + "/e/";
send(text, false);
} else if (message.startsWith("/u/")) {
String[] u = message.split("/u/|/n/|/e/");
users.update(Arrays.copyOfRange(u, 1, u.length - 1));
}
}
}
};
listen.start();
}
public void console(String message) {
}
}
Всякий раз, когда клиент закрывается, вызывается client.close()
, который порождает поток socketCLOSE, но поток ничего не делает, он входит в заблокированное состояние, как показывает трассировка стека -
Имя: socketClOSE Состояние: ЗАБЛОКИРОВАНО на java.net.DatagramSocket@1de1602, владелец: Listen Всего заблокировано: 1 Всего ожидано: 0
Трассировка стека: app//com.server.Client$2.run(Client.java:90)
Имя: Listen Состояние: RUNNABLE Всего заблокировано: 0 Всего ожидано: 0
Трассировка стека:
[email protected]/java.net.DualStackPlainDatagramSocketImpl.socketReceiveOrPeekData(собственный метод) [email protected]/java.net.DualStackPlainDatagramSocketImpl.receive0(DualStackPlainDatagramSocketImpl.java:130)
- заблокировано
- заблокирован java.net.DualStackPlainDatagramSocketImpl@3dd26cc7 [email protected]/java.net.DatagramSocket.receive(DatagramSocket.java:864)
- заблокирован java.net.DatagramPacket@6d21ecb
- заблокированное приложение java.net.DatagramSocket@1de1602//com.thecherno.chernochat.Client.receive(Client.java:59) app//com.thecherno.chernochat.ClientWindow$5.run(ClientWindow.java:183)
Это не позволяет потоку SocketCLOSE закрыть сокет внутри блока synchronized
, так как блокировка сокета удерживается потоком прослушивания. Как я могу заставить поток прослушивания снять свою блокировку, программа завершается без закрытия сокета, а отладчик показывает поток прослушивания как все еще работающий. Является ли реализация ошибочной или это можно решить?