Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
   /*
    * 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.net.Socket;
  
  
  /*
   * Ahmet Uyar <auyar@csit.fsu.edu>sent in a bug report for TCP operation of the JAIN sipStack.
   * Niklas Uhrberg suggested that a mechanism be added to limit the number of simultaneous open
   * connections. The TLS Adaptations were contributed by Daniel Martinez. Hagai Sela contributed a
   * bug fix for symmetric nat. Jeroen van Bemmel added compensation for buggy clients ( Microsoft
   * RTC clients ). Bug fixes by viswashanti.kadiyala@antepo.com, Joost Yervante Damand
   */

This is a stack abstraction for TCP connections. This abstracts a stream of parsed messages. The SIP sipStack starts this from the main SIPStack class for each connection that it accepts. It starts a message parser in its own thread and talks to the message parser via a pipe. The message parser calls back via the parseError or processMessage functions that are defined as part of the SIPMessageListener interface.

Author(s):
M. Ranganathan
Version:
1.2 $Revision: 1.83 $ $Date: 2010-12-02 22:44:53 $
See also:
gov.nist.javax.sip.parser.PipelinedMsgParser
  
  public class TCPMessageChannel extends MessageChannel implements
      private static StackLogger logger = CommonLogger.getLogger(TCPMessageChannel.class);
      private Socket mySock;
  
      private PipelinedMsgParser myParser;
  
      protected InputStream myClientInputStream// just to pass to thread.
  
      protected OutputStream myClientOutputStream;
  
      protected String key;
  
      protected boolean isCached;
  
     // Set here on initialization to avoid thread leak. See issue 266
     protected boolean isRunning = true;
 
     private Thread mythread;
 
     protected SIPTransactionStack sipStack;
 
     protected String myAddress;
 
     protected int myPort;
 
     protected InetAddress peerAddress;
     
     // This is the port that we will find in the headers of the messages from the peer
     protected int peerPortAdvertisedInHeaders = -1;
 
     protected int peerPort;
 
     protected String peerProtocol;
 
     // Incremented whenever a transaction gets assigned
     // to the message channel and decremented when
     // a transaction gets freed from the message channel.
     // protected int useCount;
 
 
     protected TCPMessageChannel(SIPTransactionStack sipStack) {
         this. = sipStack;
 
     }

    
Constructor - gets called from the SIPStack class with a socket on accepting a new client. All the processing of the message is done here with the sipStack being freed up to handle new connections. The sock input is the socket that is returned from the accept. Global data that is shared by all threads is accessible in the Server structure.

