Added sharedNetwork library

This commit is contained in:
Anonymous
2014-01-14 06:26:47 -07:00
parent 49accba13d
commit fd64603f9b
76 changed files with 10037 additions and 0 deletions
@@ -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);
}
//---------------------------------------------------------------------