Java: Complete code examples of thread-per-connection blocking IO versus NIO?

前端 未结 3 1473
长情又很酷
长情又很酷 2021-01-03 09:21

Ok, I\'m going crazy here. I have been rewriting the NIO code for my server, and running into some real headaches. The bottom line is that getting NIO \"right\" is very ha

相关标签:
3条回答
  • 2021-01-03 09:57

    If you are working with NIO, I would also suggest to use a Framework. I have been working with Apache Mina and I would recommend it.

    As to the blocking IO, essentially you will need a Listener Thread that accepts incoming connections and spawns additional threads that will handle each connection. Here is an example of such a Listener code, as originally contributed to the Apache Felix Project. If you look for the complete but modified version, you can browse the source here.

    e.g.

        /*
        * Licensed to the Apache Software Foundation (ASF) under one or more
        * contributor license agreements.  See the NOTICE file distributed with
        * this work for additional information regarding copyright ownership.
        * The ASF licenses this file to You under the Apache License, Version 2.0
        * (the "License"); you may not use this file except in compliance with
        * the License.  You may obtain a copy of the License at
        *
        *      http://www.apache.org/licenses/LICENSE-2.0
        *
            * Unless required by applicable law or agreed to in writing, software
        * distributed under the License is distributed on an "AS IS" BASIS,
        * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        * See the License for the specific language governing permissions and
        * limitations under the License.
        */
        package org.apache.felix.shell.remote;
        
        
        import java.io.IOException;
        import java.io.PrintStream;
        import java.net.ServerSocket;
        import java.net.Socket;
        import java.net.SocketException;
        
        
        /**
         * Implements a simple listener that will accept a single connection.
         * <p/>
         *
         * @author Dieter Wimberger (wimpi)
         */
        class Listener
        {
        
            private int m_Port;
            private Thread m_ListenerThread;
            private boolean m_Stop = false;
            private ServerSocket m_ServerSocket;
            private AtomicInteger m_UseCounter;
            private int m_MaxConnections;
        
        
            /**
             * Activates this listener on a listener thread (telnetconsole.Listener).
             */
            public void activate()
            {
                //configure from system property
                try
                {
                    m_Port = Integer.parseInt( System.getProperty( "osgi.shell.telnet.port", "6666" ) );
                }
                catch ( NumberFormatException ex )
                {
                    Activator.getServices().error( "Listener::activate()", ex );
                }
                try
                {
                    m_MaxConnections = Integer.parseInt( System.getProperty( "osgi.shell.telnet.maxconn", "2" ) );
                }
                catch ( NumberFormatException ex )
                {
                    Activator.getServices().error( "Listener::activate()", ex );
                }
                m_UseCounter = new AtomicInteger( 0 );
                m_ListenerThread = new Thread( new Acceptor(), "telnetconsole.Listener" );
                m_ListenerThread.start();
            }//activate
        
        
            /**
             * Deactivates this listener.
             * <p/>
             * The listener's socket will be closed, which should cause an interrupt in the
             * listener thread and allow for it to return. The calling thread joins the listener
             * thread until it returns (to ensure a clean stop).
             */
            public void deactivate()
            {
                try
                {
                    m_Stop = true;
                    //wait for the listener thread
                    m_ServerSocket.close();
                    m_ListenerThread.join();
                }
                catch ( Exception ex )
                {
                    Activator.getServices().error( "Listener::deactivate()", ex );
                }
            }//deactivate
        
            /**
             * Class that implements the listener's accept logic as a <tt>Runnable</tt>.
             */
            private class Acceptor implements Runnable
            {
        
                /**
                 * Listens constantly to a server socket and handles incoming connections.
                 * One connection will be accepted and routed into the shell, all others will
                 * be notified and closed.
                 * <p/>
                 * The mechanism that should allow the thread to unblock from the ServerSocket.accept() call
                 * is currently closing the ServerSocket from another thread. When the stop flag is set,
                 * this should cause the thread to return and stop.
                 */
                public void run()
                {
                    try
                    {
                        /*
                            A server socket is opened with a connectivity queue of a size specified
                            in int floodProtection.  Concurrent login handling under normal circumstances
                            should be handled properly, but denial of service attacks via massive parallel
                            program logins should be prevented with this.
                        */
                        m_ServerSocket = new ServerSocket( m_Port, 1 );
                        do
                        {
                            try
                            {
                                Socket s = m_ServerSocket.accept();
                                if ( m_UseCounter.get() >= m_MaxConnections )
                                {
                                    //reject with message
                                    PrintStream out = new PrintStream( s.getOutputStream() );
                                    out.print( INUSE_MESSAGE );
                                    out.flush();
                                    //close
                                    out.close();
                                    s.close();
                                }
                                else
                                {
                                    m_UseCounter.increment();
                                    //run on the connection thread
                                    Thread connectionThread = new Thread( new Shell( s, m_UseCounter ) );
                                    connectionThread.start();
                                }
                            }
                            catch ( SocketException ex )
                            {
                            }
                        }
                        while ( !m_Stop );
        
                    }
                    catch ( IOException e )
                    {
                        Activator.getServices().error( "Listener.Acceptor::activate()", e );
                    }
                }//run
        
            }//inner class Acceptor
        
            private static final String INUSE_MESSAGE = "Connection refused.\r\n"
                + "All possible connections are currently being used.\r\n";
        
        }//class Listener
    

    You can find other examples here and here.

    Note that the advantage of NIO over the blocking model comes into play when you have more load. From a certain point on, the amount of extra work for Thread creation, management and context switching will limit your system performance.

    0 讨论(0)
  • 2021-01-03 10:14

    You may also want to consider using a higher level framework such as Grizzly instead of using NIO directly. The framework should allow you to concentrate on your use case rather than the subtleties of NIO.

    0 讨论(0)
  • 2021-01-03 10:21

    I suggest you look in the sample/nio directory in your JDK. This has a number of simple examples including the two you mention.

    0 讨论(0)
提交回复
热议问题