株式会社イーヴ

EeBlog(テクニカルブログ)

TOP > EeBlog > 第100回 ソケットの入出力

第100回 ソケットの入出力

引き続き「ソケットの入出力」について見ていきます。

今回は前回の続きです。
前回はUDP通信によるデータの送信プログラムを実装したので、今回はそのデータを受信するプログラムを実装してみたいと思います。

次のサンプルコードは、データを受信するプログラムです。

import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.net.DatagramPacket;
 import java.net.DatagramSocket;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
 import java.util.concurrent.LinkedBlockingQueue;


 import javax.swing.DefaultListModel;
 import javax.swing.JButton;
 import javax.swing.JFrame;
 import javax.swing.JList;
 import javax.swing.JPanel;
 import javax.swing.JScrollPane;
 import javax.swing.JTextField;
 import javax.swing.SwingUtilities;
 import javax.swing.UIManager;


 public class Main extends JFrame implements ActionListener {


     private static final long serialVersionUID = 1L;


     public static void main(String[] args) {
         SwingUtilities.invokeLater(new Runnable() {
             public void run() {
                 try {
                     UIManager.setLookAndFeel(UIManager
                                             .getSystemLookAndFeelClassName());
                 } catch (Exception e) {}
                 new Main();
             }
         });
     }


     private final Receiver receiver = new Receiver();
     private final JTextField port = new JTextField("9999");
     private final DefaultListModel defaultListModel = new DefaultListModel();
     private final JList jList = new JList(defaultListModel);
     private final JScrollPane jScrollPane = new JScrollPane(jList);
     private final JPanel jPanel = new JPanel();
     private final JButton startButton = new JButton("受信開始");
     private final JButton endButton = new JButton("受信終了");


     public Main() {
         startButton.addActionListener(this);
         endButton.addActionListener(this);
         endButton.setEnabled(false);
         jPanel.add(port);
         jPanel.add(jScrollPane);
         jPanel.add(startButton);
         jPanel.add(endButton);
         setContentPane(jPanel);
         setTitle("RECEIVER");
         setDefaultCloseOperation(EXIT_ON_CLOSE);
         setSize(300, 225);
         setResizable(false);
         setLocationRelativeTo(null);
         setVisible(true);
     }


     public void actionPerformed(ActionEvent actionEvent) {
         if (startButton.equals(actionEvent.getSource())) {
             receiver.start(port.getText());
         } else if (endButton.equals(actionEvent.getSource())) {
             receiver.end();
         }
     }


     private void messageOut(final Object object) {
         if (SwingUtilities.isEventDispatchThread()) {
             defaultListModel.addElement(object);
         } else {
             SwingUtilities.invokeLater(new Runnable() {
                 public void run() {
                     defaultListModel.addElement(object);
                 }
             });
         }
     }


     private void changeButtonEnabled() {
         startButton.setEnabled(!startButton.isEnabled());
         endButton.setEnabled(!endButton.isEnabled());
     }


     private class Receiver implements Runnable {


         private DatagramSocket datagramSocket;
         private BlockingQueue blockingQueue 
                                       = new LinkedBlockingQueue();
         private ExecutorService executorService 
                                       = Executors.newFixedThreadPool(2);
         private Future future;


         public void run() {
             try {
                 while (!datagramSocket.isClosed()) {
                     DatagramPacket datagramPacket 
                                 = new DatagramPacket(new byte[256], 256);
                     datagramSocket.receive(datagramPacket);
                     blockingQueue.offer(datagramPacket.getData());
                 }
             } catch (Exception e) {
                 e.printStackTrace();
             } finally {
                 messageOut("受信終了");
             }
         }


         private void start(String port) {
             try {
                 datagramSocket = new DatagramSocket(Integer.parseInt(port));
                 executorService.execute(this);
                 future = executorService.submit(new Runnable() {
                     public void run() {
                         while (true) {
                             byte[] bytes = null;
                             try {
                                 bytes = blockingQueue.take();
                             } catch (InterruptedException e) {
                                 return;
                             }
                             try {
                                 messageOut(new String(bytes, "Shift-JIS"));
                             } catch (Exception e) {}
                         }
                     }
                 });
                 messageOut("受信開始");
                 changeButtonEnabled();
             } catch (Exception e) {
                 messageOut("受信できません。");
             }
         }


         private void end() {
             datagramSocket.close();
             future.cancel(true);
             changeButtonEnabled();
         }
     }
 }

DatagramSocketクラスは前回はデータグラムパケットを送信するためのソケットでした。
今回はデータグラムパケットを受信するためのソケットとして機能します。
「受信開始」ボタンを押すと、このソケットからデータグラムパケットを受信します。
receiveメソッドがデータグラムパケットの受信を待機している部分です。

また、受け取ったデータはキューに格納し、別のスレッドで順に取り出して処理を行うようにしました。
「受信終了」ボタンを押すと、このスレッドを終了するために、cancelメソッドにより割り込みを行っています。