blob: 9ac64ae54b9d91a16b20278ee27d2a7332d6dcd1 [file] [log] [blame]
/***************************************************************************
socketimpl.cpp - description
-------------------
begin : ven mai 9 2003
copyright : (C) 2003 by Michael CATANZARITI
email : mcatan@free.fr
***************************************************************************/
/***************************************************************************
* Copyright (C) The Apache Software Foundation. All rights reserved. *
* *
* This software is published under the terms of the Apache Software *
* License version 1.1, a copy of which has been included with this *
* distribution in the LICENSE.txt file. *
***************************************************************************/
#include <log4cxx/config.h>
#ifdef WIN32
#include <windows.h>
#include <winsock.h>
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/time.h>
#include <sys/types.h>
#endif
#include <log4cxx/helpers/socketimpl.h>
#include <log4cxx/helpers/loglog.h>
#include <errno.h>
using namespace log4cxx;
using namespace log4cxx::helpers;
IMPLEMENT_LOG4CXX_OBJECT(SocketImpl)
#include <string.h>
#include <assert.h>
#ifdef WIN32
namespace {
class WinSockInitializer {
public:
WinSockInitializer() {
WSAStartup(MAKEWORD(1, 1), &wsa);
}
~WinSockInitializer() {
WSACleanup();
}
WSADATA wsa;
} winSockInitializer;
}
#endif
SocketException::SocketException()
{
#ifdef WIN32
TCHAR messageBuffer[256];
DWORD dwError = ::WSAGetLastError();
if (dwError != 0)
{
DWORD dw = ::FormatMessage(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_MAX_WIDTH_MASK,
NULL,
dwError,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
messageBuffer,
sizeof(messageBuffer)/sizeof(messageBuffer[0]),
NULL);
if (dw != 0)
{
message = messageBuffer;
// on retire les retours chariots en fin de chaƮne
message = message.substr(0, message.find_last_not_of(_T("\n\r")) + 1);
}
else
{
itot(::WSAGetLastError(), messageBuffer, 10);
message = messageBuffer;
}
}
#else
USES_CONVERSION;
message = A2T(strerror(errno));
#endif
};
SocketImpl::SocketImpl() : fd(0), localport(-1), port(0), timeout(-1)
{
}
SocketImpl::~SocketImpl()
{
try
{
close();
}
catch(SocketException&)
{
}
}
/** Accepts a connection. */
void SocketImpl::accept(SocketImplPtr s)
{
sockaddr_in client_addr;
#ifdef WIN32
int client_len;
#else
socklen_t client_len;
#endif
client_len = sizeof(client_addr);
if (timeout > 0)
{
// convert timeout in milliseconds to struct timeval
timeval tv;
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(this->fd, &rfds);
int retval = ::select(this->fd+1, &rfds, NULL, NULL, &tv);
if (retval == 0)
{
throw SocketTimeoutException();
}
assert(FD_ISSET(this->fd, &rfds));
}
int fdClient = ::accept(this->fd, (sockaddr *)&client_addr, &client_len);
if (fdClient < 0)
{
throw SocketException();
}
s->address.address = ntohl(client_addr.sin_addr.s_addr);
s->fd = fdClient;
s->port = ntohs(client_addr.sin_port);
}
/** Returns the number of bytes that can be read from this socket
without blocking.
*/
int SocketImpl::available()
{
// TODO
return 0;
}
/** Binds this socket to the specified port number
on the specified host.
*/
void SocketImpl::bind(InetAddress host, int port)
{
struct sockaddr_in server_addr;
int server_len = sizeof(server_addr);
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(host.address);
server_addr.sin_port = htons(port);
if (::bind(fd, (sockaddr *)&server_addr, server_len) == -1)
{
throw BindException();
}
this->localport = port;
}
/** Closes this socket. */
void SocketImpl::close()
{
if (fd != 0)
{
LOGLOG_DEBUG(_T("closing socket"));
#ifdef WIN32
if (::closesocket(fd) == -1)
#else
if (::close(fd) == -1)
#endif
{
throw SocketException();
}
address.address = 0;
fd = 0;
port = 0;
localport = -1;
}
}
/** Connects this socket to the specified port number
on the specified host.
*/
void SocketImpl::connect(InetAddress address, int port)
{
sockaddr_in client_addr;
int client_len = sizeof(client_addr);
client_addr.sin_family = AF_INET;
client_addr.sin_addr.s_addr = htonl(address.address);
client_addr.sin_port = htons(port);
if (::connect(fd, (sockaddr *)&client_addr, client_len) == -1)
{
throw ConnectException();
}
this->address = address;
this->port = port;
}
/** Connects this socket to the specified port on the named host. */
void SocketImpl::connect(const String& host, int port)
{
connect(InetAddress::getByName(host), port);
}
/** Creates either a stream or a datagram socket. */
void SocketImpl::create(bool stream)
{
if ((fd = ::socket(AF_INET, stream ? SOCK_STREAM : SOCK_DGRAM, 0)) == -1)
{
throw SocketException();
}
}
/** Sets the maximum queue length for incoming connection
indications (a request to connect) to the count argument.
*/
void SocketImpl::listen(int backlog)
{
if (::listen(fd, backlog) == -1)
{
throw SocketException();
}
}
/** Returns the address and port of this socket as a String.
*/
String SocketImpl::toString() const
{
StringBuffer oss;
oss << address.getHostAddress() << _T(":") << port;
return oss.str();
}
// thanks to Yves Mettier (ymettier@libertysurf.fr) for this routine
size_t SocketImpl::read(void * buf, size_t len) const
{
// LOGLOG_DEBUG(_T("SocketImpl::reading ") << len << _T(" bytes."));
int len_read = 0;
unsigned char * p = (unsigned char *)buf;
while ((size_t)(p - (unsigned char *)buf) < len)
{
#ifdef WIN32
len_read = ::recv(fd, (char *)p, len - (p - (unsigned char *)buf), 0);
#else
len_read = ::read(fd, p, len - (p - (unsigned char *)buf));
#endif
if (len_read < 0)
{
throw SocketException();
}
if (len_read == 0)
{
break;
}
p += len_read;
}
return (p - (const unsigned char *)buf);
}
// thanks to Yves Mettier (ymettier@libertysurf.fr) for this routine
size_t SocketImpl::write(const void * buf, size_t len)
{
// LOGLOG_DEBUG(_T("SocketImpl::writing ") << len << _T(" bytes."));
int len_written = 0;
const unsigned char * p = (const unsigned char *)buf;
while ((size_t)(p - (const unsigned char *)buf) < len)
{
#ifdef WIN32
len_written = ::send(fd, (const char *)p, len - (p - (const unsigned char *)buf), 0);
#else
len_written = ::write(fd, p, len - (p - (const unsigned char *)buf));
#endif
if (len_written < 0)
{
throw SocketException();
}
if (len_written == 0)
{
break;
}
p += len_written;
}
return (p - (const unsigned char *)buf);
}
/** Retrive setting for SO_TIMEOUT.
*/
int SocketImpl::getSoTimeout() const
{
return timeout;
}
/** Enable/disable SO_TIMEOUT with the specified timeout, in milliseconds.
*/
void SocketImpl::setSoTimeout(int timeout)
{
this->timeout = timeout;
}