Parameters:
sock Socket from which to read and write messages. The socket is already connected (was created as a result of an accept).
sipStack Ptr to SIP Stack
 
 
     protected TCPMessageChannel(Socket sockSIPTransactionStack sipStack,
             TCPMessageProcessor msgProcessorString threadNamethrows IOException {
 
         if (.isLoggingEnabled(.)) {
             .logDebug(
                     "creating new TCPMessageChannel ");
             .logStackTrace();
         }
          = sock;
          = .getInetAddress();
          = msgProcessor.getIpAddress().getHostAddress();
          = new Thread(this);
         .setDaemon(true);
         .setName(threadName);
         // Stash away a pointer to our sipStack structure.
         this. = sipStack;
         this. = .getPort();
         this. = MessageChannel.getKey("TCP");
 
         this. = msgProcessor;
         this. = this..getPort();
         // Bug report by Vishwashanti Raj Kadiayl
         super. = msgProcessor;
         // Can drop this after response is sent potentially.
         .start();
     }

    
Constructor - connects to the given inet address. Acknowledgement -- Lamine Brahimi (IBM Zurich) sent in a bug fix for this method. A thread was being uncessarily created.

Parameters:
inetAddr inet address to connect to.
sipStack is the sip sipStack from which we are created.
Throws:
java.io.IOException if we cannot connect.
 
     protected TCPMessageChannel(InetAddress inetAddrint port,
             SIPTransactionStack sipStackTCPMessageProcessor messageProcessor)
             throws IOException {
         if (.isLoggingEnabled(.)) {
             .logDebug(
                     "creating new TCPMessageChannel ");
             .logStackTrace();
         }
         this. = inetAddr;
         this. = port;
         this. = messageProcessor.getPort();
         this. = "TCP";
         this. = sipStack;
         this. = messageProcessor;
         this. = messageProcessor.getIpAddress().getHostAddress();
         // Bug report by Vishwashanti Raj Kadiayl
         this. = MessageChannel.getKey("TCP");
         super. = messageProcessor;
 
     }


    
Returns "true" as this is a reliable transport.
 
     public boolean isReliable() {
         return true;
     }

    
Close the message channel.
 
     public void close() {
     	close(true);
     }

    
Close the message channel.
 
     public void close(boolean removeSocket) {  
          = false;
     	// we need to close everything because the socket may be closed by the other end
     	// like in LB scenarios sending OPTIONS and killing the socket after it gets the response    	
         if ( != null) {
                 .logDebug("Closing socket " + );
         	try {
 	            .close();
 	             = null;
         	} catch (IOException ex) {
                 if (.isLoggingEnabled(.))
                     .logDebug("Error closing socket " + ex);
             }
         }        
         if( != null) {
                 .logDebug("Closing my parser " + );
             .close();            
         }  
         // no need to close myClientInputStream since myParser.close() above will do it
         if( != null) {
                 .logDebug("Closing client output stream " + );
         	try {
         		.close();
         	} catch (IOException ex) {
                 if (.isLoggingEnabled(.))
                     .logDebug("Error closing client output stream" + ex);
             }
         }   
         if(removeSocket) {                  
 	        // remove the "tcp:" part of the key to cleanup the ioHandler hashmap
 	        String ioHandlerKey = .substring(4);
 	            .logDebug("Closing TCP socket " + ioHandlerKey);
 	        // Issue 358 : remove socket and semaphore on close to avoid leaking
 	        ..removeSocket(ioHandlerKey);
 	            .logDebug("Closing message Channel " + this);       
 	    } else {
     			String ioHandlerKey = .substring(4);
 	    		.logDebug("not removing socket key from the cached map since it has already been updated by the iohandler.sendBytes " + ioHandlerKey);
     		}
     	}
     
     }

    
Get my SIP Stack.

Returns:
The SIP Stack for this message channel.
 
     public SIPTransactionStack getSIPStack() {
         return ;
     }

    
get the transport string.

Returns:
"tcp" in this case.
 
     public String getTransport() {
         return "TCP";
     }

    
get the address of the client that sent the data to us.

Returns:
Address of the client that sent us data that resulted in this channel being created.
 
     public String getPeerAddress() {
         if ( != null) {
             return .getHostAddress();
         } else
             return getHost();
     }
 
     protected InetAddress getPeerInetAddress() {
         return ;
     }
 
     public String getPeerProtocol() {
         return this.;
     }

    
Send message to whoever is connected to us. Uses the topmost via address to send to.

Parameters:
msg is the message to send.
isClient
 
     private void sendMessage(byte[] msgboolean isClientthrows IOException {
 
         if ( .isLoggingEnabled(.)) {
             .logDebug("sendMessage isClient  = " + isClient);
         }
         /*
          /*
          * Patch from kircuv@dev.java.net (Issue 119 ) This patch avoids the case where two
          * TCPMessageChannels are now pointing to the same socket.getInputStream().
          * 
          * JvB 22/5 removed
          */
        // Socket s = this.sipStack.ioHandler.getSocket(IOHandler.makeKey(
        // this.peerAddress, this.peerPort));
         Socket sock = null;
         IOException problem = null;
         try {
         	sock = this...sendBytes(this..getIpAddress(),
                 this.this.this.msgisClientthis);
         } catch (IOException any) {
         	problem = any;
         	.logWarning("Failed to connect " + this. + ":" + this. +" but trying the advertised port=" + this. + " if it's different than the port we just failed on");
         }
         if(sock == null) { // If we couldn't connect to the host, try the advertised port as failsafe
         	if(this. != this. &&  > 0) { // no point in trying same port
                 .logWarning("Couldn't connect to peerAddress = " +  + " peerPort = " +  + " key = " +  +  " retrying on peerPortAdvertisedInHeaders " + );
                 
 //                MessageChannel backupChannel = this.sipStack.createRawMessageChannel(
 //                		this.messageProcessor.getIpAddress().getHostAddress(), 
 //                		this.messageProcessor.getPort(), 
 //                		new HopImpl(peerAddress.getHostAddress(), peerPortAdvertisedInHeaders, "TCP"));
 //                backupChannel.sendMessage(msg, peerAddress, peerPortAdvertisedInHeaders, retry);
                 
         		sock = this...sendBytes(this..getIpAddress(),
                     this.this.this.msgisClientthis);        		
         		this. = this.;
         		this. = MessageChannel.getKey("TCP");
                 .logWarning("retry suceeded to peerAddress = " +  + " peerPortAdvertisedInHeaders = " +  + " key = " + );
         	} else {
         		throw problem// throw the original excpetion we had from the first attempt
         	}
         }
 
         // Created a new socket so close the old one and stick the new
         // one in its place but dont do this if it is a datagram socket.
         // (could have replied via udp but received via tcp!).
         // if (mySock == null && s != null) {
         // this.uncache();
         // } else
         if (sock !=  && sock != null) {
         	if( != null && .isLoggingEnabled(.)) {
                  .logDebug(
                          "Old socket different than new socket");
                  .logStackTrace();
                  
 	             .logDebug(
 	                	 "Old socket local ip address " + .getLocalSocketAddress());
 	             .logDebug(
 	            		 "Old socket remote ip address " + .getRemoteSocketAddress());                         
                  .logDebug(
                 		 "New socket local ip address " + sock.getLocalSocketAddress());
                  .logDebug(
                 		 "New socket remote ip address " + sock.getRemoteSocketAddress());
             }
             close(false);
              = sock;
             this. = .getInputStream();
             this. = .getOutputStream();
             Thread thread = new Thread(this);
             thread.setDaemon(true);
             thread.setName("TCPMessageChannelThread");
             thread.start();
         }
 
     }

    
Return a formatted message to the client. We try to re-connect with the peer on the other end if possible.

Parameters:
sipMessage Message to send.
Throws:
java.io.IOException If there is an error sending the message
 
     public void sendMessage(final SIPMessage sipMessagethrows IOException {
 
         if ( .isLoggingEnabled(.) && !sipMessage.isNullRequest() ) {
             .logDebug("sendMessage:: " + sipMessage.getFirstLine() + " cseq method = " + sipMessage.getCSeq().getMethod());
         }
 
         for (MessageProcessor messageProcessor : getSIPStack()
                 .getMessageProcessors()) {
             if (messageProcessor.getIpAddress().getHostAddress().equals(
                     this.getPeerAddress())
                     && messageProcessor.getPort() == this.getPeerPort()
                     && messageProcessor.getTransport().equalsIgnoreCase(
                             this.getPeerProtocol())) {
                 Runnable processMessageTask = new Runnable() {
 
                     public void run() {
                         try {
                             processMessage((SIPMessagesipMessage.clone());
                         } catch (Exception ex) {
                             if (
                                     .isLoggingEnabled(.)) {
                                 
                                         .logError(
                                                 "Error self routing message cause by: ",
                                                 ex);
                             }
                         }
                     }
                 };
                 getSIPStack().getSelfRoutingThreadpoolExecutor().execute(
                         processMessageTask);
 
                 if (.isLoggingEnabled(
                         .))
                     .logDebug(
                             "Self routing message");
                 return;
             }
 
         }
 
         byte[] msg = sipMessage.encodeAsBytes(this.getTransport());
 
         long time = System.currentTimeMillis();
         
         // need to store the peerPortAdvertisedInHeaders in case the response has an rport (ephemeral) that failed to retry on the regular via port
         // for responses, no need to store anything for subsequent requests.
         if( <= 0) {
         	if(sipMessage instanceof SIPResponse) {
         		SIPResponse sipResponse = (SIPResponsesipMessage
         		Via via = sipResponse.getTopmostVia();
         		if(via.getRPort() > 0) {
 	            	if(via.getPort() <=0) {    
 	            		// if port is 0 we assume the default port for TCP
 	            		this. = 5060;
 	            	} else {
 	            		this. = via.getPort();
 	            	}
 	            	if(.isLoggingEnabled(.)) {
 	                	.logDebug("1.Storing peerPortAdvertisedInHeaders = " +  + " for via port = " + via.getPort() + " via rport = " + via.getRPort() + " and peer port = " +  + " for this channel " + this + " key " + );
 	                }	 
         		}
         	}
         }
 
         // JvB: also retry for responses, if the connection is gone we should
         // try to reconnect
         this.sendMessage(msgsipMessage instanceof SIPRequest );
 
         // message was sent without any exception so let's set set port and
         // address before we feed it to the logger
         sipMessage.setRemoteAddress(this.);
         sipMessage.setRemotePort(this.);
         sipMessage.setLocalAddress(this.getMessageProcessor().getIpAddress());
         sipMessage.setLocalPort(this.getPort());
 
         if (.isLoggingEnabled(
                 .))
             logMessage(sipMessagetime);
     }

    
Send a message to a specified address.

Parameters:
message Pre-formatted message to send.
receiverAddress Address to send it to.
receiverPort Receiver port.
Throws:
java.io.IOException If there is a problem connecting or sending.
 
     public void sendMessage(byte message[], InetAddress receiverAddress,
             int receiverPortboolean retrythrows IOException {
         if (message == null || receiverAddress == null)
             throw new IllegalArgumentException("Null argument");
         
         if( <= 0) {
         	if(.isLoggingEnabled(.)) {
             	.logDebug("receiver port = " + receiverPort + " for this channel " + this + " key " + );
             }        	
         	if(receiverPort <=0) {    
         		// if port is 0 we assume the default port for TCP
         		this. = 5060;
         	} else {
         		this. = receiverPort;
         	}
         	if(.isLoggingEnabled(.)) {
             	.logDebug("2.Storing peerPortAdvertisedInHeaders = " +  + " for this channel " + this + " key " + );
             }	        
         }
         
         Socket sock = null;
         IOException problem = null;
         try {
         	sock = this...sendBytes(this..getIpAddress(),
                     receiverAddressreceiverPort"TCP"messageretrythis);
         } catch (IOException any) {
         	problem = any;
         	.logWarning("Failed to connect " + this. + ":" + receiverPort +" but trying the advertised port=" + this. + " if it's different than the port we just failed on");
         	.logError("Error is "any);
 
         }
         if(sock == null) { // If we couldn't connect to the host, try the advertised port as failsafe
         	if(receiverPort != this. &&  > 0) { // no point in trying same port
                 .getStackLogger().logWarning("Couldn't connect to receiverAddress = " + receiverAddress + " receiverPort = " + receiverPort + " key = " +  +  " retrying on peerPortAdvertisedInHeaders " + );
                 
 //                MessageChannel backupChannel = this.sipStack.createRawMessageChannel(
 //                		this.messageProcessor.getIpAddress().getHostAddress(), 
 //                		this.messageProcessor.getPort(), 
 //                		new HopImpl(receiverAddress.getHostAddress(), peerPortAdvertisedInHeaders, "TCP"));
 //                backupChannel.sendMessage(message, receiverAddress, peerPortAdvertisedInHeaders, retry);
                 
         		sock = this...sendBytes(this..getIpAddress(),
                     receiverAddressthis."TCP"messageretrythis);
         		this. = this.;
         		this. = MessageChannel.getKey("TCP");
                 
                 .getStackLogger().logWarning("retry suceeded to receiverAddress = " + receiverAddress + " peerPortAdvertisedInHeaders = " +  + " key = " + );
         	} else {
         		throw problem// throw the original excpetion we had from the first attempt
         	}
         }
       
         if (sock !=  && sock != null) {        	        	
             if ( != null) {
             	if(.isLoggingEnabled(.)) {
 	            	.logDebug(
 	                         "Old socket different than new socket");
 	                 .logStackTrace();
 	                 
 		             .logDebug(
 		                	 "Old socket local ip address " + .getLocalSocketAddress());
 		             .logDebug(
 		            		 "Old socket remote ip address " + .getRemoteSocketAddress());                         
 	                 .logDebug(
 	                		 "New socket local ip address " + sock.getLocalSocketAddress());
 	                 .logDebug(
 	                		 "New socket remote ip address " + sock.getRemoteSocketAddress());
             	}
                 /*
                  * Delay the close of the socket for some time in case it is
                  * being used.
                  */
                 .getTimer().schedule(new SIPStackTimerTask() {
                     @Override
                     public void cleanUpBeforeCancel() {
                     	close(false);
                     }
 
                     public void runTask() {
                         close(false);
                     }
                 }, 8000);
             }
 
              = sock;
             this. = .getInputStream();
             this. = .getOutputStream();
             // start a new reader on this end of the pipe.
             Thread mythread = new Thread(this);
             mythread.setDaemon(true);
             mythread.setName("TCPMessageChannelThread");
             mythread.start();
         }
 
     }

    
Exception processor for exceptions detected from the parser. (This is invoked by the parser when an error is detected).

Parameters:
sipMessage -- the message that incurred the error.
ex -- parse exception detected by the parser.
header -- header that caused the error.
Throws:
java.text.ParseException Thrown if we want to reject the message.
 
     public void handleException(ParseException exSIPMessage sipMessage,
             Class hdrClassString headerString message)
             throws ParseException {
         if (.isLoggingEnabled())
             .logException(ex);
         // Log the bad message for later reference.
         if ((hdrClass != null)
                 && (hdrClass.equals(From.class) || hdrClass.equals(To.class)
                         || hdrClass.equals(CSeq.class)
                         || hdrClass.equals(Via.class)
                         || hdrClass.equals(CallID.class)
                         || hdrClass.equals(ContentLength.class)
                         || hdrClass.equals(RequestLine.class) || hdrClass
                         .equals(StatusLine.class))) {
             if (.isLoggingEnabled(.)) {
                 .logDebug(
                         "Encountered Bad Message \n" + sipMessage.toString());
             }
 
             // JvB: send a 400 response for requests (except ACK)
             // Currently only UDP, @todo also other transports
             String msgString = sipMessage.toString();
             if (!msgString.startsWith("SIP/") && !msgString.startsWith("ACK ")) {
             	if( != null)
             	{
 	            	 if (.isLoggingEnabled(.)) {
 	            		 .logError("Malformed mandatory headers: closing socket! :" + .toString());
 	            	 }
 	                
 	            	try
 	            	{
 	            		.close();
 	            		
 	            	} catch(IOException ie)
 	            	{
 	            		if (.isLoggingEnabled(.)) {
 	            			.logError("Exception while closing socket! :" + .toString() + ":" + ie.toString());
 	            		}
 	            		
 	            	}
             	}
             }
 
             throw ex;
         } else {
             sipMessage.addUnparsed(header);
         }
     }
 
     public void processMessage(SIPMessage sipMessageInetAddress address) {
         this. = address;
         try {
             processMessage(sipMessage);
         } catch (Exception e) {
             if (.isLoggingEnabled(
                     .)) {
                 .logError(
                         "ERROR processing self routing"e);
             }
         }
     }

    
Gets invoked by the parser as a callback on successful message parsing (i.e. no parser errors).

Parameters:
sipMessage Mesage to process (this calls the application for processing the message).
 
     public void processMessage(SIPMessage sipMessagethrows Exception {
         try {
             if (sipMessage.getFrom() == null
                     || // sipMessage.getFrom().getTag()
                     // == null ||
                     sipMessage.getTo() == null
                     || sipMessage.getCallId() == null
                     || sipMessage.getCSeq() == null
                     || sipMessage.getViaHeaders() == null) {
                 String badmsg = sipMessage.encode();
                 if (.isLoggingEnabled(.)) {
                     .logDebug(">>> Dropped Bad Msg");
                     .logDebug(badmsg);
                 }
 
                 return;
             }
             sipMessage.setRemoteAddress(this.);
             sipMessage.setRemotePort(this.getPeerPort());
             sipMessage.setLocalAddress(this.getMessageProcessor().getIpAddress());
             sipMessage.setLocalPort(this.getPort());
 
             ViaList viaList = sipMessage.getViaHeaders();
             // For a request
             // first via header tells where the message is coming from.
             // For response, this has already been recorded in the outgoing
             // message.
             if (sipMessage instanceof SIPRequest) {
                 Via v = (ViaviaList.getFirst();
                 Hop hop = ..resolveAddress(v.getHop());
                 this. = v.getTransport();
                 if( <= 0) {
                 	int hopPort = v.getPort();
                 	if(.isLoggingEnabled(.)) {
                     	.logDebug("hop port = " + hopPort + " for request " + sipMessage + " for this channel " + this + " key " + );
                     }                	
                 	if(hopPort <= 0) {    
                 		// if port is 0 we assume the default port for TCP
                 		this. = 5060;
                 	} else {
                 		this. = hopPort;
                 	}
                 	if(.isLoggingEnabled(.)) {
                     	.logDebug("3.Storing peerPortAdvertisedInHeaders = " +  + " for this channel " + this + " key " + );
                     }
                 }      
                 try {
                     if ( != null) { // selfrouting makes socket = null
                                           // https://jain-sip.dev.java.net/issues/show_bug.cgi?id=297
                         this. = .getInetAddress();
                     }
                     // Check to see if the received parameter matches
                     // the peer address and tag it appropriately.
 
                     // JvB: dont do this. It is both costly and incorrect
                     // Must set received also when it is a FQDN, regardless
                     // whether
                     // it resolves to the correct IP address
                     // InetAddress sentByAddress =
                     // InetAddress.getByName(hop.getHost());
                     // JvB: if sender added 'rport', must always set received
                     if (v.hasParameter(.)
                             || !hop.getHost().equals(
                                     this..getHostAddress())) {
                         v.setParameter(.this.
                                 .getHostAddress());
                     }
                     // @@@ hagai
                     // JvB: technically, may only do this when Via already
                     // contains
                     // rport
                     v.setParameter(., Integer.toString(this.));
                 } catch (java.text.ParseException ex) {
                     InternalErrorHandler.handleException(ex);
                 }
                 // Use this for outgoing messages as well.
                 if (!this. &&  != null) { // self routing makes
                                                         // mySock=null
                                                         // https://jain-sip.dev.java.net/issues/show_bug.cgi?id=297
                     this. = true;
                     int remotePort = ((java.net.InetSocketAddress
                             .getRemoteSocketAddress()).getPort();
                     String key = IOHandler.makeKey(.getInetAddress(),
                             remotePort);
                     ..putSocket(key);
                     // since it can close the socket it needs to be after the mySock usage otherwise
                     // it the socket will be disconnected and NPE will be thrown in some edge cases
                     ((TCPMessageProcessorthis.)
                             .cacheMessageChannel(this);
                 }
             }
 
             // Foreach part of the request header, fetch it and process it
 
             long receptionTime = System.currentTimeMillis();
 
             if (sipMessage instanceof SIPRequest) {
                 // This is a request - process the request.
                 SIPRequest sipRequest = (SIPRequestsipMessage;
                 // Create a new sever side request processor for this
                 // message and let it handle the rest.
 
                 if (.isLoggingEnabled(.)) {
                     .logDebug(
                             "----Processing Message---");
                 }
 
                 // Check for reasonable size - reject message
                 // if it is too long.
                 if (.isLoggingEnabled(
                         .)) {
                     ..logMessage(sipMessagethis
                             .getPeerHostPort().toString(), this
                             .getMessageProcessor().getIpAddress()
                             .getHostAddress()
                             + ":" + this.getMessageProcessor().getPort(),
                             falsereceptionTime);
 
                 }
 
                 if (.getMaxMessageSize() > 0
                         && sipRequest.getSize()
                                 + (sipRequest.getContentLength() == null ? 0
                                         : sipRequest.getContentLength()
                                                 .getContentLength()) > 
                                 .getMaxMessageSize()) {
                     SIPResponse sipResponse = sipRequest
                             .createResponse(.);
                     byte[] resp = sipResponse
                             .encodeAsBytes(this.getTransport());
                     this.sendMessage(respfalse);
                     throw new Exception("Message size exceeded");
                 }
 
                 /* @see Issue 292 */
                 String sipVersion = ((SIPRequestsipMessage).getRequestLine()
                         .getSipVersion();
                 if (!sipVersion.equals("SIP/2.0")) {
                     SIPResponse versionNotSupported = ((SIPRequestsipMessage)
                             .createResponse(.,
                                     "Bad version " + sipVersion);
                     this.sendMessage(versionNotSupported.encodeAsBytes(this
                             .getTransport()), false);
                     throw new Exception("Bad sip version");
                 }
 
                 String method = ((SIPRequestsipMessage).getMethod();
                 String cseqMethod = ((SIPRequestsipMessage).getCSeqHeader()
                         .getMethod();
 
                 if (!method.equalsIgnoreCase(cseqMethod)) {
                     SIPResponse sipResponse = sipRequest
                     .createResponse(.);
                     byte[] resp = sipResponse
                             .encodeAsBytes(this.getTransport());
                     this.sendMessage(respfalse);
                     throw new Exception("Bad CSeq method");
                 }
 
                 ServerRequestInterface sipServerRequest = 
                         .newSIPServerRequest(sipRequestthis);
 
                 if (sipServerRequest != null) {
                     try {
                         sipServerRequest.processRequest(sipRequestthis);
                     } finally {
                         if (sipServerRequest instanceof SIPTransaction) {
                             SIPServerTransaction sipServerTx = (SIPServerTransactionsipServerRequest;
                             if (!sipServerTx.passToListener())
                                 ((SIPTransactionsipServerRequest)
                                         .releaseSem();
                         }
                     }
                 } else {
                     if (.isLoggingEnabled())
                         
                                 .logWarning(
                                         "Dropping request -- could not acquire semaphore in 10 sec");
                 }
 
             } else {
                 SIPResponse sipResponse = (SIPResponsesipMessage;
                 // JvB: dont do this
                 // if (sipResponse.getStatusCode() == 100)
                 // sipResponse.getTo().removeParameter("tag");
                 try {
                     sipResponse.checkHeaders();
                 } catch (ParseException ex) {
                     if (.isLoggingEnabled())
                         .logError(
                                 "Dropping Badly formatted response message >>> "
                                         + sipResponse);
                     return;
                 }
                 // This is a response message - process it.
                 // Check the size of the response.
                 // If it is too large dump it silently.
                 if (.getMaxMessageSize() > 0
                         && sipResponse.getSize()
                                 + (sipResponse.getContentLength() == null ? 0
                                         : sipResponse.getContentLength()
                                                 .getContentLength()) > 
                                 .getMaxMessageSize()) {
                     if (.isLoggingEnabled(.))
                         .logDebug(
                                 "Message size exceeded");
                     return;
 
                 }
                 ServerResponseInterface sipServerResponse = 
                         .newSIPServerResponse(sipResponsethis);
                 if (sipServerResponse != null) {
                     try {
                         if (sipServerResponse instanceof SIPClientTransaction
                                 && !((SIPClientTransactionsipServerResponse)
                                         .checkFromTag(sipResponse)) {
                             if (.isLoggingEnabled())
                                 .logError(
                                         "Dropping response message with invalid tag >>> "
                                                 + sipResponse);
                             return;
                         }
 
                         sipServerResponse.processResponse(sipResponsethis);
                     } finally {
                         if (sipServerResponse instanceof SIPTransaction
                                 && !((SIPTransactionsipServerResponse)
                                         .passToListener())
                             ((SIPTransactionsipServerResponse).releaseSem();
                     }
                 } else {
                     
                             .logWarning(
                                     "Application is blocked -- could not acquire semaphore -- dropping response");
                 }
             }
         } finally {
         }
     }

    
This gets invoked when thread.start is called from the constructor. Implements a message loop - reading the tcp connection and processing messages until we are done or the other end has closed.
 
 
 
    public void run() {
         Pipeline hispipe = null;
         // Create a pipeline to connect to our message parser.
         hispipe = new Pipeline(.,
                 ((SIPTransactionStack).getTimer());
         // Create a pipelined message parser to read and parse
         // messages that we write out to him.
          = new PipelinedMsgParser(thishispipe,
                 this..getMaxMessageSize());
         // Start running the parser thread.
         .processInput();
         // bug fix by Emmanuel Proulx
         int bufferSize = 4096;
         this..++;
         this. = true;
         try {
             while (true) {
                 try {
                     byte[] msg = new byte[bufferSize];
                     int nbytes = .read(msg, 0, bufferSize);
                     // no more bytes to read...
                     if (nbytes == -1) {
                         hispipe.write("\r\n\r\n".getBytes("UTF-8"));
                         try {
                             if (. != -1) {
                                 synchronized () {
                                     .--;
                                     .notify();
                                 }
                             }
                             hispipe.close();
                             close();
                         } catch (IOException ioex) {
                         }
                         return;
                     }                    
                     
                     hispipe.write(msg, 0, nbytes);
 
                 } catch (IOException ex) {
                     // Terminate the message.
                     try {
                         hispipe.write("\r\n\r\n".getBytes("UTF-8"));
                     } catch (Exception e) {
                         // InternalErrorHandler.handleException(e);
                     }
 
                     try {
                         if (.isLoggingEnabled(.))
                             .logDebug(
                                     "IOException  closing sock " + ex);
                         try {
                             if (. != -1) {
                                 synchronized () {
                                     .--;
                                     // System.out.println("Notifying!");
                                     .notify();
                                 }
                             }
                             close();
                             hispipe.close();
                         } catch (IOException ioex) {
                         }
                     } catch (Exception ex1) {
                         // Do nothing.
                     }
                     return;
                 } catch (Exception ex) {
                     InternalErrorHandler.handleException(ex);
                 }
             }
         } finally {
             this. = false;
            this..remove(this);
            this..--;
            // parser could be null if the socket was closed by the remote end already
            if( != null) {
            	.close();
            }
        }
    }
    protected void uncache() {
        if ( && !) {
            this..remove(this);
        }
    }

    
Equals predicate.

Parameters:
other is the other object to compare ourselves to for equals
    public boolean equals(Object other) {
        if (!this.getClass().equals(other.getClass()))
            return false;
        else {
            TCPMessageChannel that = (TCPMessageChannelother;
            if (this. != that.mySock)
                return false;
            else
                return true;
        }
    }

    
Get an identifying key. This key is used to cache the connection and re-use it if necessary.
    public String getKey() {
        if (this. != null) {
            return this.;
        } else {
            this. = MessageChannel.getKey(this.this.,
                    "TCP");
            return this.;
        }
    }

    
Get the host to assign to outgoing messages.

Returns:
the host to assign to the via header.
    public String getViaHost() {
        return this.;
    }

    
Get the port for outgoing messages sent from the channel.

Returns:
the port to assign to the via header.
    public int getViaPort() {
        return this.;
    }

    
Get the port of the peer to whom we are sending messages.

Returns:
the peer port.
    public int getPeerPort() {
        return ;
    }
    public int getPeerPacketSourcePort() {
        return this.;
    }
        return this.;
    }

    
TCP Is not a secure protocol.
    public boolean isSecure() {
        return false;
    }
    /*
     * (non-Javadoc)
     * @see gov.nist.javax.sip.parser.SIPMessageListener#sendSingleCLRF()
     */
	public void sendSingleCLRF() throws Exception {
		if( != null && !.isClosed()) {