lw-support/src/liblw-support/Net/TCP/Client.cpp

202 lines
4.4 KiB
C++

/* lw-support/src/lib/Net/TCP/Client.cpp
*
* (c)2005, Laurence Withers. Released under the GNU GPL. See file
* COPYING for more information / terms of license.
*/
namespace lw {
NetClientTCP::NetClientTCP()
: sockAddr(0), peerAddr(0)
{
}
void NetClientTCP::clearAddr()
{
delete sockAddr;
sockAddr = 0;
delete peerAddr;
peerAddr = 0;
}
NetClientTCP::~NetClientTCP()
{
clearAddr();
}
NetClientTCP::NetClientTCP(int fd, NetAddress* sockAddr, uint16_t sockPort,
NetAddress* peerAddr, uint16_t peerPort)
: sockAddr(sockAddr), peerAddr(peerAddr),
sockPort(sockPort), peerPort(peerPort)
{
this->fd = fd;
ioMode = IOReadWrite;
setNonBlocking();
}
void NetClientTCP::connect(const NetAddress& addr, uint16_t port, bool block)
{
try {
// clear old connection
clearAddr();
close();
// create a socket
fd = TEMP_FAILURE_RETRY(socket(PF_INET, SOCK_STREAM, IPPROTO_TCP));
if(fd == -1)
throw SystemError().chain(L"socket()");
ioMode = IOReadWrite;
// set non-blocking mode
if(!block) setNonBlocking();
// fill out addresses and connect
peerAddr = addr.clone();
peerPort = port;
// IPv4
if(!sockAddr) {
const NetAddressIPv4* a = dynamic_cast<const NetAddressIPv4*>(&addr);
if(a) {
struct sockaddr_in sa;
socklen_t sa_len = sizeof(sa);
// get address
if(TEMP_FAILURE_RETRY(getsockname(
fd, (struct sockaddr*)&sa, &sa_len)))
throw SystemError().chain(L"getsockname()");
sockAddr = new NetAddressIPv4((char*)&(sa.sin_addr.s_addr));
sockPort = sa.sin_port;
// connect
int ret = TEMP_FAILURE_RETRY(::connect(
fd, a->copy(&sa, port), sa_len));
if(ret == -1 && (block || errno != EINPROGRESS))
throw SystemError().chain(L"connect()");
}
}
// IPv6
if(!sockAddr) {
const NetAddressIPv6* a = dynamic_cast<const NetAddressIPv6*>(&addr);
if(a) {
struct sockaddr_in6 sa;
socklen_t sa_len = sizeof(sa);
// get address
if(TEMP_FAILURE_RETRY(getsockname(
fd, (struct sockaddr*)&sa, &sa_len)))
throw SystemError().chain(L"getsockname()");
sockAddr = new NetAddressIPv6(sa.sin6_addr.s6_addr);
sockPort = sa.sin6_port;
// connect
int ret = TEMP_FAILURE_RETRY(::connect(
fd, a->copy(&sa, port), sa_len));
if(ret == -1 && (block || errno != EINPROGRESS))
throw SystemError().chain(L"connect()");
}
}
// unknown
if(!sockAddr) {
throw NetServer::UnknownProtocol();
}
// if we were doing blocking I/O, set non-blocking mode now
setNonBlocking();
}
// deal with errors gracefully
catch(Exception& e) {
try {
close();
}
catch(...) { }
e.chain(L"NetClientTCP::connect()");
throw;
}
}
void NetClientTCP::connect(const NetAddress& addr, uint16_t port)
{
connect(addr, port, false);
}
void NetClientTCP::connectBlocking(const NetAddress& addr, uint16_t port)
{
connect(addr, port, true);
}
NetClientTCP::NetClientTCP(const NetAddress& addr, uint16_t port)
: sockAddr(0), peerAddr(0)
{
connect(addr, port, true);
}
uint16_t NetClientTCP::localPort() const
{
checkOpen(L"NetClientTCP::localPort()");
return sockPort;
}
uint16_t NetClientTCP::remotePort() const
{
checkOpen(L"NetClientTCP::remotePort()");
return peerPort;
}
const NetAddress& NetClientTCP::localAddr() const
{
checkOpen(L"NetClientTCP::localPort()");
return *sockAddr;
}
const NetAddress& NetClientTCP::remoteAddr() const
{
checkOpen(L"NetClientTCP::remotePort()");
return *peerAddr;
}
void NetClientTCP::setNoDelay(bool noDelay)
{
int n = noDelay;
if(setsockopt(fd, SOL_TCP, TCP_NODELAY, &n, sizeof(n)))
throw SystemError()
.chain(L"setsockopt(..., SOL_TCP, TCP_NODELAY, ...)")
.chain(L"NetClientTCP::setNoDelay()");
}
}