blob: 9eb01951de25bb5232caeffb48facdca33905c62 [file] [log] [blame]
/*
* 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.
*/
#include <decaf/util/Config.h>
#include "TcpSocket.h"
#include "SocketInputStream.h"
#include "SocketOutputStream.h"
#include "SocketError.h"
using namespace decaf;
using namespace decaf::net;
using namespace decaf::io;
using namespace decaf::lang;
////////////////////////////////////////////////////////////////////////////////
TcpSocket::TcpSocket() throw ( SocketException )
: apr_pool( NULL ),
socketHandle( INVALID_SOCKET_HANDLE ),
inputStream( NULL ),
outputStream( NULL ) {
}
////////////////////////////////////////////////////////////////////////////////
TcpSocket::TcpSocket( SocketHandle socketHandle )
: apr_pool( NULL ),
socketHandle( INVALID_SOCKET_HANDLE ),
inputStream( NULL ),
outputStream( NULL ) {
try {
this->socketHandle = socketHandle;
this->inputStream = new SocketInputStream( socketHandle );
this->outputStream = new SocketOutputStream( socketHandle );
}
DECAF_CATCH_RETHROW( SocketException )
DECAF_CATCHALL_THROW( SocketException )
}
////////////////////////////////////////////////////////////////////////////////
TcpSocket::~TcpSocket() {
// No shutdown, just close - dont want blocking destructor.
close();
}
////////////////////////////////////////////////////////////////////////////////
InputStream* TcpSocket::getInputStream(){
return inputStream;
}
////////////////////////////////////////////////////////////////////////////////
OutputStream* TcpSocket::getOutputStream(){
return outputStream;
}
////////////////////////////////////////////////////////////////////////////////
void TcpSocket::connect(const char* host, int port) throw ( SocketException ) {
try{
if( isConnected() ) {
throw SocketException( __FILE__, __LINE__,
"Socket::connect - Socket already connected. host: %s, port: %d", host, port );
}
// Create the APR Pool
apr_pool_create( &apr_pool, NULL );
// Create the Address data
checkResult( apr_sockaddr_info_get(
&socketAddress, host, APR_INET, port, 0, apr_pool ) );
// Create the actual socket.
checkResult( apr_socket_create(
&socketHandle, socketAddress->family, SOCK_STREAM, APR_PROTO_TCP, apr_pool ) );
// it is a good idea to specify socket options explicitly. in this
// case, we make a blocking socket with system timeout, this should allow us
// the time needed to connect to the socket before returning, but not
// block us forever waiting if there isn't anyone there. Some poeple
// say that you shouldn't use this mode as its an OS timeout that is not
// controllable, if this becomes an issue, we should decide on what a
// reasonable timeout should be and set it here.
apr_socket_opt_set( socketHandle, APR_SO_NONBLOCK, 0 );
apr_socket_timeout_set( socketHandle, -1 );
checkResult( apr_socket_connect( socketHandle, socketAddress ) );
// Now that we are connected, we want to set ourself up as a blocking
// socket by default.
apr_socket_opt_set( socketHandle, APR_SO_NONBLOCK, 0 );
apr_socket_timeout_set( socketHandle, -1 );
// Create an input/output stream for this socket.
inputStream = new SocketInputStream( socketHandle );
outputStream = new SocketOutputStream( socketHandle );
} catch( SocketException& ex ) {
ex.setMark( __FILE__, __LINE__);
try{ close(); } catch( lang::Exception& cx){ /* Absorb */ }
throw ex;
} catch( ... ) {
try{ close(); } catch( lang::Exception& cx){ /* Absorb */ }
throw SocketException(
__FILE__, __LINE__,
"TcpSocket::connect() - caught unknown exception" );
}
}
////////////////////////////////////////////////////////////////////////////////
void TcpSocket::close() throw( lang::Exception )
{
// Destroy the input stream.
if( inputStream != NULL ){
delete inputStream;
inputStream = NULL;
}
// Destroy the output stream.
if( outputStream != NULL ){
delete outputStream;
outputStream = NULL;
}
// When connected we first shutdown, which breaks our reads and writes
// then we close to free APR resources.
if( isConnected() ) {
apr_socket_close( socketHandle );
socketHandle = INVALID_SOCKET_HANDLE;
}
// Destroy the APR Pool
if( apr_pool != NULL ) {
apr_pool_destroy( apr_pool );
apr_pool = NULL;
}
}
////////////////////////////////////////////////////////////////////////////////
int TcpSocket::getSoLinger() const throw( SocketException ){
try{
int value = 0;
checkResult( apr_socket_opt_get( socketHandle, APR_SO_LINGER, &value ) );
return value;
}
DECAF_CATCH_RETHROW( SocketException )
DECAF_CATCHALL_THROW( SocketException )
}
////////////////////////////////////////////////////////////////////////////////
void TcpSocket::setSoLinger( int dolinger ) throw( SocketException ){
try{
checkResult( apr_socket_opt_set( socketHandle, APR_SO_LINGER, dolinger ) );
}
DECAF_CATCH_RETHROW( SocketException )
DECAF_CATCHALL_THROW( SocketException )
}
////////////////////////////////////////////////////////////////////////////////
bool TcpSocket::getKeepAlive() const throw( SocketException ){
try{
int value = 0;
checkResult( apr_socket_opt_get( socketHandle, APR_SO_KEEPALIVE, &value ) );
return value != 0;
}
DECAF_CATCH_RETHROW( SocketException )
DECAF_CATCHALL_THROW( SocketException )
}
////////////////////////////////////////////////////////////////////////////////
void TcpSocket::setKeepAlive( const bool keepAlive ) throw( SocketException ){
try{
int value = keepAlive ? 1 : 0;
checkResult( apr_socket_opt_set( socketHandle, APR_SO_KEEPALIVE, value ) );
}
DECAF_CATCH_RETHROW( SocketException )
DECAF_CATCHALL_THROW( SocketException )
}
////////////////////////////////////////////////////////////////////////////////
int TcpSocket::getReceiveBufferSize() const throw( SocketException ){
try{
int value;
checkResult( apr_socket_opt_get( socketHandle, APR_SO_RCVBUF, &value ) );
return value;
}
DECAF_CATCH_RETHROW( SocketException )
DECAF_CATCHALL_THROW( SocketException )
}
////////////////////////////////////////////////////////////////////////////////
void TcpSocket::setReceiveBufferSize( int size ) throw( SocketException ){
try{
checkResult( apr_socket_opt_set( socketHandle, APR_SO_RCVBUF, size ) );
}
DECAF_CATCH_RETHROW( SocketException )
DECAF_CATCHALL_THROW( SocketException )
}
////////////////////////////////////////////////////////////////////////////////
bool TcpSocket::getReuseAddress() const throw( SocketException ){
try{
int value;
checkResult( apr_socket_opt_get( socketHandle, APR_SO_REUSEADDR, &value ) );
return value != 0;
}
DECAF_CATCH_RETHROW( SocketException )
DECAF_CATCHALL_THROW( SocketException )
}
////////////////////////////////////////////////////////////////////////////////
void TcpSocket::setReuseAddress( bool reuse ) throw( SocketException ){
try{
int value = reuse ? 1 : 0;
checkResult( apr_socket_opt_set( socketHandle, APR_SO_REUSEADDR, value ) );
}
DECAF_CATCH_RETHROW( SocketException )
DECAF_CATCHALL_THROW( SocketException )
}
////////////////////////////////////////////////////////////////////////////////
int TcpSocket::getSendBufferSize() const throw( SocketException ){
try{
int value;
checkResult( apr_socket_opt_get( socketHandle, APR_SO_SNDBUF, &value ) );
return value;
}
DECAF_CATCH_RETHROW( SocketException )
DECAF_CATCHALL_THROW( SocketException )
}
////////////////////////////////////////////////////////////////////////////////
void TcpSocket::setSendBufferSize( int size ) throw( SocketException ){
try{
checkResult( apr_socket_opt_set( socketHandle, APR_SO_SNDBUF, size ) );
}
DECAF_CATCH_RETHROW( SocketException )
DECAF_CATCHALL_THROW( SocketException )
}
////////////////////////////////////////////////////////////////////////////////
void TcpSocket::setSoTimeout ( const int millisecs ) throw ( SocketException )
{
try{
// Time is in microseconds so multiply by 1000.
checkResult( apr_socket_timeout_set( socketHandle, millisecs * 1000 ) );
}
DECAF_CATCH_RETHROW( SocketException )
DECAF_CATCHALL_THROW( SocketException )
}
////////////////////////////////////////////////////////////////////////////////
int TcpSocket::getSoTimeout() const throw( SocketException )
{
try{
// Time is in microseconds so divide by 1000.
apr_interval_time_t value = 0;
checkResult( apr_socket_timeout_get( socketHandle, &value ) );
return value / 1000;
}
DECAF_CATCH_RETHROW( SocketException )
DECAF_CATCHALL_THROW( SocketException )
}
////////////////////////////////////////////////////////////////////////////////
bool TcpSocket::getTcpNoDelay() const throw ( lang::Exception ) {
try{
int value;
checkResult( apr_socket_opt_get( socketHandle, APR_TCP_NODELAY, &value ) );
return value != 0;
}
DECAF_CATCH_RETHROW( SocketException )
DECAF_CATCHALL_THROW( SocketException )
}
////////////////////////////////////////////////////////////////////////////////
void TcpSocket::setTcpNoDelay( bool value ) throw ( lang::Exception ) {
try{
int ivalue = value ? 1 : 0;
checkResult( apr_socket_opt_set( socketHandle, APR_TCP_NODELAY, ivalue ) );
}
DECAF_CATCH_RETHROW( SocketException )
DECAF_CATCHALL_THROW( SocketException )
}
////////////////////////////////////////////////////////////////////////////////
void TcpSocket::checkResult( apr_status_t value ) const throw (SocketException) {
if( value != APR_SUCCESS ){
throw SocketException(
__FILE__, __LINE__,
SocketError::getErrorString().c_str() );
}
}