EeBlog(テクニカルブログ)

第109回 ソケットチャンネルの入出力

今回のテーマは「ソケットチャンネルの入出力」です。

以前、ストリームの入出力によりソケット通信を行うことについて書きましたが、チャンネルの入出力によりソケット通信を行うこともできます。

次のサンプルコードはサーバを起動し、クライアントの接続要求に応答するプログラムです。

import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import java.net.InetSocketAddress;

import java.nio.ByteBuffer;

import java.nio.channels.ServerSocketChannel;

import java.nio.channels.SocketChannel;

import java.nio.charset.Charset;

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 implements Runnable {


         private final ExecutorService executorService 
                                  = Executors.newCachedThreadPool();
         private ServerSocketChannel serverSocketChannel;


         public void run() {
             while (serverSocketChannel.isOpen()) {
                 try {
                     SocketChannel socketChannel = serverSocketChannel.accept();
                     executorService.execute(new Response(socketChannel));
                 } catch (Exception e) {
                     e.printStackTrace();
                 }
             }
         }


         private void startup(String port) {
             if (serverSocketChannel == null || !serverSocketChannel.isOpen()) {
                 try {
                     serverSocketChannel = ServerSocketChannel.open();
                     serverSocketChannel.socket().bind(new InetSocketAddress
                                                      (Integer.parseInt(port)));
                     new Thread(this).start();
                     messageOut("起動しました。");
                 } catch (Exception e) {
                     try {
                         serverSocketChannel.close();
                     } catch (Exception e2) {}
                     messageOut("起動できませんでした。");
                 }
             } else {
                 messageOut("起動しています。");
             }
         }


         private void shutdown() {
             if (serverSocketChannel != null
                                     && serverSocketChannel.isOpen()) {
                 try {
                     serverSocketChannel.close();
                     messageOut("停止しました。");
                 } catch (Exception e) {}
             } else {
                 messageOut("起動していません。");
             }
         }
     }


     private class Response implements Runnable {


         private SocketChannel socketChannel;


         private Response(SocketChannel socketChannel) {
             this.socketChannel = socketChannel;
         }


         public void run() {
             try {
                 ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                 Charset charset = Charset.forName("Shift-JIS");
                 socketChannel.read(byteBuffer);
                 byteBuffer.flip();
                 messageOut(charset.decode(byteBuffer));
                 socketChannel.write(charset.encode("response from server"));
             } catch (Exception e) {
                 e.printStackTrace();
             } finally {
                 try {
                     socketChannel.close();
                 } catch (Exception e) {}
             }
         }
     }
 }