⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 nnrpd.java

📁 java高级使用教程 全书一共分六章
💻 JAVA
📖 第 1 页 / 共 3 页
字号:
// Nnrpd - multi-threaded proxying NNRP daemon
//
// Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>.  All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
//
// Visit the ACME Labs Java page for up-to-date versions of this and other
// fine Java utilities: http://www.acme.com/java/

package Acme.Nnrpd;

import java.io.*;
import java.util.*;
import java.net.*;
import Acme.Utils;
import Acme.Fmt;
import Acme.Syslog;
import Acme.SyslogException;
import Acme.TimeKiller;

/// Multi-threaded proxying NNRP daemon.
// <P>
// This is a replacement for the standard C nnrpd.  Sites with lots
// of readers have found that running a separate nnrpd for each reader
// puts a tremendous load on the system.  All that memory, all those
// context switches.  This solves both problems by having a single
// process service multiple readers simultaneously.
// <P>
// Optionally, you can also use this as an NNRP proxy, connecting to
// a remote nnrpd instead of getting articles from the local disk.
// <P>
// Logging is via syslog, and is compatible with the standard nnrpd logging.
// <P>
// <A HREF="/resources/classes/Acme/Nnrpd/Nnrpd.java">Fetch the software.</A><BR>
// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>

public class Nnrpd
    {

    private static final String progName = "Nnrpd";

    /// Main routine, if you want to run this directly as an application.
    public static void main( String[] args )
	{
	// Parse args.
	int port = NnrpdUtils.DEFAULT_PORT;
	int aCacheSize = NnrpdUtils.DEFAULT_ACACHESIZE;
	int maxArtSize = NnrpdUtils.DEFAULT_MAXARTSIZE;
	int oCacheSize = NnrpdUtils.DEFAULT_OCACHESIZE;
	String proxyHost = null;
	boolean debug = false;
	int argc = args.length;
	int argn;
	for ( argn = 0; argn < argc && args[argn].charAt( 0 ) == '-'; ++argn )
	    {
	    if ( args[argn].equals( "-p" ) && argn + 1 < argc )
		{
		++argn;
		port = Utils.parseInt( args[argn], -1 );
		if ( port == -1 )
		    usage();
		}
	    else if ( args[argn].equals( "-a" ) && argn + 1 < argc )
		{
		++argn;
		aCacheSize = Utils.parseInt( args[argn], -1 );
		if ( aCacheSize == -1 )
		    usage();
		}
	    else if ( args[argn].equals( "-o" ) && argn + 1 < argc )
		{
		++argn;
		oCacheSize = Utils.parseInt( args[argn], -1 );
		if ( oCacheSize == -1 )
		    usage();
		}
	    else if ( args[argn].equals( "-h" ) && argn + 1 < argc )
		{
		++argn;
		proxyHost = args[argn];
		}
	    else if ( args[argn].equals( "-d" ) )
		debug = true;
	    else
		usage();
	    }
	if ( argn != argc )
	    usage();

	try
	    {
	    // Create the server.
	    Nnrpd nnrpd = new Nnrpd(
		port, aCacheSize, maxArtSize, oCacheSize, proxyHost, debug );

	    // And run.
	    nnrpd.serve();
	    }
	catch ( NnrpdException e )
	    {
	    System.err.println( e );
	    System.exit( 1 );
	    }

	System.exit( 0 );
	}

    private static void usage()
	{
	System.err.println(
	    "usage:  " + progName +
	    " [-p port] [-a art-cachesize] [-o ov-cachesize] [-h proxyhost]" );
	System.err.println( "" );
	System.err.println( "  port:" );
	System.err.println(
	    "    Normally netnews servers run on port " + NnrpdUtils.DEFAULT_PORT + ", which is where the" );
	System.err.println(
	    "    readers look for them.  You can run this on a different port if" );
	System.err.println(
	    "    you like, perhaps for debugging." );
	System.err.println( "  art-cachesize:" );
	System.err.println( "    How many bytes of articles to cache.  Default is " + NnrpdUtils.DEFAULT_ACACHESIZE + "." );
	System.err.println( "  ov-cachesize:" );
	System.err.println(
	    "    How many overview entries to cache.  Default is " + NnrpdUtils.DEFAULT_OCACHESIZE + "." );
	System.err.println( "  proxyhost:" );
	System.err.println(
	    "    The remote news server to connect to, for proxy mode.  Syntax" );
	System.err.println(
	    "    is a hostname, or hostname:port for ports other than " + NnrpdUtils.DEFAULT_PORT + "." );
	System.err.println( "" );
	System.err.println( "For more information see the web page," );
	System.err.println( NnrpdUtils.serverUrl );
	System.exit( 1 );
	}


    private int port;
    private int aCacheSize;
    private int maxArtSize;
    private int oCacheSize;
    private String proxyHost;
    private boolean debug;

    private Syslog syslog;
    private NewsDb newsDb;
    private ArticleCache articleCache;
    private boolean newsDbPostingOk;

    /// Constructor.
    // @param port Normally netnews servers run on port 119, which is where the readers look for them.  You can run this on a different port if you like, perhaps for debugging.
    // @param aCacheSize How many bytes of articles to cache.
    // @param maxArtSize Articles larger than this do not get cached at all.
    // @param oCacheSize How many overview entries to cache.
    // @param proxyHost The remote news server to connect to, for proxy mode.  Syntax is a hostname, or hostname:port for ports other than 119.
    // @param debug Whether to emit debugging syslogs or just dump them.
    // @exception NnrpdException if something goes wrong
    public Nnrpd( int port, int aCacheSize, int maxArtSize, int oCacheSize, String proxyHost, boolean debug ) throws NnrpdException
	{
	this.port = port;
	this.aCacheSize = aCacheSize;
	this.maxArtSize = maxArtSize;
	this.oCacheSize = oCacheSize;
	this.proxyHost = proxyHost;
	this.debug = debug;

	try
	    {
	    syslog = new Syslog( progName, 0, Syslog.LOG_NEWS );
	    }
	catch ( SyslogException e )
	    {
	    throw new NnrpdException( "can't initialize syslog: " + e );
	    }

	articleCache = new ArticleCache( aCacheSize, maxArtSize );

	try
	    {
	    if ( proxyHost != null )
		newsDb = new ProxyNewsDb( proxyHost, oCacheSize );
	    else
		newsDb = new LocalNewsDb( oCacheSize );
	    newsDbPostingOk = newsDb.getPostingOk();
	    }
	catch ( NewsDbException e )
	    {
	    throw new NnrpdException( "problem initializing news db: " + e );
	    }
	}


    /// Run the server.  Returns only on errors.
    // @exception NnrpdException if something goes wrong
    public void serve() throws NnrpdException
	{
	ServerSocket serverSocket;
	try
	    {
	    serverSocket = new ServerSocket( port, 1000 );
	    }
	catch ( IOException e )
	    {
	    error( "problem getting server socket: " + e );
	    return;
	    }

	try
	    {
	    while ( true )
		{
		try
		    {
		    Socket socket = serverSocket.accept();
		    new NnrpdSession(
			this, newsDb, newsDbPostingOk, articleCache, socket );
		    }
		catch ( IOException e )
		    {
		    if ( ! e.getMessage().endsWith( "Interrupted system call" ) )
			throw e;
		    }
		}
	    }
	catch ( IOException e )
	    {
	    error( "problem doing accept: " + e );
	    }
	finally
	    {
	    try
		{
		serverSocket.close();
		}
	    catch ( IOException ignore ) {}
	    }
	}


    void debug( String message ) throws NnrpdException
	{
	if ( debug )
	    log( Syslog.LOG_DEBUG, message );
	}

    void info( String message ) throws NnrpdException
	{
	log( Syslog.LOG_INFO, message );
	}

    void notice( String message ) throws NnrpdException
	{
	log( Syslog.LOG_NOTICE, message );
	}

    void warning( String message ) throws NnrpdException
	{
	log( Syslog.LOG_WARNING, message );
	}

    void error( String message ) throws NnrpdException
	{
	log( Syslog.LOG_ERR, message );
	}

    void crit( String message ) throws NnrpdException
	{
	log( Syslog.LOG_CRIT, message );
	}

    void alert( String message ) throws NnrpdException
	{
	log( Syslog.LOG_ALERT, message );
	}

    void emerg( String message ) throws NnrpdException
	{
	log( Syslog.LOG_EMERG, message );
	}

    private void log( int priority, String message ) throws NnrpdException
	{
	try
	    {
	    syslog.syslog( priority, message );
	    }
	catch ( SyslogException e )
	    {
	    throw new NnrpdException( "syslog problem: " + e );
	    }
	}

    }


