Java:完整的每个连接线程阻塞IO与NIO的代码示例?

时间:2009-08-01 00:02:16

标签: java multithreading sockets nio

好的,我在这里疯了。我一直在为我的服务器重写NIO代码,并遇到一些真正的麻烦。最重要的是,让NIO“正确”是非常困难的。有些人向我指出了http://rox-xmlrpc.sourceforge.net/niotut/的Rox教程,这个教程似乎走得很好,但并不像我想的那样完整。例如,我需要知道如何在发送排队的传出ByteBuffers之后关闭服务器端的连接。 SocketChannel.close()是突然的,如果提前完成,可能会丢失数据。我还需要发送大于读取ByteBuffer的大数据包。 Rox代码(我看过的任何其他代码)都没有解决这个问题。还有很多地方似乎未被捕获的异常未得到妥善处理。在我的测试中,由于NIO的复杂性,存在一些错误并且不清楚如何正确处理它们。

无论如何,当我试图解决这些问题时,会出现更多棘手的细微之处,并且它变得非常复杂。所以我正在考虑一种完全不同的方法。许多人都说NIO非常容易出错,并且不必要地混淆和复杂。他们提倡使用“每线程连接”模型,该模型使用阻塞IO,其中每个套接字连接在其自己的线程上运行。这似乎是一个好主意,并且通过为所有连接(如在NIO中)设置一个选择器线程来减少前端的瓶颈,代价是更高的开销(对于线程)。这种情绪与http://paultyma.blogspot.com/2008/03/writing-java-multithreaded-servers.htmlhttp://mailinator.blogspot.com/2008/02/kill-myth-please-nio-is-not-faster-than.html

等帖子相呼应

与NIO相比,代码应该很简单,但我真的想要一些示例代码来查看。我似乎找不到任何东西。问题是我不认为这种“每个连接的线程阻塞I / O”策略有一个更好的名称,我实际上可以获得良好的Google结果。任何人都可以链接我的一些教程或简单的例子来解释使用这种“老”的I / O方法并使用线程池扩展它?还是有其他任何智慧的话?非常感谢!

3 个答案:

答案 0 :(得分:1)

如果您正在使用NIO,我还建议您使用框架。我一直在与Apache Mina合作,我会推荐它。

对于阻塞IO,基本上你需要一个监听器线程来接受传入的连接并产生将处理每个连接的其他线程。下面是一个这样的Listener代码示例,最初是为Apache Felix项目提供的。 如果您查找完整但已修改的版本,可以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

您可以找到其他示例herehere

请注意,当您有更多负载时,NIO优于阻止模型的优势就会发挥作用。从某一点来看,线程创建,管理和上下文切换的额外工作量将限制您的系统性能。

答案 1 :(得分:0)

我建议你查看JDK中的sample / nio目录。这有一些简单的例子,包括你提到的两个例子。

答案 2 :(得分:0)

您可能还想考虑使用更高级别的框架,例如Grizzly,而不是直接使用NIO。该框架应该让您专注于您的用例,而不是NIO的微妙之处。