/* * Conditions Of Use * * This software was developed by employees of the National Institute of * Standards and Technology (NIST), an agency of the Federal Government. * Pursuant to title 15 Untied States Code Section 105, works of NIST * employees are not subject to copyright protection in the United States * and are considered to be in the public domain. As a result, a formal * license is not needed to use the software. * * This software is provided by NIST as a service and is expressly * provided "AS IS." NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED * OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT * AND DATA ACCURACY. NIST does not warrant or make any representations * regarding the use of the software or the results thereof, including but * not limited to the correctness, accuracy, reliability or usefulness of * the software. * * Permission to use this software is contingent upon your acceptance * of the terms of this agreement * * . * */ /******************************************************************************* * Product of NIST/ITL Advanced Networking Technologies Division (ANTD). * *******************************************************************************/ package gov.nist.javax.sip.stack; import java.io.IOException; import java.util.LinkedList; import java.net.*; import gov.nist.core.*; /** * Sit in a loop and handle incoming udp datagram messages. For each Datagram * packet, a new UDPMessageChannel is created (upto the max thread pool size). * Each UDP message is processed in its own thread). * * @version 1.2 $Revision: 1.37 $ $Date: 2009/11/14 20:06:16 $ * * @author M. Ranganathan <br/> * * * * <a href="{@docRoot}/../uml/udp-request-processing-sequence-diagram.jpg"> * See the implementation sequence diagram for processing incoming requests. * </a> * * * Acknowledgement: Jeff Keyser contributed ideas on starting and stoppping the * stack that were incorporated into this code. Niklas Uhrberg suggested that * thread pooling be added to limit the number of threads and improve * performance. */ public class UDPMessageProcessor extends MessageProcessor { /** * The Mapped port (in case STUN suport is enabled) */ private int port; /** * Incoming messages are queued here. */ protected LinkedList messageQueue; /** * A list of message channels that we have started. */ protected LinkedList messageChannels; /** * Max # of udp message channels */ protected int threadPoolSize; protected DatagramSocket sock; /** * A flag that is set to false to exit the message processor (suggestion by * Jeff Keyser). */ protected boolean isRunning; private static final int HIGHWAT=5000; private static final int LOWAT=2500; /** * Constructor. * * @param sipStack * pointer to the stack. */ protected UDPMessageProcessor(InetAddress ipAddress, SIPTransactionStack sipStack, int port) throws IOException { super(ipAddress, port, "udp",sipStack); this.sipStack = sipStack; this.messageQueue = new LinkedList(); this.port = port; try { this.sock = sipStack.getNetworkLayer().createDatagramSocket(port, ipAddress); // Create a new datagram socket. sock.setReceiveBufferSize(sipStack.getReceiveUdpBufferSize()); sock.setSendBufferSize(sipStack.getSendUdpBufferSize()); /** * If the thread auditor is enabled, define a socket timeout value in order to * prevent sock.receive() from blocking forever */ if (sipStack.getThreadAuditor().isEnabled()) { sock.setSoTimeout((int) sipStack.getThreadAuditor().getPingIntervalInMillisecs()); } if ( ipAddress.getHostAddress().equals(IN_ADDR_ANY) || ipAddress.getHostAddress().equals(IN6_ADDR_ANY)){ // Store the address to which we are actually bound // Note that on WINDOWS this is actually broken. It will // return IN_ADDR_ANY again. On linux it will return the // address to which the socket was actually bound. super.setIpAddress( sock.getLocalAddress() ); } } catch (SocketException ex) { throw new IOException(ex.getMessage()); } } /** * Get port on which to listen for incoming stuff. * * @return port on which I am listening. */ public int getPort() { return this.port; } /** * Start our processor thread. */ public void start() throws IOException { this.isRunning = true; Thread thread = new Thread(this); thread.setDaemon(true); // Issue #32 on java.net thread.setName("UDPMessageProcessorThread"); // Issue #184 thread.setPriority(Thread.MAX_PRIORITY); thread.start(); } /** * Thread main routine. */ public void run() { // Check for running flag. this.messageChannels = new LinkedList(); // start all our messageChannels (unless the thread pool size is // infinity. if (sipStack.threadPoolSize != -1) { for (int i = 0; i < sipStack.threadPoolSize; i++) { UDPMessageChannel channel = new UDPMessageChannel(sipStack, this); this.messageChannels.add(channel); } } // Ask the auditor to monitor this thread ThreadAuditor.ThreadHandle threadHandle = sipStack.getThreadAuditor().addCurrentThread(); // Somebody asked us to exit. if isRunnning is set to false. while (this.isRunning) { try { // Let the thread auditor know we're up and running threadHandle.ping(); int bufsize = sock.getReceiveBufferSize(); byte message[] = new byte[bufsize]; DatagramPacket packet = new DatagramPacket(message, bufsize); sock.receive(packet); // This is a simplistic congestion control algorithm. // It accepts packets if queuesize is < LOWAT. It drops // requests if the queue size exceeds a HIGHWAT and accepts // requests with probability p proportional to the difference // between current queue size and LOWAT in the range // of queue sizes between HIGHWAT and LOWAT. // TODO -- penalize spammers by looking at the source // port and IP address. if ( sipStack.stackDoesCongestionControl ) { if ( this.messageQueue.size() >= HIGHWAT) { if (sipStack.isLoggingEnabled()) { sipStack.getStackLogger().logDebug("Dropping message -- queue length exceeded"); } //System.out.println("HIGHWAT Drop!"); continue; } else if ( this.messageQueue.size() > LOWAT && this .messageQueue.size() < HIGHWAT ) { // Drop the message with a probabilty that is linear in the range 0 to 1 float threshold = ((float)(messageQueue.size() - LOWAT))/ ((float)(HIGHWAT - LOWAT)); boolean decision = Math.random() > 1.0 - threshold; if ( decision ) { if (sipStack.isLoggingEnabled()) { sipStack.getStackLogger().logDebug("Dropping message with probability " + (1.0 - threshold)); } //System.out.println("RED Drop!"); continue; } } } // Count of # of packets in process. // this.useCount++; if (sipStack.threadPoolSize != -1) { // Note: the only condition watched for by threads // synchronizing on the messageQueue member is that it is // not empty. As soon as you introduce some other // condition you will have to call notifyAll instead of // notify below. synchronized (this.messageQueue) { // was addLast this.messageQueue.add(packet); this.messageQueue.notify(); } } else { new UDPMessageChannel(sipStack, this, packet); } } catch (SocketTimeoutException ex) { // This socket timeout alows us to ping the thread auditor periodically } catch (SocketException ex) { if (sipStack.isLoggingEnabled()) getSIPStack().getStackLogger() .logDebug("UDPMessageProcessor: Stopping"); isRunning = false; // The notifyAll should be in a synchronized block. // ( bug report by Niklas Uhrberg ). synchronized (this.messageQueue) { this.messageQueue.notifyAll(); } } catch (IOException ex) { isRunning = false; ex.printStackTrace(); if (sipStack.isLoggingEnabled()) getSIPStack().getStackLogger() .logDebug("UDPMessageProcessor: Got an IO Exception"); } catch (Exception ex) { if (sipStack.isLoggingEnabled()) getSIPStack().getStackLogger() .logDebug("UDPMessageProcessor: Unexpected Exception - quitting"); InternalErrorHandler.handleException(ex); return; } } } /** * Shut down the message processor. Close the socket for recieving incoming * messages. */ public void stop() { synchronized (this.messageQueue) { this.isRunning = false; this.messageQueue.notifyAll(); sock.close(); } } /** * Return the transport string. * * @return the transport string */ public String getTransport() { return "udp"; } /** * Returns the stack. * * @return my sip stack. */ public SIPTransactionStack getSIPStack() { return sipStack; } /** * Create and return new TCPMessageChannel for the given host/port. */ public MessageChannel createMessageChannel(HostPort targetHostPort) throws UnknownHostException { return new UDPMessageChannel(targetHostPort.getInetAddress(), targetHostPort.getPort(), sipStack, this); } public MessageChannel createMessageChannel(InetAddress host, int port) throws IOException { return new UDPMessageChannel(host, port, sipStack, this); } /** * Default target port for UDP */ public int getDefaultTargetPort() { return 5060; } /** * UDP is not a secure protocol. */ public boolean isSecure() { return false; } /** * UDP can handle a message as large as the MAX_DATAGRAM_SIZE. */ public int getMaximumMessageSize() { return 8*1024; } /** * Return true if there are any messages in use. */ public boolean inUse() { synchronized (messageQueue) { return messageQueue.size() != 0; } } }