class NnrpdSession implements Runnable
    {

    /// The overview.fmt we implement.  It seems that some netnews user agents
    // just assume this format, rather than asking the backend what format
    // it provides.  Therefore we hardcode the format.
    private static String[] overviewFmt = {
	"Subject", "From", "Date", "Message-ID", "References", "Bytes",
	"Lines" };

    private Nnrpd nnrpd;
    private NewsDb newsDb;
    private boolean newsDbPostingOk;
    private ArticleCache articleCache;
    private Socket socket;
    private String clientHost;

    /// Constructor.
    public NnrpdSession( Nnrpd nnrpd, NewsDb newsDb, boolean newsDbPostingOk, ArticleCache articleCache, Socket socket )
	{
	// Save arguments.
	this.nnrpd = nnrpd;
	this.newsDb = newsDb;
	this.newsDbPostingOk = newsDbPostingOk;
	this.articleCache = articleCache;
	this.socket = socket;

	// Start a thread.
	Thread thread = new Thread( this );
	thread.start();
	}

    private DataInputStream din;
    private PrintStream pout;

    public void run()
	{
	try
	    {
	    clientHost = socket.getInetAddress().getHostName();
	    notice( clientHost + " connect" );
	    try
		{
		// Get the streams.
		din = new DataInputStream( new BufferedInputStream(
		    socket.getInputStream() ) );
		pout = new PrintStream( new CrLfOutputStream(
		    new BufferedOutputStream( socket.getOutputStream() ) ) );
		}
	    catch ( IOException e )
		{
		warning( clientHost + " problem getting streams: " + e );
		return;
		}

	    handleSession();

	    try
		{
		din.close();
		pout.flush();
		pout.close();
		socket.close();
		}
	    catch ( IOException ignore ) {}
	    }
	catch ( NnrpdException e )
	    {
	    System.err.println( "uncaught exception: " + e );
	    }
	}


    /// Read and handle requests until done.
    private void handleSession() throws NnrpdException
	{
	if ( newsDbPostingOk )
	    response(
		"200 " + NnrpdUtils.serverName + " " +
		NnrpdUtils.serverVersion + " ready - posting allowed" );
	else
	    response(
		"201 " + NnrpdUtils.serverName + " " +
		NnrpdUtils.serverVersion + " ready - no posting allowed" );

	TimeKiller tk = new TimeKiller( NnrpdUtils.INT_IDLETIMEOUT );
	while ( true )
	    {
	    // Flush previous output.
	    pout.flush();
	    // Reset idle timer.
	    tk.reset();
	    // Read a command line.
	    String commandLine;
	    try
		{
		commandLine = din.readLine();
		}
	    catch ( IOException e )
		{
		warning( clientHost + " problem reading command: " + e );
		break;
		}
	    debug( clientHost + " < " + commandLine );
	    try
		{
		if ( commandLine == null )
		    {
		    // End of file - treat as a QUIT.
		    cmdQuit( null );
		    break;
		    }

		// Split up the command word and the rest of the line.
		int ws = Utils.strCSpan( commandLine, " \t" );
		String command = commandLine.substring( 0, ws );
		String[] args =
		    Utils.splitStr( commandLine.substring( ws ).trim() );

		// Dispatch the command to the right subroutine.
		if ( command.equalsIgnoreCase( NnrpdUtils.CMD_ARTICLE ) )
		    cmdArticle( args );
		else if ( command.equalsIgnoreCase( NnrpdUtils.CMD_AUTHINFO ) )
		    cmdAuthinfo( args );
		else if ( command.equalsIgnoreCase( NnrpdUtils.CMD_BODY ) )
		    cmdBody( args );
		else if ( command.equalsIgnoreCase( NnrpdUtils.CMD_DATE ) )
		    cmdDate( args );
		else if ( command.equalsIgnoreCase( NnrpdUtils.CMD_GROUP ) )
		    cmdGroup( args );
		else if ( command.equalsIgnoreCase( NnrpdUtils.CMD_HEAD ) )
		    cmdHead( args );
		else if ( command.equalsIgnoreCase( NnrpdUtils.CMD_HELP ) )
		    cmdHelp( args );
		else if ( command.equalsIgnoreCase( NnrpdUtils.CMD_LAST ) )
		    cmdLast( args );
		else if ( command.equalsIgnoreCase( NnrpdUtils.CMD_LIST ) )
		    cmdList( args );
		else if ( command.equalsIgnoreCase( NnrpdUtils.CMD_LISTGROUP ) )
		    cmdListgroup( args );
		else if ( command.equalsIgnoreCase( NnrpdUtils.CMD_MODE ) ||
		          command.equalsIgnoreCase( NnrpdUtils.CMD_XMODE ) )

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -