第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 BlockingQueueblockingQueue = 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メソッドにより割り込みを行っています。