第97回 ソケットの入出力
今回からは「ソケットの入出力」について見ていきます。
Javaにおけるソケット通信は、ソケットのストリームに対して読み書きをすることで行うことができます。
今回は、サーバとクライアントの間でデータのやり取りを行うプログラムを作成してみたいと思います。
次のサンプルコードは、サーバを起動し、クライアントの接続要求に応答するプログラムです。
Swingアプリケーションとして実装しました。
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Observable;
import java.util.Observer;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
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) throws Exception {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
UIManager.setLookAndFeel(UIManager
.getSystemLookAndFeelClassName());
} catch (Exception e) {}
new Main();
}
});
}
private final Service service = new Service();
private final JPanel jPanel = new JPanel();
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 JButton startupButton = new JButton("起動");
private final JButton shutdownButton = new JButton("停止");
public Main() {
startupButton.addActionListener(this);
shutdownButton.addActionListener(this);
jPanel.add(port);
jPanel.add(jScrollPane);
jPanel.add(startupButton);
jPanel.add(shutdownButton);
setContentPane(jPanel);
setTitle("SERVER");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(300, 225);
setResizable(false);
setLocationRelativeTo(null);
setVisible(true);
}
public void actionPerformed(ActionEvent actionEvent) {
if (startupButton.equals(actionEvent.getSource())) {
service.startup(port.getText());
}
if (shutdownButton.equals(actionEvent.getSource())) {
service.shutdown();
}
}
private void messageOut(final Object object) {
if (SwingUtilities.isEventDispatchThread()) {
defaultListModel.addElement(object);
} else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
defaultListModel.addElement(object);
}
});
}
}
private class Service extends Observable implements Runnable {
private final ExecutorService executorService
= Executors.newCachedThreadPool();
private ServerSocket serverSocket;
public void run() {
while (!serverSocket.isClosed()) {
try {
Session session = new Session(serverSocket.accept());
addObserver(session);
executorService.execute(session);
} catch (Exception e) {}
}
}
private void startup(String port) {
if (serverSocket == null || serverSocket.isClosed()) {
try {
serverSocket = new ServerSocket(Integer.parseInt(port));
new Thread(this).start();
messageOut("起動しました。");
} catch (Exception e) {
messageOut("起動できませんでした。");
}
} else {
messageOut("既に起動しています。");
}
}
private void shutdown() {
if (serverSocket != null && !serverSocket.isClosed()) {
try {
serverSocket.close();
messageOut("停止しました。");
} catch (Exception e) {}
setChanged();
notifyObservers();
clearChanged();
} else {
messageOut("起動していません。");
}
}
}
private class Session implements Observer, Runnable {
private Socket socket;
private Session(Socket socket) {
this.socket = socket;
}
public void update(Observable observable, Object object) {
try {
socket.close();
} catch (Exception e) {}
}
public void run() {
try {
InputStream inputStream = socket.getInputStream();
BufferedReader bufferedReader = new BufferedReader
(new InputStreamReader(inputStream, "Shift-JIS"));
OutputStream outputStream = socket.getOutputStream();
PrintWriter printWriter = new PrintWriter
(new OutputStreamWriter(outputStream, "Shift-JIS"), true);
String requestText = null;
while ((requestText = bufferedReader.readLine()) != null) {
messageOut(requestText);
printWriter.println("response from server:"
+ socket.getLocalPort());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
messageOut("通信終了(client:" + socket.getPort() + ")");
try {
socket.close();
} catch (Exception e) {}
}
}
}
}
ポート番号を指定し「起動」ボタンを押すとサーバが起動します。
起動後に「netstat -a」コマンドを実行してみると、指定したポートのState欄が「LISTENING」になっているのが確認できると思います。
また、通信の状況を確認するためにメッセージを画面に表示していきます。
ServerSocketはサーバソケットを表します。
ServerSocketのacceptメソッドはこのソケットに対する接続要求を待機し、それを受け取ります。
接続があった場合は、その応答処理は別のスレッドで行います。
応答処理中でも、別の接続要求に対応するためです。
acceptメソッドはSocketインスタンスを返します。
これはクライアントソケットを表します。
getInputStreamメソッドでそのソケットの入力ストリーム、getOutputStreamメソッドでそのソケットの出力ストリームを取得できます。
このストリームに対して読み書きを行うことで、通信を行うことができます。
とはいえ、サーバ側のプログラムだけでは通信はできないので、次回はクライアント側のプログラムを実装してみたいと思います。

