我正在将数据从pc(app)发送到控制台(端口10023 UDP协议),然后控制台从pc端口回复发送数据(随机端口)。 所以我有两个线程用于发送数据,一个用于从控制台监听数据.....所以每次我将数据发送到控制台时,我需要更改监听端口...所以我必须杀死听取线程并开始新的。
但是经过一段时间后,有一个和应用程序有大约x1000个线程打开。
如何在不创建新线程的情况下重新启动线程或更新侦听端口?
这里是本节的代码,整个文件是@ gihub
监听线程类:
public class Receiver implements Runnable {
private List<IReceiverListener> listeners;
private final static int PACKETSIZE = 48;
private int port;
public Receiver() {
listeners = new ArrayList();
}
public void addReceiverListener(IReceiverListener listener) {
listeners.add(listener);
}
private void update(String data, String adress) {
for (IReceiverListener listener : listeners) {
listener.receiveConsoleData(data, adress);
if (data.indexOf("active") > -1) {
listener.incrementWatchDog();
}
}
}
@Override
public void run() {
try {
// Convert the argument to ensure that is it valid
// Construct the socket
while (true) {
//System.out.println("Listen on Port:" + this.port);
DatagramSocket socket = new DatagramSocket(this.port);
// Create a packet
DatagramPacket packet = new DatagramPacket(new byte[PACKETSIZE], PACKETSIZE);
// Receive a packet (blocking)
socket.receive(packet);
// Print the packet
update(new String(packet.getData()), packet.getAddress().toString());
//logger.addLogData(new String(packet.getData())+" "+packet.getAddress().toString());
// Return the packet to the sender
socket.close();
}
} catch (IOException e) {
}
}
public void setPort(int port) {
this.port = port;
}
public int getPort() {
return port;
}
}
这里是我的端口updateFunction
@Override
public void updatePort(int port) {
receiverThread.interrupt();
receiverThread = null;
receiver.setPort(port);
receiverThread = new Thread(receiver);
receiverThread.start();
}
并且发送线程在发送数据时执行此操作:
listener.updatePort(dsocket.getLocalPort());
答案 0 :(得分:2)
这实际上不是线程问题。问题是,接收器线程卡在receive
方法中,因此无法对更改的端口做出反应。但是,从另一个线程调用方法DatagramSocket#close
会释放阻塞接收器线程SocketException
。
因此,您可以通过在接收端口更改时关闭当前接收的套接字来解决此问题。接收线程现在可以捕获SocketException
并创建一个侦听新端口的新DatagramSocket
。
无需杀死并重新创建线程。
首先将套接字放入字段中。这允许您从另一个线程访问它,因此您可以调用socket.close()
方法。其次,你将另一个try-catch块放入while(true)
循环中,该循环只捕获SocketException
。
这样的事可能会正常工作:
public class Receiver implements Runnable {
private static final int PACKETSIZE = 48;
private final ConcurrentLinkedQueue<IReceiverListener> listeners = new ConcurrentLinkedQueue<>();
private volatile DatagramSocket socket;
private volatile int port;
public Receiver(int port) {
this.port = port;
}
public void addReceiverListener(IReceiverListener listener) {
listeners.add(listener);
}
public void updatePort(int port) {
this.port = port;
DatagramSocket socket = this.socket;
if (socket != null) {
socket.close();
}
}
@Override
public void run() {
try {
while (true) {
receiveLoop(new DatagramSocket(port));
}
} catch (IOException e) {
// handle error
}
}
private void receiveLoop(DatagramSocket newSocket) throws IOException {
try (DatagramSocket socket = newSocket) {
this.socket = newSocket;
while (true) {
DatagramPacket packet = new DatagramPacket(new byte[PACKETSIZE], PACKETSIZE);
socket.receive(packet);
process(packet);
}
} catch (SocketException e) {
// port was changed -> return and restart with a new socket
} finally {
this.socket = null;
}
}
private void process(DatagramPacket packet) {
update(new String(packet.getData()), packet.getAddress().toString());
}
private void update(String data, String adress) {
for (IReceiverListener listener : listeners) {
listener.receiveConsoleData(data, adress);
if (data.indexOf("active") > -1) {
listener.incrementWatchDog();
}
}
}
}
请注意,这可能仍然包含一些错误。它只能让你大致了解如何解决这个问题。
答案 1 :(得分:1)
当您使用DatagramSocket
时,可以通过绑定将套接字更改为新端口而不是使用的端口:
socket.bind(new InetSocketAddress(new_port));
但请记住bind()
方法除非套接字已经打开并且分配了一个端口,否则不会起作用,所以第一次必须定期创建套接字,然后当你尝试更改时端口,只需绑定它。
以下是该过程的完整可视化:
public void video_udp_server(int port) throws Exception
{
byte[] receiveData = new byte[Integer.MAX_VALUE/100];
for(int i = 0; i < receiveData.length; i++){
receiveData[i] = ' ';
}
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
DatagramSocket socket = null;
try{
socket = new DatagramSocket(port);
}catch(Exception ex){
socket.bind(new InetSocketAddress(port));
}
socket.setReuseAddress(true);
socket.receive(receivePacket);
System.out.println(new String(receivePacket.getData()));
}