2016-09-04 2 views
0

リモートコントロールに小さなアプリケーションを書き込んでいます。(Behringer x32)ミキシングコンソール。そして私はコミュニケーションに問題がありました。マルチスレッドスレッド再開

私はPC(アプリケーション)からコンソール(ポート10023 UDPプロトコル)にデータを送信していますが、コンソールからのデータは、送信データ(ランダムポート)からのポートに応答します。 私はデータを送信するために2つのスレッドとコンソールからデータを受信するために1つのスレッドを持っています.....したがって、コンソールにデータを送信するたびにリスニングポートを変更する必要があります。スレッドをリッスンし、新しいスレッドを開始します。

しかし、しばらくしてから、アプリケーションに約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

可能な重複http://stackoverflow.com/questions/671049/how-:

そして次は、プロセスの完全な視覚化したものですdo-you-kill-a-thread-in-java) – Raedwald

+0

スレッドを殺す別の解決策がある場合はshureしないでください –

答えて

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(); 
      } 
     } 
    } 
} 

を、これはまだいくつかのバグが含まれている可能性があることに注意してください。これはあなたにこれを解決する方法の大まかなアイデアを伝えることになっています。

+0

ありがとう! ...私はどのように新しいソケットを作成する必要がありますか?別の方法で?今のところ、私はちょうど実行メソッドの再帰呼び出しを行う..しかし、私はそれが本当に良いアイデアではないと思う:/? –

+0

@SimonMüller実際には、 'run'を再帰的に呼び出すべきではありません。これは最終的に' StackOverflowError'を引き起こすためです。コード例を使用して回答を更新しました。 –

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())); 

} 
[?あなたはどのようにJavaでスレッドを殺すん](の
+0

ソケットは受信メソッドで待機しているため、これは機能しません。とにかくソケットを閉じる必要があります...または間違っていますか? –

+0

いいえ、そうする必要はありません。単に 'socket.setReuseAddress(true);'を設定して、完全なビューを得るために私の答えを見てください。 –

関連する問題