mirror of
https://bitbucket.org/theswgsource/src-1.2.git
synced 2026-09-28 13:12:56 -04:00
Added sharedNetwork library
This commit is contained in:
@@ -0,0 +1,372 @@
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
#include "FirstSharedNetwork.h"
|
||||
#include "Address.h"
|
||||
#include <cassert>
|
||||
#include <netdb.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <cstdio>
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
Address::Address() :
|
||||
addr4(new struct sockaddr_in),
|
||||
hostAddress("0.0.0.0")
|
||||
{
|
||||
memset(addr4, 0, sizeof(struct sockaddr_in));
|
||||
addr4->sin_family = AF_INET;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
Address::Address(const std::string & newHostAddress, unsigned short newHostPort) :
|
||||
addr4(new struct sockaddr_in),
|
||||
hostAddress(newHostAddress)
|
||||
{
|
||||
struct hostent * h;
|
||||
unsigned long u;
|
||||
|
||||
memset(addr4, 0, sizeof(struct sockaddr_in));
|
||||
addr4->sin_port = htons(newHostPort);
|
||||
addr4->sin_family = AF_INET;
|
||||
// was an address supplied?
|
||||
if(hostAddress.size() > 0)
|
||||
{
|
||||
// Is the first byte a number? (IP names begin with an alpha)
|
||||
if(!isdigit(hostAddress[0]))
|
||||
{
|
||||
// The first byte is a letter, resolve it
|
||||
if( (h = gethostbyname(hostAddress.c_str())) != 0)
|
||||
{
|
||||
memcpy(&addr4->sin_addr, h->h_addr_list[0], sizeof(addr4->sin_addr));
|
||||
}
|
||||
else
|
||||
{
|
||||
// boom! grab the entry from the h_addr member instead!
|
||||
if( (h = gethostbyname(hostAddress.c_str())) != 0)
|
||||
{
|
||||
memcpy(&addr4->sin_addr, h->h_addr, sizeof(addr4->sin_addr));
|
||||
}
|
||||
else
|
||||
{
|
||||
// no resolution, INADDR_ANY
|
||||
memset(&addr4->sin_addr, 0, sizeof(addr4->sin_addr));
|
||||
|
||||
// in debug, fail, something is wrong.
|
||||
// potential problems - an insanely bogus address was
|
||||
// passed or the host system is misconfigured and cannot
|
||||
// resolve the name
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
char addrbuf[17] = {"\0"};
|
||||
unsigned char * a = (unsigned char *)&addr4->sin_addr;
|
||||
snprintf(addrbuf, sizeof(addrbuf), "%u.%u.%u.%u", a[0], a[1], a[2], a[3]);
|
||||
hostAddress = addrbuf;
|
||||
}
|
||||
else
|
||||
{
|
||||
// A dotted decimal ip number string was supplied. Convert for sin_addr
|
||||
u = inet_addr(hostAddress.c_str());
|
||||
memcpy(&addr4->sin_addr, &u, sizeof(addr4->sin_addr));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// nothing was supplied, assign INADDR_ANY
|
||||
addr4->sin_addr.s_addr = INADDR_ANY;
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
Address::Address(const Address & source) :
|
||||
addr4(new struct sockaddr_in),
|
||||
hostAddress(source.hostAddress)
|
||||
{
|
||||
*addr4 = *source.addr4;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
Address::Address(const struct sockaddr_in & ipv4addr) :
|
||||
addr4(new struct sockaddr_in),
|
||||
hostAddress("")
|
||||
{
|
||||
convertFromSockAddr(ipv4addr);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
Address::~Address()
|
||||
{
|
||||
delete addr4;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
Address & Address::operator = (const Address & rhs)
|
||||
{
|
||||
if(this != &rhs)
|
||||
{
|
||||
hostAddress = rhs.hostAddress;
|
||||
*addr4 = *rhs.addr4;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
Address & Address::operator = (const struct sockaddr_in & rhs)
|
||||
{
|
||||
convertFromSockAddr(rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
void Address::convertFromSockAddr(const struct sockaddr_in & source)
|
||||
{
|
||||
// extract IP bytes from ipv4add4
|
||||
const unsigned char * ip;
|
||||
char name[17] = {"\0"};
|
||||
|
||||
ip = reinterpret_cast<const unsigned char *>(&source.sin_addr);
|
||||
snprintf(name, 17, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]); //lint !e534
|
||||
hostAddress = name;
|
||||
*addr4 = source;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
/**
|
||||
@brief get a human readable host address
|
||||
|
||||
Example:
|
||||
\code
|
||||
void foo(struct sockaddr_in & a)
|
||||
{
|
||||
Address b(a);
|
||||
printf("address = %%s\\n", b.getHostAddress().c_str());
|
||||
}
|
||||
\endcode
|
||||
|
||||
@return A human readable host address string
|
||||
|
||||
@author Justin Randall
|
||||
*/
|
||||
const std::string & Address::getHostAddress() const
|
||||
{
|
||||
return hostAddress;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
/**
|
||||
@brief get the port associated with this address
|
||||
|
||||
Example:
|
||||
\code
|
||||
void foo(struct sockaddr_in & a)
|
||||
{
|
||||
Address b(a);
|
||||
printf("port = %%i\\n", b.getHostPort());
|
||||
}
|
||||
\endcode
|
||||
|
||||
@return A human readable port in host-byte order associated with
|
||||
this address.
|
||||
|
||||
@author Justin Randall
|
||||
*/
|
||||
const unsigned short Address::getHostPort() const
|
||||
{
|
||||
return ntohs(addr4->sin_port);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
/**
|
||||
@brief get the BSD sockaddr describing this address
|
||||
|
||||
Example:
|
||||
\code
|
||||
void foo(SOCKET s, unsigned char * d, int l, const Address & a)
|
||||
{
|
||||
int t = sizeof(struct sockaddr_in);
|
||||
sendto(s, s, l, 0, reinterpret_cast<const struct sockaddr *>(&(a.getSockAddr4())), t);
|
||||
}
|
||||
\endcode
|
||||
|
||||
@return a BSD sockaddr that describes this IPv4 address
|
||||
|
||||
@author Justin Randall
|
||||
*/
|
||||
const struct sockaddr_in & Address::getSockAddr4() const
|
||||
{
|
||||
return *addr4;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
/**
|
||||
@brief equality operator
|
||||
|
||||
The equality operator compares the ip address, ip port,
|
||||
and address family to establish equality.
|
||||
|
||||
Example:
|
||||
\code
|
||||
Address a("127.0.0.1", 55443);
|
||||
Address b;
|
||||
|
||||
b = a;
|
||||
assert(b == a);
|
||||
\endcode
|
||||
|
||||
@return True of the right hand side is equal to this address
|
||||
|
||||
@author Justin Randall
|
||||
*/
|
||||
const bool Address::operator == (const Address & rhs) const
|
||||
{
|
||||
return (addr4->sin_addr.s_addr == rhs.addr4->sin_addr.s_addr &&
|
||||
addr4->sin_family == rhs.addr4->sin_family &&
|
||||
addr4->sin_port == rhs.addr4->sin_port);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
/**
|
||||
@brief less-than comparison operator
|
||||
|
||||
The < comparison operator compares the IP number and port. If
|
||||
the IP numbers are identical, but the left hand side port is
|
||||
less than the right hand side port, the operator will return
|
||||
true.
|
||||
|
||||
@return true if the left hand side's IP number is less than
|
||||
the right hand side IP number. If the numbers are equal, it
|
||||
will return true if the left hand side IP port is less
|
||||
than the right hand side port. Otherwise it returns false.
|
||||
|
||||
@author Justin Randall
|
||||
*/
|
||||
const bool Address::operator < (const Address & rhs) const
|
||||
{
|
||||
return(addr4->sin_addr.s_addr < rhs.addr4->sin_addr.s_addr ||
|
||||
addr4->sin_addr.s_addr == rhs.addr4->sin_addr.s_addr &&
|
||||
addr4->sin_port < rhs.addr4->sin_port);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
/**
|
||||
@brief inequality operator
|
||||
|
||||
Leverages the equality operator, so whenever == returns true,
|
||||
this returns false, and visa versa.
|
||||
|
||||
@return true if the right hand side is not equal to the left
|
||||
hand side. False if they are equal.
|
||||
|
||||
@see Adress::operator==
|
||||
|
||||
@author Justin Randall
|
||||
*/
|
||||
const bool Address::operator != (const Address & rhs) const
|
||||
{
|
||||
return(! (rhs == *this));
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
/**
|
||||
@brief greater-than comparison operator
|
||||
|
||||
The > comparison operator compares the IP number and port. If
|
||||
the IP numbers are identical, but the right hand side port is
|
||||
lesser than the left hand side port, the operator will return
|
||||
true.
|
||||
|
||||
@return true if the left hand side's IP number is greater than
|
||||
the right hand side IP number. If the numbers are equal, it
|
||||
will return true if the left hand side IP port is greater
|
||||
than the right hand side port. Otherwise it returns false.
|
||||
|
||||
@author Justin Randall
|
||||
*/
|
||||
const bool Address::operator > (const Address & rhs) const
|
||||
{
|
||||
return(addr4->sin_addr.s_addr > rhs.addr4->sin_addr.s_addr ||
|
||||
addr4->sin_addr.s_addr == rhs.addr4->sin_addr.s_addr &&
|
||||
addr4->sin_port > rhs.addr4->sin_port);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
/**
|
||||
@brief a hash_map support routine
|
||||
|
||||
The STL hash_map (present in most STL implementations) requires
|
||||
a size_t return from a hash function to identify which bucket
|
||||
a particular value should reside in. On 32 bit or better platforms
|
||||
the sockaddr_in.sin_addr.s_addr member is small enough to
|
||||
qualify as a hash-result, provides reasonably unique values
|
||||
and is reproducable given an address input.
|
||||
|
||||
Example:
|
||||
\code
|
||||
typedef std::hash_map<Address, Connection *, Address::HashFunction, Address::EqualFunction> AddressMap;
|
||||
\endcode
|
||||
|
||||
@return the ip number member of a sockaddr_in struct
|
||||
|
||||
@author Justin Randall
|
||||
*/
|
||||
size_t Address::hashFunction() const
|
||||
{
|
||||
return addr4->sin_addr.s_addr;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
/**
|
||||
@brief STL map support routine
|
||||
|
||||
STL maps (including hash_maps) require unique keys, and therefore
|
||||
need to compare a key for equality with an existing target.
|
||||
|
||||
The functor uses Address::operator = for the comparison.
|
||||
|
||||
Example:
|
||||
\code
|
||||
typedef std::hash_map<Address, Connection *, Address::HashFunction, Address::EqualFunction> AddressMap;
|
||||
\endcode
|
||||
|
||||
@return true if the left hand side and right hand side are equal
|
||||
using Address::operator =
|
||||
@see Address::operator=
|
||||
|
||||
*/
|
||||
bool Address::EqualFunction::operator () (const Address & lhs, const Address & rhs) const
|
||||
{
|
||||
return lhs == rhs;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
/**
|
||||
@brief STL hash_map support routine
|
||||
|
||||
The HashFunction::operator() invokes Address::hashFunction to
|
||||
determine an appropriate hash for the address.
|
||||
|
||||
Example:
|
||||
\code
|
||||
typedef std::hash_map<Address, Connection *, Address::HashFunction, Address::EqualFunction> AddressMap;
|
||||
\endcode
|
||||
|
||||
@see Address::hashFunction
|
||||
|
||||
@author Justin Randall
|
||||
*/
|
||||
size_t Address::HashFunction::operator () (const Address & a) const
|
||||
{
|
||||
return a.hashFunction();
|
||||
}
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
// NetworkGetHostName.cpp
|
||||
// Copyright 2000-02, Sony Online Entertainment Inc., all rights reserved.
|
||||
// Author: Justin Randall
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
#include "FirstSharedNetwork.h"
|
||||
#include "Address.h"
|
||||
#include "sharedNetwork/NetworkHandler.h"
|
||||
#include <net/if.h>
|
||||
#include <unistd.h>
|
||||
#include <string>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
struct HN
|
||||
{
|
||||
HN();
|
||||
std::string hostName;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
HN::HN()
|
||||
{
|
||||
char name[512] = {"\0"};
|
||||
if(gethostname(name, sizeof(name)) == 0)
|
||||
{
|
||||
Address a(name, 0);
|
||||
//hostName = name;
|
||||
hostName = a.getHostAddress();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
const std::string & NetworkHandler::getHostName()
|
||||
{
|
||||
static HN hn;
|
||||
return hn.hostName;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
const std::string & NetworkHandler::getHumanReadableHostName()
|
||||
{
|
||||
char name[512] = {"\0"};
|
||||
static std::string nameString;
|
||||
if(nameString.empty())
|
||||
{
|
||||
if(gethostname(name, sizeof(name)) == 0)
|
||||
{
|
||||
name[sizeof(name) - 1] = 0;
|
||||
//hostName = name;
|
||||
nameString = name;
|
||||
}
|
||||
}
|
||||
return nameString;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
struct HL
|
||||
{
|
||||
HL();
|
||||
std::vector<std::pair<std::string, std::string> > addrList;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
HL::HL() :
|
||||
addrList()
|
||||
{
|
||||
struct ifconf ifc;
|
||||
int sock = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
|
||||
// create a socket to perform SIOCGIFCONF on
|
||||
if(sock > -1)
|
||||
{
|
||||
// determine buffer size to receive array of if_req's, initialize it
|
||||
// when length is appropriate
|
||||
int reqSize = 100 * sizeof(struct ifreq);
|
||||
int lastReqSize = reqSize;
|
||||
for(;;)
|
||||
{
|
||||
char * buf = new char[reqSize];
|
||||
ifc.ifc_len = reqSize;
|
||||
ifc.ifc_buf = buf;
|
||||
memset(ifc.ifc_buf, 0, reqSize);
|
||||
if (ioctl(sock, SIOCGIFCONF, &ifc) < 0)
|
||||
{
|
||||
if (errno != EINVAL || lastReqSize != 0)
|
||||
{
|
||||
WARNING(true, ("Error getting interface list: %s", strerror(errno)));
|
||||
delete buf;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(ifc.ifc_len == lastReqSize)
|
||||
break; // we have the reqs
|
||||
lastReqSize = ifc.ifc_len;
|
||||
}
|
||||
|
||||
// need more buffer space for request
|
||||
reqSize += sizeof(struct ifreq);
|
||||
delete [] buf;
|
||||
}
|
||||
|
||||
int reqCount = reqSize / sizeof(struct ifreq);
|
||||
int i;
|
||||
ifreq * reqs = (struct ifreq *)ifc.ifc_buf;
|
||||
for(i = 0; i < reqCount; ++i)
|
||||
{
|
||||
struct sockaddr_in * sa = (struct sockaddr_in *)&reqs[i].ifr_addr;
|
||||
if(sa->sin_family == AF_INET)
|
||||
{
|
||||
char addrbuf[17] = {"\0"};
|
||||
unsigned char * a = (unsigned char *)&sa->sin_addr;
|
||||
snprintf(addrbuf, sizeof(addrbuf), "%u.%u.%u.%u", a[0], a[1], a[2], a[3]);
|
||||
addrList.push_back(std::make_pair(std::string(reqs[i].ifr_name), std::string(addrbuf)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
const std::vector<std::pair<std::string, std::string> > & NetworkHandler::getInterfaceAddresses()
|
||||
{
|
||||
static HL hl;
|
||||
return hl.addrList;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
@@ -0,0 +1,236 @@
|
||||
//---------------------------------------------------------------------
|
||||
#pragma warning(disable : 4127)
|
||||
|
||||
#include "FirstSharedNetwork.h"
|
||||
#include <cassert>
|
||||
#include "Sock.h"
|
||||
#include <netdb.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
const int INVALID_SOCKET = 0xFFFFFFFF;
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
Sock::Sock() :
|
||||
handle(INVALID_SOCKET),
|
||||
lastError(Sock::SOCK_NO_ERROR),
|
||||
bindAddress()
|
||||
{
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
Sock::~Sock()
|
||||
{
|
||||
// ensure we don't block, and that pending
|
||||
// data is sent with a graceful shutdown
|
||||
int err;
|
||||
err = close(handle);
|
||||
assert(err == 0);
|
||||
handle = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
/**
|
||||
@brief Bind the socket to the specified local address
|
||||
*/
|
||||
bool Sock::bind(const Address & newBindAddress)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
int enable = 1;
|
||||
setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable));
|
||||
|
||||
bindAddress = newBindAddress;
|
||||
assert(handle != INVALID_SOCKET);
|
||||
socklen_t namelen = sizeof(struct sockaddr_in);
|
||||
int err = ::bind(handle, reinterpret_cast<const struct sockaddr *>(&(bindAddress.getSockAddr4())), namelen);
|
||||
if(err == 0)
|
||||
{
|
||||
result = true;
|
||||
struct sockaddr_in a;
|
||||
int r;
|
||||
r = getsockname(handle, reinterpret_cast<struct sockaddr *>(&a), &namelen);
|
||||
assert(r == 0);
|
||||
bindAddress = a;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
|
||||
assert(err == 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
bool Sock::bind()
|
||||
{
|
||||
bool result = false;
|
||||
struct sockaddr_in a;
|
||||
socklen_t namelen = sizeof(struct sockaddr_in);
|
||||
memset(&a, 0, sizeof(struct sockaddr_in));
|
||||
a.sin_family = AF_INET;
|
||||
a.sin_port = 0;
|
||||
a.sin_addr.s_addr = INADDR_ANY;
|
||||
assert(handle != INVALID_SOCKET);
|
||||
int err = ::bind(handle, reinterpret_cast<struct sockaddr *>(&a), namelen);
|
||||
if(err == 0)
|
||||
{
|
||||
result = true;
|
||||
int r;
|
||||
r = getsockname(handle, reinterpret_cast<struct sockaddr *>(&a), &namelen);
|
||||
assert(r == 0);
|
||||
bindAddress = a;
|
||||
}
|
||||
assert(err == 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
bool Sock::canRecv() const
|
||||
{
|
||||
pollfd r;
|
||||
r.fd = handle;
|
||||
r.events = POLLIN | POLLPRI | POLLERR;
|
||||
|
||||
int err = poll(&r, 1, 0);
|
||||
|
||||
if(err < 0)
|
||||
{
|
||||
switch(errno)
|
||||
{
|
||||
case EINTR: // the system has interrupted the call, it's too busy to send right now
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (err > 0);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
bool Sock::canSend() const
|
||||
{
|
||||
pollfd w;
|
||||
w.fd = handle;
|
||||
w.events = POLLOUT | POLLERR;
|
||||
int err = poll(&w, 1, 0);
|
||||
|
||||
if(err < 0)
|
||||
{
|
||||
switch(errno)
|
||||
{
|
||||
case EINTR: // the system has interrupted the call, it's too busy to send right now
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (err > 0);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
const unsigned int Sock::getInputBytesPending() const
|
||||
{
|
||||
unsigned long int bytes = 0;
|
||||
int err;
|
||||
err = ioctl(handle, FIONREAD, &bytes); //lint !e1924 (I don't know WHAT Microsoft is doing here!)
|
||||
assert(err == 0);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
const std::string Sock::getLastError() const
|
||||
{
|
||||
std::string errString;
|
||||
|
||||
switch(errno)
|
||||
{
|
||||
case EBADF:
|
||||
errString = "The argument s is not a valid descriptor.";
|
||||
break;
|
||||
case ENOTSOCK:
|
||||
errString = "The argument s is a file, not a socket.";
|
||||
break;
|
||||
case ENOPROTOOPT:
|
||||
errString = "The option is unknown at the level indicated.";
|
||||
break;
|
||||
case EFAULT:
|
||||
errString = "The address pointed to by optval is not in a valid part of the process address space. For getsockopt, this error may also be returned if optlen is not in a valid part of the process address space.";
|
||||
break;
|
||||
case ENOTTY:
|
||||
errString = "The scoket handle is not associated with a character special device.";
|
||||
break;
|
||||
case EINVAL:
|
||||
errString = "argument is not valid.";
|
||||
break;
|
||||
case EAGAIN:
|
||||
errString = "EAGAIN";
|
||||
break;
|
||||
case EDEADLK:
|
||||
errString = "EDEADLK";
|
||||
break;
|
||||
case ECONNRESET:
|
||||
errString = "The connection was reset by the peer.";
|
||||
lastError = CONNECTION_RESET;
|
||||
break;
|
||||
default:
|
||||
errString = strerror(errno);
|
||||
break;
|
||||
}
|
||||
|
||||
return errString;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
void Sock::getPeerName(struct sockaddr_in & target, int s)
|
||||
{
|
||||
socklen_t namelen = sizeof(struct sockaddr_in);
|
||||
int err;
|
||||
err = getpeername(s, reinterpret_cast<sockaddr *>(&(target)), &namelen);
|
||||
assert(err != -1);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/** @brief determine the maximum message size that may be sent on this socket
|
||||
*/
|
||||
const unsigned int Sock::getMaxMessageSendSize() const
|
||||
{
|
||||
int maxMsgSize = 1500;
|
||||
/** @todo get msg size via IP interface on Linux
|
||||
int optlen = sizeof(int);
|
||||
int result = getsockopt(handle, SOL_SOCKET, SO_MAX_MSG_SIZE, reinterpret_cast<char *>(&maxMsgSize), &optlen);
|
||||
if(result != 0)
|
||||
{
|
||||
perror("Sock::getMaxMessageSendSize() : getsockopt()");
|
||||
}
|
||||
*/
|
||||
return static_cast<unsigned int>(maxMsgSize);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
void Sock::setNonBlocking() const
|
||||
{
|
||||
assert(handle != INVALID_SOCKET);
|
||||
unsigned long int nb = 1;
|
||||
int err;
|
||||
err = ioctl(handle, FIONBIO, &nb); //lint !e569 // loss of precision in the FIONBIO macro, beyond my control
|
||||
assert(err == 0);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// Sock.h
|
||||
//
|
||||
// Copyright 2003 Sony Online Entertainment
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#ifndef INCLUDED_Sock_H
|
||||
#define INCLUDED_Sock_H
|
||||
|
||||
// ======================================================================
|
||||
|
||||
#include "sharedNetwork/Address.h"
|
||||
|
||||
// ======================================================================
|
||||
|
||||
const unsigned int SOCK_ERROR = 0xFFFFFFFF;
|
||||
|
||||
/**
|
||||
@brief a BSD socket abstraction
|
||||
|
||||
Sock abstracts BSD sockets for platform independant operation. It
|
||||
also provides common socket operations to simplify socket management.
|
||||
|
||||
@see BroadcastSock
|
||||
@see TcpSock
|
||||
@see UdpSock
|
||||
|
||||
@author Justin Randall
|
||||
*/
|
||||
class Sock
|
||||
{
|
||||
public:
|
||||
/**
|
||||
@brief failure states for a socket
|
||||
*/
|
||||
enum ErrorCodes
|
||||
{
|
||||
SOCK_NO_ERROR,
|
||||
CONNECTION_FAILED,
|
||||
CONNECTION_CLOSED,
|
||||
CONNECTION_RESET
|
||||
};
|
||||
|
||||
Sock();
|
||||
virtual ~Sock() = 0;
|
||||
bool bind(const Address & bindAddress);
|
||||
bool bind();
|
||||
bool canSend() const;
|
||||
bool canRecv() const;
|
||||
const Address & getBindAddress() const;
|
||||
const int getHandle() const;
|
||||
const unsigned int getInputBytesPending() const;
|
||||
const std::string getLastError() const;
|
||||
const enum ErrorCodes getLastErrorCode() const;
|
||||
const unsigned int getMaxMessageSendSize() const;
|
||||
static void getPeerName(struct sockaddr_in & target, int s);
|
||||
|
||||
private:
|
||||
// disabled
|
||||
Sock(const Sock & source);
|
||||
Sock & operator= (const Sock & source);
|
||||
|
||||
protected:
|
||||
void setNonBlocking() const;
|
||||
protected:
|
||||
int handle;
|
||||
|
||||
/**
|
||||
@brief support for setting/getting last error from derived
|
||||
sock classes
|
||||
*/
|
||||
mutable enum ErrorCodes lastError;
|
||||
private:
|
||||
Address bindAddress;
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
/**
|
||||
@brief return the local address of the socket
|
||||
|
||||
Until a socket is bound, the bind address may be reported as
|
||||
0.0.0.0:0
|
||||
|
||||
@return a const Address reference describing the local address
|
||||
of the socket.
|
||||
|
||||
@author Justin Randall
|
||||
*/
|
||||
inline const Address & Sock::getBindAddress() const
|
||||
{
|
||||
return bindAddress;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
/**
|
||||
@brief return the platform specific socket handle
|
||||
|
||||
the handle returned is not portable and should only be used locally
|
||||
for Sock specific operations.
|
||||
|
||||
@author Justin Randall
|
||||
*/
|
||||
inline const int Sock::getHandle() const
|
||||
{
|
||||
return handle;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
/**
|
||||
@brief get the last error code on the socket
|
||||
|
||||
@return the last error code on the socket
|
||||
|
||||
@see Sock::ErrorCodes
|
||||
|
||||
@author Justin Randall
|
||||
*/
|
||||
inline const enum Sock::ErrorCodes Sock::getLastErrorCode() const
|
||||
{
|
||||
return lastError;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
#endif // _Sock_H
|
||||
|
||||
@@ -0,0 +1,610 @@
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
#include "sharedNetwork/FirstSharedNetwork.h"
|
||||
#include "TcpClient.h"
|
||||
#include "TcpServer.h"
|
||||
|
||||
#include "Archive/Archive.h"
|
||||
#include "sharedFoundation/Clock.h"
|
||||
#include "sharedFoundation/Os.h"
|
||||
#include "sharedLog/Log.h"
|
||||
#include "sharedLog/NetLogConnection.h"
|
||||
#include "sharedNetwork/Connection.h"
|
||||
#include "sharedNetwork/ConfigSharedNetwork.h"
|
||||
#include <map>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <unistd.h>
|
||||
#include <netdb.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
const unsigned long KEEPALIVE_MS = 1000;
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
namespace TcpClientNamespace
|
||||
{
|
||||
std::set<TcpClient *> s_pendingConnectionSends;
|
||||
std::set<TcpClient *> s_tcpClients;
|
||||
bool s_installed = false;
|
||||
}
|
||||
|
||||
using namespace TcpClientNamespace;
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
TcpClient::TcpClient (int sockHandle, TcpServer * server) :
|
||||
m_socket(sockHandle),
|
||||
m_tcpServer(server),
|
||||
m_pendingSend(),
|
||||
m_connection(0),
|
||||
m_recvBuffer(0),
|
||||
m_recvBufferLength(0),
|
||||
m_remoteAddress(),
|
||||
m_refCount(0),
|
||||
m_connected (true),
|
||||
m_lastSendTime(0),
|
||||
m_bindPort(0),
|
||||
m_rawTCP( false )
|
||||
{
|
||||
s_tcpClients.insert(this);
|
||||
setSockOptions();
|
||||
|
||||
struct sockaddr_in target;
|
||||
socklen_t namelen = sizeof(struct sockaddr_in);
|
||||
if (getpeername(sockHandle, reinterpret_cast<sockaddr *>(&target), &namelen) == 0)
|
||||
m_remoteAddress = target;
|
||||
struct sockaddr_in bindAddr;
|
||||
|
||||
if(getsockname(sockHandle, reinterpret_cast<struct sockaddr *>(&bindAddr), &namelen) == 0)
|
||||
{
|
||||
m_bindPort = ntohs(bindAddr.sin_port);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
TcpClient::TcpClient (const std::string & a, const unsigned short port) :
|
||||
m_socket(-1),
|
||||
m_tcpServer(0),
|
||||
m_connection(0),
|
||||
m_recvBuffer(0),
|
||||
m_recvBufferLength(0),
|
||||
m_remoteAddress(a, port),
|
||||
m_refCount(0),
|
||||
m_connected (false),
|
||||
m_lastSendTime(0),
|
||||
m_bindPort(0),
|
||||
m_rawTCP( false )
|
||||
{
|
||||
FATAL(! s_installed, ("TcpClient is not installed!"));
|
||||
|
||||
s_tcpClients.insert(this);
|
||||
setupSocket();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
TcpClient::~TcpClient()
|
||||
{
|
||||
FATAL(m_refCount > 0, ("Attempting to destroy a TcpClient while references still exist!"));
|
||||
std::set<TcpClient *>::iterator f = s_tcpClients.find(this);
|
||||
if(f != s_tcpClients.end())
|
||||
s_tcpClients.erase(f);
|
||||
f = s_pendingConnectionSends.find(this);
|
||||
if(f != s_pendingConnectionSends.end())
|
||||
s_pendingConnectionSends.erase(f);
|
||||
|
||||
delete[] m_recvBuffer;
|
||||
if(m_socket != -1)
|
||||
close (m_socket);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
void TcpClient::addRef()
|
||||
{
|
||||
m_refCount++;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
std::string const &TcpClient::getRemoteAddress() const
|
||||
{
|
||||
return m_remoteAddress.getHostAddress();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
unsigned short TcpClient::getRemotePort() const
|
||||
{
|
||||
return m_remoteAddress.getHostPort();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
void TcpClient::setSockOptions()
|
||||
{
|
||||
int optval = 1;
|
||||
setsockopt(m_socket, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
|
||||
setsockopt(m_socket, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval));
|
||||
ioctl(m_socket, FIONBIO, &optval);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
|
||||
void TcpClient::commit()
|
||||
{
|
||||
int sent = 0;
|
||||
const unsigned char * ptr = m_pendingSend.getBuffer();
|
||||
const unsigned char * const end = ptr + m_pendingSend.getSize();
|
||||
static Archive::ByteStream s_localScratchBuffer;
|
||||
s_localScratchBuffer.clear();
|
||||
int retries = 0;
|
||||
static const int maxRetries = ConfigSharedNetwork::getMaxTCPRetries();
|
||||
bool sentSomething = false;
|
||||
|
||||
do
|
||||
{
|
||||
// attempt to push pending data to socket
|
||||
sent = ::send (m_socket, ptr, end - ptr, MSG_NOSIGNAL);
|
||||
if(sent == -1)
|
||||
{
|
||||
// failed
|
||||
switch(errno)
|
||||
{
|
||||
// because it would block on a non-blocking socket
|
||||
case EAGAIN:
|
||||
case ENOBUFS:
|
||||
case ENOMEM:
|
||||
case EINTR:
|
||||
{
|
||||
retries++;
|
||||
// requeue
|
||||
break;
|
||||
}
|
||||
// an unhandled error (connection reset, for example)
|
||||
default:
|
||||
{
|
||||
m_pendingSend.clear();
|
||||
perror("send: ");
|
||||
if (m_connection)
|
||||
m_connection->setDisconnectReason("TcpClient::commit send returned -1, errno=%d", errno);
|
||||
onConnectionClosed();
|
||||
return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(sent > 0)
|
||||
{
|
||||
sentSomething=true;
|
||||
ptr += sent;
|
||||
if(sent < end - ptr)
|
||||
{
|
||||
// data was passed to the socket, but not all
|
||||
// of it could be committed, keep trying!
|
||||
retries = 0;
|
||||
Os::sleep(1);
|
||||
}
|
||||
}
|
||||
else if(sent == 0)
|
||||
{
|
||||
// tried to send and got nothing through (should have returned an error, but be thorough)
|
||||
retries ++;
|
||||
Os::sleep(1);
|
||||
}
|
||||
|
||||
} while(ptr < end && retries < maxRetries);
|
||||
|
||||
if(ptr == end)
|
||||
{
|
||||
// all data was committed, clear pending buffer
|
||||
m_pendingSend.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
// bailed out before all data was committed, save unsent data for
|
||||
// next pass on commit()
|
||||
if (sentSomething)
|
||||
{
|
||||
s_localScratchBuffer.put(ptr, end - ptr);
|
||||
m_pendingSend.clear();
|
||||
m_pendingSend.put(s_localScratchBuffer.getBuffer(), s_localScratchBuffer.getSize());
|
||||
}
|
||||
s_pendingConnectionSends.insert(this);
|
||||
}
|
||||
|
||||
// clean up local accounting
|
||||
s_localScratchBuffer.clear();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
void TcpClient::flush()
|
||||
{
|
||||
if (m_connected && m_pendingSend.getSize() > 0)
|
||||
{
|
||||
// flush pending buffer
|
||||
commit();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
void TcpClient::flushPendingWrites()
|
||||
{
|
||||
std::set<TcpClient *> sends = s_pendingConnectionSends;
|
||||
s_pendingConnectionSends.clear();
|
||||
|
||||
for (std::set<TcpClient *>::const_iterator i = sends.begin(); i != sends.end(); ++i)
|
||||
{
|
||||
TcpClient * const c = *i;
|
||||
|
||||
if (c->m_connected)
|
||||
{
|
||||
c->flush();
|
||||
|
||||
if ( c->m_connection
|
||||
&& c->m_pendingSend.getSize()
|
||||
&& ConfigSharedNetwork::getLogSendingTooMuchData()
|
||||
&& !dynamic_cast<NetLogConnection const *>(c->m_connection))
|
||||
{
|
||||
WARNING(true, ("(%s) Failed to commit %d bytes this frame to %s:%d. This process is sending more data than the remote can receive!", c->m_connection->getConnectionDescription().c_str(), c->m_pendingSend.getSize(), c->getRemoteAddress().c_str(), c->getRemotePort()));
|
||||
LOG("Network", ("(%s) Failed to commit %d bytes this frame to %s:%d. This process is sending more data than the remote can receive!", c->m_connection->getConnectionDescription().c_str(), c->m_pendingSend.getSize(), c->getRemoteAddress().c_str(), c->getRemotePort()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
unsigned short TcpClient::getBindPort() const
|
||||
{
|
||||
return m_bindPort;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
int TcpClient::getSocket() const
|
||||
{
|
||||
return m_socket;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
void TcpClient::install()
|
||||
{
|
||||
FATAL(s_installed, ("TcpClient is already installed!"));
|
||||
s_installed = true;
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
void TcpClient::onConnectionClosed()
|
||||
{
|
||||
if (m_connection)
|
||||
m_connection->setDisconnectReason("TcpClient::onConnectionClosed called");
|
||||
m_connected = false;
|
||||
shutdown(m_socket, SHUT_RDWR);
|
||||
if(m_tcpServer)
|
||||
{
|
||||
m_tcpServer->removeClient(this);
|
||||
}
|
||||
close(m_socket);
|
||||
m_socket = -1;
|
||||
if(m_connection)
|
||||
{
|
||||
NetworkHandler::onTerminate(m_connection);
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
void TcpClient::onConnectionOpened()
|
||||
{
|
||||
// Get the local port this socket was bound to. If
|
||||
// a client socket repeatedly attempts to connect to a local
|
||||
// service that should be listening on a port that the operating
|
||||
// system may assign AND the service isn't actually running and
|
||||
// using the port, the client socket may connect to itself.
|
||||
// For example, A client connecting to 127.0.0.1:50005, the operating
|
||||
// system may assign the client socket port 50005 and the connection
|
||||
// succeeds, but it is in fact a connection to itself, meaning the
|
||||
// client isn't actually talking to any valid service.
|
||||
struct sockaddr_in bindAddr;
|
||||
socklen_t addrLen = sizeof(struct sockaddr_in);
|
||||
if(getsockname(m_socket, reinterpret_cast<struct sockaddr *>(&bindAddr), &addrLen) == 0)
|
||||
{
|
||||
m_bindPort = ntohs(bindAddr.sin_port);
|
||||
}
|
||||
|
||||
// this *might* be a valid connection to a remote address, but
|
||||
// to easily catch all cases of loopback connections, attempt a
|
||||
// reconnect if the remote port is identical to the local port
|
||||
if(m_remoteAddress.getHostPort() == m_bindPort && ! m_tcpServer)
|
||||
{
|
||||
shutdown(m_socket, SHUT_RDWR);
|
||||
DEBUG_FATAL(m_tcpServer, ("Detected a loopback client connection on a TCP SERVER!!!"));
|
||||
close(m_socket);
|
||||
m_socket = -1;
|
||||
|
||||
// reconnect the socket
|
||||
// address and port info is initialized in the constructor
|
||||
setupSocket();
|
||||
LOG("Network", ("Detected a loopback, closing socket and reconnecting"));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_connected = true;
|
||||
if(m_connection)
|
||||
{
|
||||
m_connection->onConnectionOpened();
|
||||
}
|
||||
flush();
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
void TcpClient::onReceive (const unsigned char * const recvBuf, const int bytes)
|
||||
{
|
||||
if(m_connection)
|
||||
{
|
||||
m_connection->receive(recvBuf, bytes);
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
void TcpClient::queryConnect ()
|
||||
{
|
||||
struct pollfd pfd;
|
||||
pfd.fd = m_socket;
|
||||
pfd.events = POLLOUT;
|
||||
pfd.revents = 0;
|
||||
int pollResult = poll(&pfd, 1, 0);
|
||||
if(pollResult > 0)
|
||||
{
|
||||
if (pfd.revents & POLLERR)
|
||||
{
|
||||
if (m_connection)
|
||||
m_connection->setDisconnectReason("TcpClient::queryConnect POLLERR");
|
||||
onConnectionClosed();
|
||||
}
|
||||
else if (pfd.revents & POLLHUP)
|
||||
{
|
||||
if (m_connection)
|
||||
m_connection->setDisconnectReason("TcpClient::queryConnect POLLHUP");
|
||||
onConnectionClosed();
|
||||
}
|
||||
else
|
||||
{
|
||||
onConnectionOpened();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
void TcpClient::release()
|
||||
{
|
||||
m_refCount--;
|
||||
if(m_refCount == 0)
|
||||
{
|
||||
if (m_connected)
|
||||
{
|
||||
if (m_connection)
|
||||
m_connection->setDisconnectReason("TcpClient::release called");
|
||||
onConnectionClosed();
|
||||
}
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
void TcpClient::remove()
|
||||
{
|
||||
FATAL(!s_installed, ("TcpClient already removed!"));
|
||||
std::set<TcpClient *>::iterator i;
|
||||
for(i = s_tcpClients.begin(); i != s_tcpClients.end(); ++i)
|
||||
{
|
||||
TcpClient * c = (*i);
|
||||
c->release();
|
||||
}
|
||||
s_installed = false;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
void TcpClient::send(const unsigned char * const buffer, const int bufferLen)
|
||||
{
|
||||
if (bufferLen)
|
||||
{
|
||||
m_lastSendTime = Clock::getFrameStartTimeMs();
|
||||
s_pendingConnectionSends.insert(this);
|
||||
if( !m_rawTCP )
|
||||
Archive::put(m_pendingSend, bufferLen);
|
||||
m_pendingSend.put(buffer, bufferLen);
|
||||
|
||||
static int const tcpMinimumFrame = ConfigSharedNetwork::getTcpMinimumFrame();
|
||||
if (static_cast<int>(m_pendingSend.getSize()) >= tcpMinimumFrame)
|
||||
flush();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
void TcpClient::setConnection(Connection * c)
|
||||
{
|
||||
m_connection = c;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
void TcpClient::checkKeepalive()
|
||||
{
|
||||
if (m_connected)
|
||||
{
|
||||
unsigned long const timeNow = Clock::getFrameStartTimeMs();
|
||||
if (timeNow-m_lastSendTime > KEEPALIVE_MS)
|
||||
{
|
||||
m_lastSendTime = timeNow;
|
||||
s_pendingConnectionSends.insert(this);
|
||||
Archive::put(m_pendingSend, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
void TcpClient::update()
|
||||
{
|
||||
addRef();
|
||||
if(!m_connected)
|
||||
{
|
||||
queryConnect();
|
||||
}
|
||||
|
||||
if(m_connected)
|
||||
{
|
||||
struct pollfd pfd;
|
||||
pfd.fd = m_socket;
|
||||
pfd.events = POLLIN|POLLHUP;
|
||||
pfd.revents = 0;
|
||||
int pollResult = poll (&pfd, 1, 0);
|
||||
// POLLERR is mutually exclusive with POLLIN and POLLHUP.
|
||||
// POLLIN and POLLHUP are not consistent cross-platform. Additionally,
|
||||
// a POLLHUP doesn't mean that there's not data available. The best,
|
||||
// cross-platform way to check for disconnection is to see if either
|
||||
// POLLHUP or POLLIN are set, and then read from the socket.
|
||||
// In cases where there's data, we'll just end up checking it next
|
||||
// frame anyway. In cases where there's no data, it means we've
|
||||
// disconnected so we can handle cleanup.
|
||||
if (pollResult)
|
||||
{
|
||||
if (m_recvBuffer == NULL)
|
||||
{
|
||||
m_recvBufferLength = 1500;
|
||||
m_recvBuffer = new unsigned char [m_recvBufferLength];
|
||||
}
|
||||
int bytesReceived = recv(m_socket, m_recvBuffer, m_recvBufferLength, 0);
|
||||
if (bytesReceived > 0)
|
||||
{
|
||||
onReceive(m_recvBuffer, bytesReceived);
|
||||
}
|
||||
else if (bytesReceived == -1)
|
||||
{
|
||||
switch (errno)
|
||||
{
|
||||
case EAGAIN:
|
||||
case EINTR:
|
||||
break;
|
||||
default:
|
||||
if (m_connection)
|
||||
m_connection->setDisconnectReason("TcpClient::update recv returned -1, errno=%d", errno);
|
||||
onConnectionClosed();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
else if (bytesReceived == 0)
|
||||
{
|
||||
// peer closed connection
|
||||
|
||||
|
||||
|
||||
if (m_connection)
|
||||
{
|
||||
// Log this, as it's a pretty deep change.
|
||||
LOG("Network", ("(%s) Read zero bytes on a successful poll (POLLIN|POLLHUP). Closing socket since we think it is closed.", m_connection->getConnectionDescription().c_str()));
|
||||
m_connection->setDisconnectReason("TcpClient::update recv returned 0");
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG("Network", ("(null connection object) Read zero bytes on a successful poll (POLLIN|POLLHUP). Closing socket since we think it is closed."));
|
||||
}
|
||||
onConnectionClosed();
|
||||
}
|
||||
|
||||
if (bytesReceived == m_recvBufferLength)
|
||||
{
|
||||
// grow the buffer
|
||||
delete [] m_recvBuffer;
|
||||
m_recvBufferLength = m_recvBufferLength * 2;
|
||||
m_recvBuffer = new unsigned char [m_recvBufferLength];
|
||||
}
|
||||
}
|
||||
}
|
||||
release();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void TcpClient::setupSocket()
|
||||
{
|
||||
FATAL(! s_installed, ("TcpClient is not installed!"));
|
||||
|
||||
protoent * p = getprotobyname ("tcp");
|
||||
const int entry = p->p_proto;
|
||||
m_socket = socket (AF_INET, SOCK_STREAM, entry);
|
||||
if (m_socket != -1)
|
||||
{
|
||||
setSockOptions();
|
||||
|
||||
int nameLen = sizeof (struct sockaddr_in);
|
||||
int result;
|
||||
result = connect (m_socket, reinterpret_cast<const struct sockaddr *> (&m_remoteAddress.getSockAddr4 () ), nameLen);
|
||||
if(result == -1)
|
||||
{
|
||||
switch(errno)
|
||||
{
|
||||
case EINPROGRESS:
|
||||
case EALREADY:
|
||||
break;
|
||||
default:
|
||||
perror("connect ");
|
||||
if (m_connection)
|
||||
m_connection->setDisconnectReason("TcpClient::TcpClient connect returned -1, errno=%d", errno);
|
||||
onConnectionClosed();
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
struct sockaddr_in bindAddr;
|
||||
socklen_t addrLen = sizeof(struct sockaddr_in);
|
||||
if(getsockname(m_socket, reinterpret_cast<struct sockaddr *>(&bindAddr), &addrLen) == 0)
|
||||
{
|
||||
m_bindPort = ntohs(bindAddr.sin_port);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
void TcpClient::setRawTCP( bool bNewValue )
|
||||
{
|
||||
m_rawTCP = bNewValue;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void TcpClient::clearTcpServer()
|
||||
{
|
||||
m_tcpServer = 0;
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
// TcpClient.h
|
||||
// Copyright 2000-02, Sony Online Entertainment Inc., all rights reserved.
|
||||
// Author: Justin Randall
|
||||
|
||||
#ifndef _INCLUDED_TcpClient_H
|
||||
#define _INCLUDED_TcpClient_H
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
#include "Archive/ByteStream.h"
|
||||
#include "sharedNetwork/Address.h"
|
||||
#include <string>
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
class Connection;
|
||||
class TcpServer;
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
class TcpClient
|
||||
{
|
||||
public:
|
||||
TcpClient(int sock, TcpServer *);
|
||||
TcpClient(const std::string & address, const unsigned short port);
|
||||
|
||||
void addRef();
|
||||
static void install();
|
||||
void release();
|
||||
static void remove();
|
||||
void send(const unsigned char * const buffer, const int length);
|
||||
|
||||
unsigned short getBindPort() const;
|
||||
std::string const &getRemoteAddress() const;
|
||||
unsigned short getRemotePort() const;
|
||||
void setPendingSendAllocatedSizeLimit(unsigned int limit);
|
||||
|
||||
// only used by clients
|
||||
void update();
|
||||
static void flushPendingWrites();
|
||||
|
||||
protected:
|
||||
friend class TcpServer;
|
||||
friend class Connection;
|
||||
|
||||
void setSockOptions();
|
||||
void commit();
|
||||
int getSocket() const;
|
||||
void onConnectionClosed();
|
||||
void onConnectionOpened();
|
||||
void onReceive(const unsigned char * const recvBuf, const int bytes);
|
||||
void queryConnect();
|
||||
void queueReceive();
|
||||
void setConnection(Connection *);
|
||||
void checkKeepalive();
|
||||
void clearTcpServer();
|
||||
void setRawTCP( bool bNewValue );
|
||||
|
||||
private:
|
||||
TcpClient & operator = (const TcpClient & rhs);
|
||||
TcpClient(const TcpClient & source);
|
||||
~TcpClient();
|
||||
void flush ();
|
||||
void setupSocket();
|
||||
int m_socket;
|
||||
TcpServer * m_tcpServer;
|
||||
Archive::ByteStream m_pendingSend;
|
||||
Connection * m_connection;
|
||||
unsigned char * m_recvBuffer;
|
||||
int m_recvBufferLength;
|
||||
Address m_remoteAddress;
|
||||
int m_refCount;
|
||||
bool m_connected;
|
||||
unsigned long m_lastSendTime;
|
||||
unsigned short m_bindPort;
|
||||
bool m_rawTCP;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
inline void TcpClient::setPendingSendAllocatedSizeLimit(const unsigned int limit)
|
||||
{
|
||||
m_pendingSend.setAllocatedSizeLimit(limit);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
#endif // _INCLUDED_TcpClient_H
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
// TcpServer.cpp
|
||||
// Copyright 2000-02, Sony Online Entertainment Inc., all rights reserved.
|
||||
// Author: Justin Randall
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
#include "sharedNetwork/FirstSharedNetwork.h"
|
||||
#include "TcpServer.h"
|
||||
|
||||
#include "sharedNetwork/Connection.h"
|
||||
#include "sharedNetwork/Service.h"
|
||||
#include "TcpClient.h"
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <netdb.h>
|
||||
#include <vector>
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
TcpServer::TcpServer(Service * service, const std::string & a, const unsigned short port) :
|
||||
m_bindAddress(a, port),
|
||||
m_handle(-1),
|
||||
m_service(service),
|
||||
m_connections(),
|
||||
m_connectionSockets(),
|
||||
m_inputBuffer(0),
|
||||
m_inputBufferSize(0)
|
||||
{
|
||||
protoent * p = getprotobyname("tcp");
|
||||
if(p)
|
||||
{
|
||||
int entry = p->p_proto;
|
||||
m_handle = socket(AF_INET, SOCK_STREAM, entry);
|
||||
FATAL(m_handle == -1, ("Failed to create a server socket %s:%d", a.c_str(), port));
|
||||
int optval = 1;
|
||||
if(m_handle != -1)
|
||||
{
|
||||
int optResult = setsockopt(m_handle, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
|
||||
FATAL(optResult == -1, ("Failed to set socket option SO_REUSEADDR"));
|
||||
unsigned long nb = 1;
|
||||
int ctlResult = ioctl(m_handle, FIONBIO, &nb);
|
||||
FATAL(ctlResult == -1, ("Failed to set socket non-blocking"));
|
||||
socklen_t addrlen = sizeof(struct sockaddr_in);
|
||||
int bindResult = bind(m_handle, (struct sockaddr *)(&m_bindAddress.getSockAddr4()), addrlen);
|
||||
FATAL(bindResult != 0, ("Failed to bind to port %d", port));
|
||||
if(bindResult == 0)
|
||||
{
|
||||
int result = listen(m_handle, 256);
|
||||
if(result == -1)
|
||||
perror("listen ");
|
||||
FATAL(result == -1, ("Failed to start listening on port %d", port));
|
||||
struct sockaddr_in b;
|
||||
getsockname(m_handle, (struct sockaddr *)(&b), &addrlen);
|
||||
m_bindAddress = b;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
TcpServer::~TcpServer()
|
||||
{
|
||||
close(m_handle);
|
||||
for (std::map<int, TcpClient *>::const_iterator i = m_connections.begin(); i != m_connections.end(); ++i)
|
||||
(*i).second->clearTcpServer();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
const std::string & TcpServer::getBindAddress() const
|
||||
{
|
||||
return m_bindAddress.getHostAddress();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
const unsigned short TcpServer::getBindPort() const
|
||||
{
|
||||
return m_bindAddress.getHostPort();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
void TcpServer::onConnectionClosed(TcpClient * c)
|
||||
{
|
||||
if(c)
|
||||
{
|
||||
std::set<TcpClient *>::iterator f = m_pendingDestroys.find(c);
|
||||
if(f == m_pendingDestroys.end())
|
||||
{
|
||||
m_pendingDestroys.insert(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
void TcpServer::removeClient(TcpClient * c)
|
||||
{
|
||||
std::map<int, TcpClient *>::iterator f = m_connections.find(c->getSocket());
|
||||
if(f != m_connections.end())
|
||||
{
|
||||
// c->release();
|
||||
m_connections.erase(f);
|
||||
}
|
||||
|
||||
std::vector<pollfd>::iterator s;
|
||||
for(s = m_connectionSockets.begin(); s != m_connectionSockets.end(); ++s)
|
||||
{
|
||||
if((*s).fd == c->getSocket())
|
||||
{
|
||||
m_connectionSockets.erase(s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
void TcpServer::update()
|
||||
{
|
||||
struct pollfd pfd;
|
||||
pfd.fd = m_handle;
|
||||
pfd.events = POLLIN;
|
||||
pfd.revents = 0;
|
||||
|
||||
int result = poll(&pfd, 1, 0);
|
||||
if(result > 0)
|
||||
{
|
||||
// connection established
|
||||
struct sockaddr_in addr;
|
||||
socklen_t len = sizeof(struct sockaddr_in);
|
||||
int newSock = accept(m_handle, (struct sockaddr *)(&addr), &len);
|
||||
if(newSock != -1)
|
||||
{
|
||||
TcpClient * ptc = new TcpClient(newSock, this);
|
||||
ptc->addRef();
|
||||
m_connections.insert(std::make_pair(newSock, ptc));
|
||||
struct pollfd readFd;
|
||||
readFd.fd = ptc->getSocket();
|
||||
readFd.events = POLLIN|POLLERR|POLLHUP;
|
||||
readFd.revents = 0;
|
||||
m_connectionSockets.push_back(readFd);
|
||||
ptc->onConnectionOpened();
|
||||
if(m_service)
|
||||
m_service->onConnectionOpened(ptc);
|
||||
ptc->release();
|
||||
}
|
||||
}
|
||||
|
||||
struct pollfd * clients = &m_connectionSockets[0];
|
||||
|
||||
int readResult = poll (clients, m_connectionSockets.size (), 0);
|
||||
if (readResult > 0)
|
||||
{
|
||||
std::vector<struct pollfd>::iterator i;
|
||||
std::vector<struct pollfd> cs = m_connectionSockets;
|
||||
for (i = cs.begin (); i != cs.end (); ++i)
|
||||
{
|
||||
if ((*i).revents & POLLERR)
|
||||
{
|
||||
std::map<int, TcpClient *>::iterator f = m_connections.find ( (*i).fd );
|
||||
if (f != m_connections.end ())
|
||||
{
|
||||
TcpClient * c = (*f).second;
|
||||
if (c->m_connection)
|
||||
c->m_connection->setDisconnectReason("TcpServer::update POLLERR");
|
||||
c->onConnectionClosed();
|
||||
}
|
||||
}
|
||||
// POLLERR is mutually exclusive with POLLIN and POLLHUP.
|
||||
// POLLIN and POLLHUP are not consistent cross-platform. Additionally,
|
||||
// a POLLHUP doesn't mean that there's not data available. The best,
|
||||
// cross-platform way to check for disconnection is to see if either
|
||||
// POLLHUP or POLLIN are set, and then read from the socket.
|
||||
// In cases where there's data, we'll just end up checking it next
|
||||
// frame anyway. In cases where there's no data, it means we've
|
||||
// disconnected so we can handle cleanup.
|
||||
else if( (*i).revents & (POLLIN|POLLHUP) )
|
||||
{
|
||||
|
||||
if (m_inputBuffer == 0)
|
||||
{
|
||||
m_inputBuffer = new unsigned char[1500];
|
||||
m_inputBufferSize = 1500;
|
||||
}
|
||||
int bytesReceived = recv ( (*i).fd, m_inputBuffer, m_inputBufferSize, 0);
|
||||
std::map<int, TcpClient *>::iterator f = m_connections.find ( (*i).fd );
|
||||
if (f != m_connections.end ())
|
||||
{
|
||||
TcpClient * c = (*f).second;
|
||||
std::set<TcpClient *>::iterator pd = m_pendingDestroys.find(c);
|
||||
if(pd == m_pendingDestroys.end())
|
||||
{
|
||||
c->addRef();
|
||||
if(bytesReceived > 0)
|
||||
{
|
||||
c->onReceive(m_inputBuffer, bytesReceived);
|
||||
}
|
||||
else if (bytesReceived == -1)
|
||||
{
|
||||
if (c->m_connection)
|
||||
c->m_connection->setDisconnectReason("TcpServer::update recv returned %d, errno=%d", bytesReceived, errno);
|
||||
c->onConnectionClosed();
|
||||
}
|
||||
else if (bytesReceived == 0)
|
||||
{
|
||||
// 0 bytes received on a read is the guaranteed signal for a
|
||||
// closed connection.
|
||||
if (c->m_connection)
|
||||
c->m_connection->setDisconnectReason("TcpServer::update recv returned no bytes, singaling a closed socket (bytes=%d, errno=%d", bytesReceived, errno);
|
||||
c->onConnectionClosed();
|
||||
}
|
||||
c->release();
|
||||
}
|
||||
}
|
||||
if (bytesReceived == m_inputBufferSize)
|
||||
{
|
||||
delete [] m_inputBuffer;
|
||||
m_inputBufferSize = m_inputBufferSize * 2;
|
||||
m_inputBuffer = new unsigned char [m_inputBufferSize];
|
||||
}
|
||||
}
|
||||
}
|
||||
std::set<TcpClient *>::iterator pdIter;
|
||||
for(pdIter = m_pendingDestroys.begin(); pdIter != m_pendingDestroys.end(); ++pdIter)
|
||||
{
|
||||
removeClient((*pdIter));
|
||||
}
|
||||
m_pendingDestroys.clear();
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
// TcpServer.h
|
||||
// Copyright 2000-02, Sony Online Entertainment Inc., all rights reserved.
|
||||
// Author: Justin Randall
|
||||
|
||||
#ifndef _INCLUDED_TcpServer_H
|
||||
#define _INCLUDED_TcpServer_H
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
#include "Address.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
class Service;
|
||||
class TcpClient;
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
class TcpServer
|
||||
{
|
||||
public:
|
||||
TcpServer(Service * service, const std::string & bindAddress, const unsigned short bindPort);
|
||||
~TcpServer();
|
||||
|
||||
const std::string & getBindAddress () const;
|
||||
const unsigned short getBindPort () const;
|
||||
void onConnectionClosed (TcpClient *);
|
||||
void removeClient (TcpClient *);
|
||||
void update ();
|
||||
|
||||
private:
|
||||
TcpServer & operator = (const TcpServer & rhs);
|
||||
TcpServer(const TcpServer & source);
|
||||
|
||||
private:
|
||||
Address m_bindAddress;
|
||||
int m_handle;
|
||||
Service * m_service;
|
||||
std::map<int, TcpClient *> m_connections;
|
||||
std::vector<struct pollfd> m_connectionSockets;
|
||||
unsigned char * m_inputBuffer;
|
||||
int m_inputBufferSize;
|
||||
std::set<TcpClient *> m_pendingDestroys;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
#endif // _INCLUDED_TcpServer_H
|
||||
@@ -0,0 +1,63 @@
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
#include "FirstSharedNetwork.h"
|
||||
#include <cassert>
|
||||
#include <netdb.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include "sharedNetwork/UdpSock.h"
|
||||
|
||||
const int INVALID_SOCKET = 0xFFFFFFFF;
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
UdpSock::UdpSock() :
|
||||
Sock()
|
||||
{
|
||||
handle = socket(AF_INET, SOCK_DGRAM, 17);
|
||||
assert(handle != INVALID_SOCKET);
|
||||
setNonBlocking();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
UdpSock::~UdpSock()
|
||||
{
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
const unsigned int UdpSock::recvFrom(Address & outAddr, void * targetBuffer, const unsigned int bufferSize) const
|
||||
{
|
||||
socklen_t fromLen = sizeof(struct sockaddr_in);
|
||||
struct sockaddr_in addr;
|
||||
unsigned int result = ::recvfrom(handle, static_cast<char *>(targetBuffer), static_cast<int>(bufferSize), 0, reinterpret_cast<struct sockaddr *>(&addr), &fromLen); //lint !e732 // MS wants an int, should be unsigned IMO
|
||||
outAddr = addr;
|
||||
return result;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
const unsigned int UdpSock::sendTo(const Address & targetAddress, const void * sourceBuffer, const unsigned int length) const
|
||||
{
|
||||
unsigned int bytesSent = 0;
|
||||
if(canSend())
|
||||
{
|
||||
socklen_t toLen = sizeof(struct sockaddr_in);
|
||||
bytesSent = ::sendto(handle, sourceBuffer, static_cast<const int>(length), 0, reinterpret_cast<const struct sockaddr *>(&(targetAddress.getSockAddr4())), toLen); //lint !e732 // MS wants an int, should be unsigned IMO
|
||||
}
|
||||
return bytesSent;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
void UdpSock::enableBroadcast()
|
||||
{
|
||||
int optval = 1;
|
||||
socklen_t optlen = sizeof(int);
|
||||
setsockopt(handle, SOL_SOCKET, SO_BROADCAST, &optval, optlen);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user