株式会社イーヴ

EeBlog(テクニカルブログ)

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

第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メソッドでそのソケットの出力ストリームを取得できます。
このストリームに対して読み書きを行うことで、通信を行うことができます。

とはいえ、サーバ側のプログラムだけでは通信はできないので、次回はクライアント側のプログラムを実装してみたいと思います。