第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メソッドでそのソケットの出力ストリームを取得できます。
このストリームに対して読み書きを行うことで、通信を行うことができます。
とはいえ、サーバ側のプログラムだけでは通信はできないので、次回はクライアント側のプログラムを実装してみたいと思います。