Java가 NIO 패키지를 사용하여 Socket 통신을 구현하는 인스턴스 코드

13763 단어 javaniosocket
앞의 몇 편의 문장은java를 사용하는 것을 소개하였다.io와java.net 라이브러리에서 이루어진 Socket 통신, 다음은 자바를 사용하는 것을 소개합니다.nio 라이브러리에서 구현된 Socket
java.nio 패키지는 I/O 작업의 효율성을 높이기 위해 Java가 1.4 이후에 추가되었습니다.nio 패키지에는 다음과 같은 클래스 또는 인터페이스가 포함됩니다.
  • 버퍼: 버퍼, 입력이나 출력 데이터를 저장할 때 사용..
  • Charset: 유니코드 문자 인코딩과 다른 문자 인코딩을 서로 바꾸는 데 사용..
  • Channel: Buffer의 데이터를 데이터 원본에 기록하거나 데이터 원본의 데이터를 Buffer에 읽는 데이터 전송 채널..
  • Selector: 비동기식 입출력 작업을 지원하며 비차단 입출력 작업이라고도 합니다..
  • nio 패키지에서는 다음 두 가지 측면에서 입출력 작업 효율을 높입니다.
  • Buffer 및 Channel을 통한 I/O 작업 속도 향상..
  • Selector를 통해 비차단 I/O 작업을 지원합니다..
  • 다음은 프로그램에서 어떻게 이런 라이브러리를 통해 Socket 기능을 실현하는지 살펴보겠습니다.
    일단 보조 클래스 몇 개 소개해드릴게요.
    보조 클래스 Serializable Util, 이 클래스는 자바 대상을 바이트 그룹으로 서열화하거나 바이트 그룹을 자바 대상으로 반서열화하는 데 사용됩니다.
    
    package com.googlecode.garbagecan.test.socket; 
     
    import java.io.ByteArrayInputStream; 
    import java.io.ByteArrayOutputStream; 
    import java.io.IOException; 
    import java.io.ObjectInputStream; 
    import java.io.ObjectOutputStream; 
     
    public class SerializableUtil { 
       
      public static byte[] toBytes(Object object) { 
        ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
        ObjectOutputStream oos = null; 
        try { 
          oos = new ObjectOutputStream(baos); 
          oos.writeObject(object); 
          byte[] bytes = baos.toByteArray(); 
          return bytes; 
        } catch(IOException ex) { 
          throw new RuntimeException(ex.getMessage(), ex); 
        } finally { 
          try { 
            oos.close(); 
          } catch (Exception e) {} 
        } 
      } 
       
      public static Object toObject(byte[] bytes) { 
        ByteArrayInputStream bais = new ByteArrayInputStream(bytes); 
        ObjectInputStream ois = null; 
        try { 
          ois = new ObjectInputStream(bais); 
          Object object = ois.readObject(); 
          return object; 
        } catch(IOException ex) { 
          throw new RuntimeException(ex.getMessage(), ex); 
        } catch(ClassNotFoundException ex) { 
          throw new RuntimeException(ex.getMessage(), ex); 
        } finally { 
          try { 
            ois.close(); 
          } catch (Exception e) {} 
        } 
      } 
    } 
    
    보조 클래스 My Request Object와 My Response Object, 이 두 클래스는 일반적인java 대상으로 Serializable 인터페이스를 실현했다.MyRequestObject 클래스는 클라이언트가 보낸 요청이고 MyResponseObject는 서버 측에서 응답한 것입니다.
    
    package com.googlecode.garbagecan.test.socket.nio; 
     
    import java.io.Serializable; 
     
    public class MyRequestObject implements Serializable { 
     
      private static final long serialVersionUID = 1L; 
     
      private String name; 
       
      private String value; 
     
      private byte[] bytes; 
       
      public MyRequestObject(String name, String value) { 
        this.name = name; 
        this.value = value; 
        this.bytes = new byte[1024]; 
      } 
       
      public String getName() { 
        return name; 
      } 
     
      public void setName(String name) { 
        this.name = name; 
      } 
     
      public String getValue() { 
        return value; 
      } 
     
      public void setValue(String value) { 
        this.value = value; 
      } 
       
      @Override 
      public String toString() { 
        StringBuffer sb = new StringBuffer(); 
        sb.append("Request [name: " + name + ", value: " + value + ", bytes: " + bytes.length+ "]"); 
        return sb.toString(); 
      } 
    } 
     
    package com.googlecode.garbagecan.test.socket.nio; 
     
    import java.io.Serializable; 
     
    public class MyResponseObject implements Serializable { 
     
      private static final long serialVersionUID = 1L; 
     
      private String name; 
       
      private String value; 
     
      private byte[] bytes; 
       
      public MyResponseObject(String name, String value) { 
        this.name = name; 
        this.value = value; 
        this.bytes = new byte[1024]; 
      } 
       
      public String getName() { 
        return name; 
      } 
     
      public void setName(String name) { 
        this.name = name; 
      } 
     
      public String getValue() { 
        return value; 
      } 
     
      public void setValue(String value) { 
        this.value = value; 
      } 
       
      @Override 
      public String toString() { 
        StringBuffer sb = new StringBuffer(); 
        sb.append("Response [name: " + name + ", value: " + value + ", bytes: " + bytes.length+ "]"); 
        return sb.toString(); 
      } 
    } 
    
    
    다음은 주로 서버 측의 코드를 살펴보겠습니다. 그 중에서 일부 영문 주석은 코드를 이해하는 데 도움이 됩니다. 주석은 주로 jdk의 문서와 예입니다. 여기는 재번역이 없습니다.
    
    package com.googlecode.garbagecan.test.socket.nio; 
     
    import java.io.ByteArrayOutputStream; 
    import java.io.IOException; 
    import java.net.InetSocketAddress; 
    import java.nio.ByteBuffer; 
    import java.nio.channels.ClosedChannelException; 
    import java.nio.channels.SelectionKey; 
    import java.nio.channels.Selector; 
    import java.nio.channels.ServerSocketChannel; 
    import java.nio.channels.SocketChannel; 
    import java.util.Iterator; 
    import java.util.logging.Level; 
    import java.util.logging.Logger; 
     
    import com.googlecode.garbagecan.test.socket.SerializableUtil; 
     
    public class MyServer3 { 
     
      private final static Logger logger = Logger.getLogger(MyServer3.class.getName()); 
       
      public static void main(String[] args) { 
        Selector selector = null; 
        ServerSocketChannel serverSocketChannel = null; 
         
        try { 
          // Selector for incoming time requests 
          selector = Selector.open(); 
     
          // Create a new server socket and set to non blocking mode 
          serverSocketChannel = ServerSocketChannel.open(); 
          serverSocketChannel.configureBlocking(false); 
           
          // Bind the server socket to the local host and port 
          serverSocketChannel.socket().setReuseAddress(true); 
          serverSocketChannel.socket().bind(new InetSocketAddress(10000)); 
           
          // Register accepts on the server socket with the selector. This 
          // step tells the selector that the socket wants to be put on the 
          // ready list when accept operations occur, so allowing multiplexed 
          // non-blocking I/O to take place. 
          serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); 
       
          // Here's where everything happens. The select method will 
          // return when any operations registered above have occurred, the 
          // thread has been interrupted, etc. 
          while (selector.select() > 0) { 
            // Someone is ready for I/O, get the ready keys 
            Iterator<SelectionKey> it = selector.selectedKeys().iterator(); 
       
            // Walk through the ready keys collection and process date requests. 
            while (it.hasNext()) { 
              SelectionKey readyKey = it.next(); 
              it.remove(); 
               
              // The key indexes into the selector so you 
              // can retrieve the socket that's ready for I/O 
              execute((ServerSocketChannel) readyKey.channel()); 
            } 
          } 
        } catch (ClosedChannelException ex) { 
          logger.log(Level.SEVERE, null, ex); 
        } catch (IOException ex) { 
          logger.log(Level.SEVERE, null, ex); 
        } finally { 
          try { 
            selector.close(); 
          } catch(Exception ex) {} 
          try { 
            serverSocketChannel.close(); 
          } catch(Exception ex) {} 
        } 
      } 
     
      private static void execute(ServerSocketChannel serverSocketChannel) throws IOException { 
        SocketChannel socketChannel = null; 
        try { 
          socketChannel = serverSocketChannel.accept(); 
          MyRequestObject myRequestObject = receiveData(socketChannel); 
          logger.log(Level.INFO, myRequestObject.toString()); 
           
          MyResponseObject myResponseObject = new MyResponseObject( 
              "response for " + myRequestObject.getName(),  
              "response for " + myRequestObject.getValue()); 
          sendData(socketChannel, myResponseObject); 
          logger.log(Level.INFO, myResponseObject.toString()); 
        } finally { 
          try { 
            socketChannel.close(); 
          } catch(Exception ex) {} 
        } 
      } 
       
      private static MyRequestObject receiveData(SocketChannel socketChannel) throws IOException { 
        MyRequestObject myRequestObject = null; 
        ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
        ByteBuffer buffer = ByteBuffer.allocate(1024); 
         
        try { 
          byte[] bytes; 
          int size = 0; 
          while ((size = socketChannel.read(buffer)) >= 0) { 
            buffer.flip(); 
            bytes = new byte[size]; 
            buffer.get(bytes); 
            baos.write(bytes); 
            buffer.clear(); 
          } 
          bytes = baos.toByteArray(); 
          Object obj = SerializableUtil.toObject(bytes); 
          myRequestObject = (MyRequestObject)obj; 
        } finally { 
          try { 
            baos.close(); 
          } catch(Exception ex) {} 
        } 
        return myRequestObject; 
      } 
     
      private static void sendData(SocketChannel socketChannel, MyResponseObject myResponseObject) throws IOException { 
        byte[] bytes = SerializableUtil.toBytes(myResponseObject); 
        ByteBuffer buffer = ByteBuffer.wrap(bytes); 
        socketChannel.write(buffer); 
      } 
    } 
    
    다음은 클라이언트의 코드입니다. 코드가 비교적 간단합니다. 100개의 라인을 시작하여 서버에 접근하는 것입니다.
    
    package com.googlecode.garbagecan.test.socket.nio; 
     
    import java.io.ByteArrayOutputStream; 
    import java.io.IOException; 
    import java.net.InetSocketAddress; 
    import java.net.SocketAddress; 
    import java.nio.ByteBuffer; 
    import java.nio.channels.SocketChannel; 
    import java.util.logging.Level; 
    import java.util.logging.Logger; 
     
    import com.googlecode.garbagecan.test.socket.SerializableUtil; 
     
    public class MyClient3 { 
     
      private final static Logger logger = Logger.getLogger(MyClient3.class.getName()); 
       
      public static void main(String[] args) throws Exception { 
        for (int i = 0; i < 100; i++) { 
          final int idx = i; 
          new Thread(new MyRunnable(idx)).start(); 
        } 
      } 
       
      private static final class MyRunnable implements Runnable { 
         
        private final int idx; 
     
        private MyRunnable(int idx) { 
          this.idx = idx; 
        } 
     
        public void run() { 
          SocketChannel socketChannel = null; 
          try { 
            socketChannel = SocketChannel.open(); 
            SocketAddress socketAddress = new InetSocketAddress("localhost", 10000); 
            socketChannel.connect(socketAddress); 
     
            MyRequestObject myRequestObject = new MyRequestObject("request_" + idx, "request_" + idx); 
            logger.log(Level.INFO, myRequestObject.toString()); 
            sendData(socketChannel, myRequestObject); 
             
            MyResponseObject myResponseObject = receiveData(socketChannel); 
            logger.log(Level.INFO, myResponseObject.toString()); 
          } catch (Exception ex) { 
            logger.log(Level.SEVERE, null, ex); 
          } finally { 
            try { 
              socketChannel.close(); 
            } catch(Exception ex) {} 
          } 
        } 
     
        private void sendData(SocketChannel socketChannel, MyRequestObject myRequestObject) throws IOException { 
          byte[] bytes = SerializableUtil.toBytes(myRequestObject); 
          ByteBuffer buffer = ByteBuffer.wrap(bytes); 
          socketChannel.write(buffer); 
          socketChannel.socket().shutdownOutput(); 
        } 
     
        private MyResponseObject receiveData(SocketChannel socketChannel) throws IOException { 
          MyResponseObject myResponseObject = null; 
          ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
           
          try { 
            ByteBuffer buffer = ByteBuffer.allocateDirect(1024); 
            byte[] bytes; 
            int count = 0; 
            while ((count = socketChannel.read(buffer)) >= 0) { 
              buffer.flip(); 
              bytes = new byte[count]; 
              buffer.get(bytes); 
              baos.write(bytes); 
              buffer.clear(); 
            } 
            bytes = baos.toByteArray(); 
            Object obj = SerializableUtil.toObject(bytes); 
            myResponseObject = (MyResponseObject) obj; 
            socketChannel.socket().shutdownInput(); 
          } finally { 
            try { 
              baos.close(); 
            } catch(Exception ex) {} 
          } 
          return myResponseObject; 
        } 
      } 
    } 
    
    마지막으로 위의 코드를 테스트합니다. 먼저 서버 클래스를 실행하고 클라이언트 클래스를 실행하면 서버 사이드와 클라이언트 사이드 컨트롤러에서 보내거나 받은 My Request Object 또는 My Response Object 대상을 볼 수 있습니다.
    이상은 본문의 전체 내용입니다. 여러분의 학습에 도움이 되고 저희를 많이 응원해 주십시오.

    좋은 웹페이지 즐겨찾기