Dict.CN 在線詞典, 英語學習, 在線翻譯

          都市淘沙者

          荔枝FM Everyone can be host

          統計

          留言簿(23)

          積分與排名

          優秀學習網站

          友情連接

          閱讀排行榜

          評論排行榜

          socket, nio socket 及nio socket框架MINA總結 (轉)

          http://blog.csdn.net/lcllcl987/archive/2007/04/16/1566114.aspx
          nio學習:
          最近花了點時間研究了一下nio,及其開源框架MINA,現把心得總結如下:
          1:傳統socket:阻塞式通信
          每建立一個Socket連接時,同時創建一個新線程對該Socket進行單獨通信(采用阻塞的方式通信)。
          這種方式具有很高的響應速度,并且控制起來也很簡單,在連接數較少的時候非常有效,但是如果
          對每一個連接都產生一個線程的無疑是對系統資源的一種浪費,如果連接數較多將會出現資源不足的情況
          example:
          server code:

          public class MultiUserServer extends Thread {
           private Socket client;
           
           public MultiUserServer(Socket c) {
            this.client = c;
           }

           public void run() {
            try {
             BufferedReader in = new BufferedReader(new InputStreamReader(client
               .getInputStream()));
             PrintWriter out = new PrintWriter(client.getOutputStream());
             // Mutil User but can't parallel
             while (true) {
              String str = in.readLine();
              System.out.println(str);
              SocketLog.debug("receive message: " + str);
              out.println("has receive....");
              out.flush();
              if (str.equals("end"))
               break;
             }
             client.close();
            } catch (IOException ex) {
            }
           }

           public static void main(String[] args) throws IOException {
            int port = 5678;
            if (args.length > 0)
             port = Integer.parseInt(args[0]);
            ServerSocket server = new ServerSocket(port);
            SocketLog.debug("the server socket application is created!");
            while (true) {
             // transfer location change Single User or Multi User
             MultiUserServer mu = new MultiUserServer(server.accept());
             mu.start();
            }
           }
          }

          client code:

          public class Client {

           static Socket server;

           public static void main(String[] args) throws Exception {
            
            //set socket proxy.
            String proxyHost = "192.168.254.212";
            String proxyPort = "1080";
            System.getProperties().put("socksProxySet","true");
            System.getProperties().put("socksProxyHost",proxyHost);
            System.getProperties().put("socksProxyPort",proxyPort);
            
            String host = "132.201.69.80";
            int port = 13086;
            if (args.length > 1)
            {
             host = args[0];
             port = Integer.parseInt(args[1]);
            }
            System.out.println("connetioning:" + host + ":" + port);
            server = new Socket(host, port);
            BufferedReader in = new BufferedReader(new InputStreamReader(server
              .getInputStream()));
            PrintWriter out = new PrintWriter(server.getOutputStream());
            BufferedReader wt = new BufferedReader(new InputStreamReader(System.in));
            while (true) {
             String str = wt.readLine();
             out.println(str);
             out.flush();
             if (str.equals("end")) {
              break;
             }
             System.out.println(in.readLine());
            }
            server.close();
           }
          }

          2.nio:非阻塞通訊模式
          2.1NIO 設計背后的基石:反應器模式,用于事件多路分離和分派的體系結構模式。
          反應器模式的核心功能如下:
          將事件多路分用
          將事件分派到各自相應的事件處理程序

          NIO 的非阻塞 I/O 機制是圍繞 選擇器和 通道構建的。 Channel 類表示服務器和客戶機之間的
          一種通信機制。Selector 類是 Channel 的多路復用器。 Selector 類將傳入客戶機請求多路分
          用并將它們分派到各自的請求處理程序。
          通道(Channel 類):表示服務器和客戶機之間的一種通信機制。
          選擇器(Selector類):是 Channel 的多路復用器。Selector 類將傳入的客戶機請求多路分用并將它們
          分派到各自的請求處理程序。

          簡單的來說:

          NIO是一個基于事件的IO架構,最基本的思想就是:有事件我通知你,你再去做你的事情.
          而且NIO的主線程只有一個,不像傳統的模型,需要多個線程以應對客戶端請求,也減輕
          了JVM的工作量。
          當Channel注冊至Selector以后,經典的調用方法如下:

                  while (somecondition) {
                      int n = selector.select(TIMEOUT);
                      if (n == 0)
                          continue;
                      for (Iterator iter = selector.selectedKeys().iterator(); iter
                              .hasNext();) {
                          if (key.isAcceptable())
                              doAcceptable(key);
                          if (key.isConnectable())
                              doConnectable(key);
                          if (key.isValid() && key.isReadable())
                              doReadable(key);
                          if (key.isValid() && key.isWritable())
                              doWritable(key);
                          iter.remove();
                      }
                  }
          nio中取得事件通知,就是在selector的select事件中完成的。在selector事件時有一個線程
          向操作系統詢問,selector中注冊的Channel&&SelectionKey的鍵值對的各種事件是否有發生,
          如果有則添加到selector的selectedKeys屬性Set中去,并返回本次有多少個感興趣的事情發生。
          如果發現這個值>0,表示有事件發生,馬上迭代selectedKeys中的SelectionKey,
          根據Key中的表示的事件,來做相應的處理。
          實際上,這段說明表明了異步socket的核心,即異步socket不過是將多個socket的調度(或者還有他們的線程調度)
          全部交給操作系統自己去完成,異步的核心Selector,不過是將這些調度收集、分發而已。
          2.2 nio example:
          server code:


          public class NonBlockingServer
          {
              public Selector sel = null;
              public ServerSocketChannel server = null;
              public SocketChannel socket = null;
              public int port = 4900;
              String result = null;


              public NonBlockingServer()
              {
            System.out.println("Inside default ctor");
              }
             
           public NonBlockingServer(int port)
              {
            System.out.println("Inside the other ctor");
            this.port = port;
              }

              public void initializeOperations() throws IOException,UnknownHostException
              {
            System.out.println("Inside initialization");
            sel = Selector.open();
            server = ServerSocketChannel.open();
            server.configureBlocking(false);
            InetAddress ia = InetAddress.getLocalHost();
            InetSocketAddress isa = new InetSocketAddress(ia,port);
            server.socket().bind(isa);
              }
             
           public void startServer() throws IOException
              {
            System.out.println("Inside startserver");
                  initializeOperations();
            System.out.println("Abt to block on select()");
            SelectionKey acceptKey = server.register(sel, SelectionKey.OP_ACCEPT ); 
           
            while (acceptKey.selector().select() > 0 )
            { 
              
             Set readyKeys = sel.selectedKeys();
             Iterator it = readyKeys.iterator();

             while (it.hasNext()) {
              SelectionKey key = (SelectionKey)it.next();
              it.remove();
                         
              if (key.isAcceptable()) {
               System.out.println("Key is Acceptable");
               ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
               socket = (SocketChannel) ssc.accept();
               socket.configureBlocking(false);
               SelectionKey another = socket.register(sel,SelectionKey.OP_READ|SelectionKey.OP_WRITE);
              }
              if (key.isReadable()) {
               System.out.println("Key is readable");
               String ret = readMessage(key);
               if (ret.length() > 0) {
                writeMessage(socket,ret);
               }
              }
              if (key.isWritable()) {
               System.out.println("THe key is writable");
               String ret = readMessage(key);
               socket = (SocketChannel)key.channel();
               if (result.length() > 0 ) {
                writeMessage(socket,ret);
               }
              }
             }
            }
              }

              public void writeMessage(SocketChannel socket,String ret)
              {
            System.out.println("Inside the loop");

            if (ret.equals("quit") || ret.equals("shutdown")) {
             return;
            }
            try
            {

             String s = "This is context from server!-----------------------------------------";
             Charset set = Charset.forName("us-ascii");
             CharsetDecoder dec = set.newDecoder();
             CharBuffer charBuf = dec.decode(ByteBuffer.wrap(s.getBytes()));
             System.out.println(charBuf.toString());
             int nBytes = socket.write(ByteBuffer.wrap((charBuf.toString()).getBytes()));
             System.out.println("nBytes = "+nBytes);
              result = null;
            }
            catch(Exception e)
            {
             e.printStackTrace();
            }

              }
           
              public String readMessage(SelectionKey key)
              {
            int nBytes = 0;
            socket = (SocketChannel)key.channel();
                  ByteBuffer buf = ByteBuffer.allocate(1024);
            try
            {
                      nBytes = socket.read(buf);
             buf.flip();
             Charset charset = Charset.forName("us-ascii");
             CharsetDecoder decoder = charset.newDecoder();
             CharBuffer charBuffer = decoder.decode(buf);
             result = charBuffer.toString();
              
                  }
            catch(IOException e)
            {
             e.printStackTrace();
            }
            return result;
              }

              public static void main(String args[])
              {
               NonBlockingServer nb;
               if (args.length < 1)
               {
                nb = new NonBlockingServer();
               }
               else
               {
                int port = Integer.parseInt(args[0]);
                nb = new NonBlockingServer(port);
               }
            
            try
            {
             nb.startServer();
             System.out.println("the nonBlocking server is started!");
            }
            catch (IOException e)
            {
             e.printStackTrace();
             System.exit(-1);
            }
            
           }
          }

          client code:

          public class Client {
           public SocketChannel client = null;

           public InetSocketAddress isa = null;

           public RecvThread rt = null;

           private String host;

           private int port;

           public Client(String host, int port) {
            this.host = host;
            this.port = port;
           }

           public void makeConnection() {
            String proxyHost = "192.168.254.212";
            String proxyPort = "1080";
            System.getProperties().put("socksProxySet", "true");
            System.getProperties().put("socksProxyHost", proxyHost);
            System.getProperties().put("socksProxyPort", proxyPort);

            int result = 0;
            try {
             client = SocketChannel.open();
             isa = new InetSocketAddress(host, port);
             client.connect(isa);
             client.configureBlocking(false);
             receiveMessage();
            } catch (UnknownHostException e) {
             e.printStackTrace();
            } catch (IOException e) {
             e.printStackTrace();
            }
            long begin = System.currentTimeMillis();

            sendMessage();

            long end = System.currentTimeMillis();
            long userTime = end - begin;
            System.out.println("use tiem: " + userTime);
            try {
             interruptThread();
             client.close();
             System.exit(0);
            } catch (IOException e) {
             e.printStackTrace();
            }
           }

           public int sendMessage() {
              System.out.println("Inside SendMessage");
            String msg = null;
            ByteBuffer bytebuf;
            int nBytes = 0;
            try {
             msg = "It's message from client!";
             System.out.println("msg is "+msg);
             bytebuf = ByteBuffer.wrap(msg.getBytes());
             for (int i = 0; i < 1000; i++) {
              nBytes = client.write(bytebuf);
              System.out.println(i + " finished");
             }
             interruptThread();
             try {
              Thread.sleep(5000);
             } catch (Exception e) {
              e.printStackTrace();
             }
             client.close();
             return -1;

            } catch (IOException e) {
             e.printStackTrace();
            }

            return nBytes;

           }

           public void receiveMessage() {
            rt = new RecvThread("Receive THread", client);
            rt.start();

           }

           public void interruptThread() {
            rt.val = false;
           }

           public static void main(String args[]) {
            if (args.length < 2) {
             System.err.println("You should put 2 args: host,port");
            } else {
             String host = args[0];
             int port = Integer.parseInt(args[1]);
             Client cl = new Client(host, port);
             cl.makeConnection();
            }
            BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
            String msg;

           }

           public class RecvThread extends Thread {
            public SocketChannel sc = null;

            public boolean val = true;

            public RecvThread(String str, SocketChannel client) {
             super(str);
             sc = client;
            }

            public void run() {
             int nBytes = 0;
             ByteBuffer buf = ByteBuffer.allocate(2048);
             try {
              while (val) {
               while ((nBytes = nBytes = client.read(buf)) > 0) {
                buf.flip();
                Charset charset = Charset.forName("us-ascii");
                CharsetDecoder decoder = charset.newDecoder();
                CharBuffer charBuffer = decoder.decode(buf);
                String result = charBuffer.toString();
                System.out.println("the server return: " + result);
                buf.flip();

               }
              }

             } catch (IOException e) {
              e.printStackTrace();

             }

            }
           }
          }
          3:Socket網絡框架 MINA
          MINA是一個網絡應用框架,在不犧牲性能和可擴展性的前提下用于解決如下問題:
          1:快速開發自己的英勇。
          2:高可維護性,高可復用性:網絡I/O編碼,消息的編/解碼,業務邏輯互相分離。
          3:相對容易的進行單元測試。

           

          3.1 IoFilters:
          IoFilter為MINA的功能擴展提供了接口。它攔截所有的IO事件進行事件的預處理和后處理(AOP)。我們可以把它想象成
          Servlet的filters。
          IoFilter能夠實現以下幾種目的:
          事件日志
          性能檢測
          數據轉換(e.g. SSL support),codec
          防火墻…等等

          3.2 codec: ProtocolCodecFactory
          MINA提供了方便的Protocol支持。如上說講,codec在IoFilters中設置。
          通過它的Encoder和Decoder,可以方便的擴展并支持各種基于Socket的網絡協議,比如HTTP服務器、FTP服務器、Telnet服務器等等。

          要實現自己的編碼/解碼器(codec)只需要實現interface: ProtocolCodecFactory即可.
          在MINA 1.0版本,MINA已經實現了幾個常用的(codec factory):

          DemuxingProtocolCodecFactory,
          NettyCodecFactory,
          ObjectSerializationCodecFactory,
          TextLineCodecFactory
           
          其中:
          TextLineCodecFactory:
           A ProtocolCodecFactory that performs encoding and decoding between a text line data and a Java
           string object. This codec is useful especially when you work with a text-based protocols such as SMTP and IMAP.

          ObjectSerializationCodecFactory:
          A ProtocolCodecFactory that serializes and deserializes Java objects. This codec is very useful when
          you have to prototype your application rapidly without any specific codec.

          DemuxingProtocolCodecFactory:
          A composite ProtocolCodecFactory that consists of multiple MessageEncoders and MessageDecoders. ProtocolEncoder
          and ProtocolDecoder this factory returns demultiplex incoming messages and buffers to appropriate MessageEncoders
          and MessageDecoders.

          NettyCodecFactory:
          A MINA ProtocolCodecFactory that provides encoder and decoder for Netty2 Messages and MessageRecognizers.

          3.3 business logic: IoHandler

          MINA中,所有的業務邏輯都有實現了IoHandler的class完成
          interfaceHandles:
           all protocol events fired by MINA. There are 6 event handler methods, and they are all invoked by MINA automatically.
           當事件發生時,將觸發IoHandler中的方法:
           sessionCreated, sessionOpened, sessionClosed, sessionIdle, exceptionCaught, messageReceived, messageSent
          MINA 1.O中,IoHandler的實現類:
          ChainedIoHandler, DemuxingIoHandler, IoHandlerAdapter, SingleSessionIoHandlerDelegate, StreamIoHandler
          具體細節可參考javadoc。

          3.4   MINA的高級主題:線程模式
          MINA通過它靈活的filter機制來提供多種線程模型。
          沒有線程池過濾器被使用時MINA運行在一個單線程模式。
          如果添加了一個IoThreadPoolFilter到IoAcceptor,將得到一個leader-follower模式的線程池。
          如果再添加一個ProtocolThreadPoolFilter,server將有兩個線程池;
          一個(IoThreadPoolFilter)被用于對message對象進行轉換,另外一個(ProtocolThreadPoolFilter)被用于處理業務邏輯。
          SimpleServiceRegistry加上IoThreadPoolFilter和ProtocolThreadPoolFilter的缺省實現即可適用于需
          要高伸縮性的應用。如果想使用自己的線程模型,請參考SimpleServiceRegistry的源代碼,并且自己

          初始化Acceptor。

          IoThreadPoolFilter threadPool = new IoThreadPoolFilter();threadPool.start();
          IoAcceptor acceptor = new SocketAcceptor();
          acceptor.getFilterChain().addLast( "threadPool", threadPool);
          ProtocolThreadPoolFilter threadPool2 = new ProtocolThreadPoolFilter();
          threadPool2.start();
          ProtocolAcceptor acceptor2 = new IoProtocolAcceptor( acceptor );
          acceptor2.getFilterChain().addLast( "threadPool", threadPool2 );
          ...
          threadPool2.stop();
          threadPool.stop();


          采用MINA進行socket開發,一般步驟如下:
          1:
          server:
          IoAcceptor acceptor = new SocketAcceptor(); //建立client接收器
          or client:
          SocketConnector connector = new SocketConnector();  //建立一個連接器
          2:server的屬性配置:
                  SocketAcceptorConfig cfg = new SocketAcceptorConfig();
                  cfg.setReuseAddress(true);
                  cfg.getFilterChain().addLast(
                              "codec",
                              new ProtocolCodecFilter( new ObjectSerializationCodecFactory() ) ); //對象序列化 codec factory
                  cfg.getFilterChain().addLast( "logger", new LoggingFilter() );
          3:綁定address和business logic
          server:
                  acceptor.bind(
                          new InetSocketAddress( SERVER_PORT ),
                          new ServerSessionHandler( ), cfg ); // 綁定address和handler

          client:
                  connector.connect(new InetSocketAddress( HOSTNAME, PORT ),
                                  new ClientSessionHandler(msg), cfg );

          下面的這個簡單的example演示client和server傳遞object的過程:
          Message.java
          public class Message implements Serializable {

              private int type;
              private int status;
              private String msgBody;
             
              public Message(int type, int status, String msgBody)
              {
                  this.type = type;
                  this.status = status;
                  this.msgBody = msgBody;
              }

              public String getMsgBody() {
                  return msgBody;
              }

              public void setMsgBody(String msgBody) {
                  this.msgBody = msgBody;
              }

              public int getStatus() {
                  return status;
              }

              public void setStatus(int status) {
                  this.status = status;
              }

              public int getType() {
                  return type;
              }

              public void setType(int type) {
                  this.type = type;
              }
          }

          Client.java
          public class Client
          {
              private static final String HOSTNAME = "localhost";
              private static final int PORT = 8080;
              private static final int CONNECT_TIMEOUT = 30; // seconds


              public static void main( String[] args ) throws Throwable
              {
                  SocketConnector connector = new SocketConnector();       
                  // Configure the service.
                  SocketConnectorConfig cfg = new SocketConnectorConfig();
                  cfg.setConnectTimeout( CONNECT_TIMEOUT );
                    cfg.getFilterChain().addLast(
                              "codec",
                              new ProtocolCodecFilter( new ObjectSerializationCodecFactory() ) );

                  cfg.getFilterChain().addLast( "logger", new LoggingFilter() );
                 
                  IoSession session;
                  Message msg = new Message(0,1,"hello");
                  connector.connect(new InetSocketAddress( HOSTNAME, PORT ),
                                  new ClientSessionHandler(msg), cfg );

              }
          }

          ClientSessionHandler.java
          public class ClientSessionHandler extends IoHandlerAdapter
          {
              private Object msg;
             
              public ClientSessionHandler(Object msg)
              {
                  this.msg = msg;
              }


              public void sessionOpened( IoSession session )
              {
                  session.write(this.msg);
              }

              public void messageReceived( IoSession session, Object message )
              {
                  System.out.println("in messageReceived!");
                  Message rm = (Message ) message;       
                  SessionLog.debug(session, rm.getMsgBody());
                  System.out.println("message is: " + rm.getMsgBody());
                  session.write(rm);
              }

              public void exceptionCaught( IoSession session, Throwable cause )
              {
                  session.close();
              }
          }

          Server.java
          public class Server
          {
              private static final int SERVER_PORT = 8080;

              public static void main( String[] args ) throws Throwable
              {
                  IoAcceptor acceptor = new SocketAcceptor();
                 
                  // Prepare the service configuration.
                  SocketAcceptorConfig cfg = new SocketAcceptorConfig();
                  cfg.setReuseAddress( true );

                  cfg.getFilterChain().addLast(
                              "codec",
                              new ProtocolCodecFilter( new ObjectSerializationCodecFactory() ) );
                  cfg.getFilterChain().addLast( "logger", new LoggingFilter() );

                  acceptor.bind(
                          new InetSocketAddress( SERVER_PORT ),
                          new ServerSessionHandler( ), cfg );

                  System.out.println( "The server Listening on port " + SERVER_PORT );
              }
          }

          ServerSessionHandler.java
          public class ServerSessionHandler extends IoHandlerAdapter
          {
              public void sessionOpened( IoSession session )
              {
                  // set idle time to 60 seconds
                  session.setIdleTime( IdleStatus.BOTH_IDLE, 60 );
                  session.setAttribute("times",new Integer(0));
              }

              public void messageReceived( IoSession session, Object message )
              {
                  System.out.println("in messageReceived");
                  int times = ((Integer)(session.getAttribute("times"))).intValue();
                  System.out.println("tiems = " + times);
                  // communicate 30 times,then close the session.
                  if (times < 30)
                  {
                      times++;
                      session.setAttribute("times", new Integer(times));          
                   Message msg;
                   msg = (Message) message;
                   msg.setMsgBody("in server side: " + msg.getMsgBody()); 
                   System.out.println("begin send msg: " + msg.getMsgBody());
                   session.write(msg);
                  }
                  else
                  {
                      session.close();
                  }
              }

              public void sessionIdle( IoSession session, IdleStatus status )
              {
                  SessionLog.info( session, "Disconnecting the idle." );
                  // disconnect an idle client
                  session.close();
              }

              public void exceptionCaught( IoSession session, Throwable cause )
              {
                  // close the connection on exceptional situation
                  session.close();
              }
          }
          MINA自己附帶的Demo已經很好的說明了它的運用。
          值得一提的是它的SumUp:客戶端發送幾個數字,服務端求和后并返回結果。這個簡單的程序演示了如何自己實現CODEC。

          補充提示:
          下載并運行MINA的demo程序還頗非周折:
          運行MINA demo appli擦tion:
          1:在JDK5
          產生錯誤:
          Exception in thread "main" java.lang.NoClassDefFoundError: edu/emory/mathcs/backport/java/util/concurrent/Executor
           at org.apache.mina.example.reverser.Main.main(Main.java:44)
           
          察看mina的QA email:
          http://www.mail-archive.com/mina-dev@directory.apache.org/msg02252.html

          原來需要下載:backport-util-concurrent.jar并加入classpath
          http://dcl.mathcs.emory.edu/util/backport-util-concurrent/

          繼續運行還是報錯:
          Exception in thread "main" java.lang.NoClassDefFoundError: org/slf4j/LoggerFactory

          原來MINA采用了slf4j項目作為log,繼續下載
          slf4j-simple.jar等,并加入classpath:
          http://www.slf4j.org/download.html

          posted on 2007-06-06 14:31 都市淘沙者 閱讀(17148) 評論(1)  編輯  收藏 所屬分類: Android/J2ME/Symbian/Jabber

          評論

          # re: socket, nio socket 及nio socket框架MINA總結 (轉) 2012-06-20 14:42 sunjunliang52

          gras a  回復  更多評論   

          主站蜘蛛池模板: 丰都县| 新竹市| 池州市| 濮阳县| 台州市| 峨眉山市| 梁山县| 蒲江县| 秦皇岛市| 繁峙县| 霍山县| 青冈县| 浑源县| 洛浦县| 景东| 大理市| 石棉县| 宁德市| 榆树市| 青海省| 葫芦岛市| 新源县| 商洛市| 儋州市| 金塔县| 格尔木市| 长顺县| 漳浦县| 云阳县| 清河县| 吉水县| 武功县| 普兰店市| 泸西县| 琼中| 襄垣县| 遂昌县| 武强县| 通州市| 阿拉善右旗| 北宁市|