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

