mirror of
https://github.com/debauchee/barrier.git
synced 2026-11-04 22:45:23 +08:00
Started over.
This commit is contained in:
65
net/CNetworkAddress.cpp
Normal file
65
net/CNetworkAddress.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
#include "CNetworkAddress.h"
|
||||
#include <netdb.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
//
|
||||
// CNetworkAddress
|
||||
//
|
||||
|
||||
CNetworkAddress::CNetworkAddress(UInt16 port) throw(XSocketAddress)
|
||||
{
|
||||
if (port == 0)
|
||||
throw XSocketAddress(XSocketAddress::kBadPort, CString(), port);
|
||||
|
||||
struct sockaddr_in* inetAddress = reinterpret_cast<struct sockaddr_in*>(&m_address);
|
||||
inetAddress->sin_family = AF_INET;
|
||||
inetAddress->sin_port = htons(port);
|
||||
inetAddress->sin_addr.s_addr = INADDR_ANY;
|
||||
::memset(inetAddress->sin_zero, 0, sizeof(inetAddress->sin_zero));
|
||||
}
|
||||
|
||||
CNetworkAddress::CNetworkAddress(const CString& hostname, UInt16 port)
|
||||
throw(XSocketAddress)
|
||||
{
|
||||
if (port == 0)
|
||||
throw XSocketAddress(XSocketAddress::kBadPort, hostname, port);
|
||||
|
||||
struct hostent* hent = gethostbyname(hostname.c_str());
|
||||
if (hent == NULL) {
|
||||
switch (h_errno) {
|
||||
case HOST_NOT_FOUND:
|
||||
throw XSocketAddress(XSocketAddress::kNotFound, hostname, port);
|
||||
|
||||
case NO_DATA:
|
||||
throw XSocketAddress(XSocketAddress::kNoAddress, hostname, port);
|
||||
|
||||
case NO_RECOVERY:
|
||||
case TRY_AGAIN:
|
||||
default:
|
||||
throw XSocketAddress(XSocketAddress::kUnknown, hostname, port);
|
||||
}
|
||||
}
|
||||
|
||||
struct sockaddr_in* inetAddress = reinterpret_cast<struct sockaddr_in*>(&m_address);
|
||||
inetAddress->sin_family = hent->h_addrtype;
|
||||
inetAddress->sin_port = htons(port);
|
||||
::memcpy(&inetAddress->sin_addr, hent->h_addr_list[0], hent->h_length);
|
||||
::memset(inetAddress->sin_zero, 0, sizeof(inetAddress->sin_zero));
|
||||
}
|
||||
|
||||
CNetworkAddress::~CNetworkAddress()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
const struct sockaddr* CNetworkAddress::getAddress() const throw()
|
||||
{
|
||||
return &m_address;
|
||||
}
|
||||
|
||||
int CNetworkAddress::getAddressLength() const throw()
|
||||
{
|
||||
return sizeof(m_address);
|
||||
}
|
||||
27
net/CNetworkAddress.h
Normal file
27
net/CNetworkAddress.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef CNETWORKADDRESS_H
|
||||
#define CNETWORKADDRESS_H
|
||||
|
||||
#include "BasicTypes.h"
|
||||
#include "XSocket.h"
|
||||
#include <sys/socket.h>
|
||||
|
||||
class CString;
|
||||
|
||||
class CNetworkAddress {
|
||||
public:
|
||||
CNetworkAddress(UInt16 port) throw(XSocketAddress);
|
||||
CNetworkAddress(const CString& hostname, UInt16 port) throw(XSocketAddress);
|
||||
~CNetworkAddress();
|
||||
|
||||
// manipulators
|
||||
|
||||
// accessors
|
||||
|
||||
const struct sockaddr* getAddress() const throw();
|
||||
int getAddressLength() const throw();
|
||||
|
||||
private:
|
||||
struct sockaddr m_address;
|
||||
};
|
||||
|
||||
#endif
|
||||
93
net/CSocketInputStream.cpp
Normal file
93
net/CSocketInputStream.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
#include "CSocketInputStream.h"
|
||||
#include "CLock.h"
|
||||
#include "CMutex.h"
|
||||
#include "CThread.h"
|
||||
#include "IJob.h"
|
||||
#include "XIO.h"
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
//
|
||||
// CSocketInputStream
|
||||
//
|
||||
|
||||
CSocketInputStream::CSocketInputStream(CMutex* mutex, IJob* closeCB) :
|
||||
m_mutex(mutex),
|
||||
m_empty(mutex, true),
|
||||
m_closeCB(closeCB),
|
||||
m_closed(false),
|
||||
m_hungup(false)
|
||||
{
|
||||
assert(m_mutex != NULL);
|
||||
}
|
||||
|
||||
CSocketInputStream::~CSocketInputStream()
|
||||
{
|
||||
delete m_closeCB;
|
||||
}
|
||||
|
||||
void CSocketInputStream::write(
|
||||
const void* data, UInt32 n) throw()
|
||||
{
|
||||
if (!m_hungup && n > 0) {
|
||||
m_buffer.write(data, n);
|
||||
m_empty = (m_buffer.getSize() == 0);
|
||||
m_empty.broadcast();
|
||||
}
|
||||
}
|
||||
|
||||
void CSocketInputStream::hangup() throw()
|
||||
{
|
||||
m_hungup = true;
|
||||
m_empty.broadcast();
|
||||
}
|
||||
|
||||
void CSocketInputStream::close() throw(XIO)
|
||||
{
|
||||
CLock lock(m_mutex);
|
||||
if (m_closed) {
|
||||
throw XIOClosed();
|
||||
}
|
||||
|
||||
m_closed = true;
|
||||
if (m_closeCB) {
|
||||
m_closeCB->run();
|
||||
}
|
||||
}
|
||||
|
||||
UInt32 CSocketInputStream::read(
|
||||
void* dst, UInt32 n) throw(XIO)
|
||||
{
|
||||
CLock lock(m_mutex);
|
||||
if (m_closed) {
|
||||
throw XIOClosed();
|
||||
}
|
||||
|
||||
// wait for data (or hangup)
|
||||
while (!m_hungup && m_empty == true) {
|
||||
m_empty.wait();
|
||||
}
|
||||
|
||||
// read data
|
||||
const UInt32 count = m_buffer.getSize();
|
||||
if (n > count) {
|
||||
n = count;
|
||||
}
|
||||
if (n > 0) {
|
||||
::memcpy(dst, m_buffer.peek(n), n);
|
||||
m_buffer.pop(n);
|
||||
}
|
||||
|
||||
// update empty state
|
||||
if (m_buffer.getSize() == 0) {
|
||||
m_empty = true;
|
||||
m_empty.broadcast();
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
UInt32 CSocketInputStream::getSize() const throw()
|
||||
{
|
||||
CLock lock(m_mutex);
|
||||
return m_buffer.getSize();
|
||||
}
|
||||
42
net/CSocketInputStream.h
Normal file
42
net/CSocketInputStream.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#ifndef CSOCKETINPUTSTREAM_H
|
||||
#define CSOCKETINPUTSTREAM_H
|
||||
|
||||
#include "CSocketStreamBuffer.h"
|
||||
#include "CCondVar.h"
|
||||
#include "IInputStream.h"
|
||||
|
||||
class CMutex;
|
||||
class IJob;
|
||||
|
||||
class CSocketInputStream : public IInputStream {
|
||||
public:
|
||||
CSocketInputStream(CMutex*, IJob* adoptedCloseCB);
|
||||
~CSocketInputStream();
|
||||
|
||||
// manipulators
|
||||
|
||||
// write() appends n bytes to the buffer
|
||||
void write(const void*, UInt32 n) throw();
|
||||
|
||||
// causes read() to always return immediately. if there is no
|
||||
// more data then it returns 0. further writes are discarded.
|
||||
void hangup() throw();
|
||||
|
||||
// accessors
|
||||
|
||||
// IInputStream overrides
|
||||
// these all lock the mutex for their duration
|
||||
virtual void close() throw(XIO);
|
||||
virtual UInt32 read(void*, UInt32 count) throw(XIO);
|
||||
virtual UInt32 getSize() const throw();
|
||||
|
||||
private:
|
||||
CMutex* m_mutex;
|
||||
CCondVar<bool> m_empty;
|
||||
IJob* m_closeCB;
|
||||
CSocketStreamBuffer m_buffer;
|
||||
bool m_closed;
|
||||
bool m_hungup;
|
||||
};
|
||||
|
||||
#endif
|
||||
78
net/CSocketOutputStream.cpp
Normal file
78
net/CSocketOutputStream.cpp
Normal file
@@ -0,0 +1,78 @@
|
||||
#include "CSocketOutputStream.h"
|
||||
#include "CLock.h"
|
||||
#include "CMutex.h"
|
||||
#include "CThread.h"
|
||||
#include "IJob.h"
|
||||
#include "XIO.h"
|
||||
#include <assert.h>
|
||||
|
||||
//
|
||||
// CSocketOutputStream
|
||||
//
|
||||
|
||||
CSocketOutputStream::CSocketOutputStream(CMutex* mutex, IJob* closeCB) :
|
||||
m_mutex(mutex),
|
||||
m_closeCB(closeCB),
|
||||
m_closed(false)
|
||||
{
|
||||
assert(m_mutex != NULL);
|
||||
}
|
||||
|
||||
CSocketOutputStream::~CSocketOutputStream()
|
||||
{
|
||||
delete m_closeCB;
|
||||
}
|
||||
|
||||
const void* CSocketOutputStream::peek(UInt32 n) throw()
|
||||
{
|
||||
return m_buffer.peek(n);
|
||||
}
|
||||
|
||||
void CSocketOutputStream::pop(UInt32 n) throw()
|
||||
{
|
||||
m_buffer.pop(n);
|
||||
}
|
||||
|
||||
UInt32 CSocketOutputStream::getSize() const throw()
|
||||
{
|
||||
return m_buffer.getSize();
|
||||
}
|
||||
|
||||
void CSocketOutputStream::close() throw(XIO)
|
||||
{
|
||||
CLock lock(m_mutex);
|
||||
if (m_closed) {
|
||||
throw XIOClosed();
|
||||
}
|
||||
|
||||
m_closed = true;
|
||||
if (m_closeCB) {
|
||||
m_closeCB->run();
|
||||
}
|
||||
}
|
||||
|
||||
UInt32 CSocketOutputStream::write(
|
||||
const void* data, UInt32 n) throw(XIO)
|
||||
{
|
||||
CLock lock(m_mutex);
|
||||
if (m_closed) {
|
||||
throw XIOClosed();
|
||||
}
|
||||
|
||||
m_buffer.write(data, n);
|
||||
return n;
|
||||
}
|
||||
|
||||
void CSocketOutputStream::flush() throw(XIO)
|
||||
{
|
||||
// wait until all data is written
|
||||
while (getSizeWithLock() > 0) {
|
||||
CThread::sleep(0.05);
|
||||
}
|
||||
}
|
||||
|
||||
UInt32 CSocketOutputStream::getSizeWithLock() const throw()
|
||||
{
|
||||
CLock lock(m_mutex);
|
||||
return m_buffer.getSize();
|
||||
}
|
||||
43
net/CSocketOutputStream.h
Normal file
43
net/CSocketOutputStream.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#ifndef CSOCKETOUTPUTSTREAM_H
|
||||
#define CSOCKETOUTPUTSTREAM_H
|
||||
|
||||
#include "CSocketStreamBuffer.h"
|
||||
#include "IOutputStream.h"
|
||||
|
||||
class CMutex;
|
||||
class IJob;
|
||||
|
||||
class CSocketOutputStream : public IOutputStream {
|
||||
public:
|
||||
CSocketOutputStream(CMutex*, IJob* adoptedCloseCB);
|
||||
~CSocketOutputStream();
|
||||
|
||||
// manipulators
|
||||
|
||||
// peek() returns a buffer of n bytes (which must be <= getSize()).
|
||||
// pop() discards the next n bytes.
|
||||
const void* peek(UInt32 n) throw();
|
||||
void pop(UInt32 n) throw();
|
||||
|
||||
// accessors
|
||||
|
||||
// return the number of bytes in the buffer
|
||||
UInt32 getSize() const throw();
|
||||
|
||||
// IOutputStream overrides
|
||||
// these all lock the mutex for their duration
|
||||
virtual void close() throw(XIO);
|
||||
virtual UInt32 write(const void*, UInt32 count) throw(XIO);
|
||||
virtual void flush() throw(XIO);
|
||||
|
||||
private:
|
||||
UInt32 getSizeWithLock() const throw();
|
||||
|
||||
private:
|
||||
CMutex* m_mutex;
|
||||
IJob* m_closeCB;
|
||||
CSocketStreamBuffer m_buffer;
|
||||
bool m_closed;
|
||||
};
|
||||
|
||||
#endif
|
||||
106
net/CSocketStreamBuffer.cpp
Normal file
106
net/CSocketStreamBuffer.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
#include "CSocketStreamBuffer.h"
|
||||
#include <assert.h>
|
||||
|
||||
//
|
||||
// CSocketStreamBuffer
|
||||
//
|
||||
|
||||
const UInt32 CSocketStreamBuffer::kChunkSize = 4096;
|
||||
|
||||
CSocketStreamBuffer::CSocketStreamBuffer() : m_size(0)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
CSocketStreamBuffer::~CSocketStreamBuffer()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
const void* CSocketStreamBuffer::peek(UInt32 n) throw()
|
||||
{
|
||||
assert(n <= m_size);
|
||||
|
||||
// reserve space in first chunk
|
||||
ChunkList::iterator head = m_chunks.begin();
|
||||
head->reserve(n);
|
||||
|
||||
// consolidate chunks into the first chunk until it has n bytes
|
||||
ChunkList::iterator scan = head;
|
||||
++scan;
|
||||
while (head->size() < n && scan != m_chunks.end()) {
|
||||
head->insert(head->end(), scan->begin(), scan->end());
|
||||
scan = m_chunks.erase(scan);
|
||||
}
|
||||
|
||||
return reinterpret_cast<const void*>(head->begin());
|
||||
}
|
||||
|
||||
void CSocketStreamBuffer::pop(UInt32 n) throw()
|
||||
{
|
||||
m_size -= n;
|
||||
|
||||
// discard chunks until more than n bytes would've been discarded
|
||||
ChunkList::iterator scan = m_chunks.begin();
|
||||
while (scan->size() <= n && scan != m_chunks.end()) {
|
||||
n -= scan->size();
|
||||
scan = m_chunks.erase(scan);
|
||||
}
|
||||
|
||||
// if there's anything left over then remove it from the head chunk.
|
||||
// if there's no head chunk then we're already empty.
|
||||
if (scan == m_chunks.end()) {
|
||||
m_size = 0;
|
||||
}
|
||||
else if (n > 0) {
|
||||
scan->erase(scan->begin(), scan->begin() + n);
|
||||
}
|
||||
}
|
||||
|
||||
void CSocketStreamBuffer::write(
|
||||
const void* vdata, UInt32 n) throw()
|
||||
{
|
||||
assert(vdata != NULL);
|
||||
|
||||
if (n == 0) {
|
||||
return;
|
||||
}
|
||||
m_size += n;
|
||||
|
||||
// cast data to bytes
|
||||
const UInt8* data = reinterpret_cast<const UInt8*>(vdata);
|
||||
|
||||
// point to last chunk if it has space, otherwise append an empty chunk
|
||||
ChunkList::iterator scan = m_chunks.end();
|
||||
if (scan != m_chunks.begin()) {
|
||||
--scan;
|
||||
if (scan->size() >= kChunkSize)
|
||||
++scan;
|
||||
}
|
||||
if (scan == m_chunks.end()) {
|
||||
scan = m_chunks.insert(scan);
|
||||
}
|
||||
|
||||
// append data in chunks
|
||||
while (n > 0) {
|
||||
// choose number of bytes for next chunk
|
||||
UInt32 count = kChunkSize - scan->size();
|
||||
if (count > n)
|
||||
count = n;
|
||||
|
||||
// transfer data
|
||||
scan->insert(scan->end(), data, data + count);
|
||||
n -= count;
|
||||
data += count;
|
||||
|
||||
// append another empty chunk if we're not done yet
|
||||
if (n > 0) {
|
||||
scan = m_chunks.insert(scan);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UInt32 CSocketStreamBuffer::getSize() const throw()
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
38
net/CSocketStreamBuffer.h
Normal file
38
net/CSocketStreamBuffer.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#ifndef CSOCKETSTREAMBUFFER_H
|
||||
#define CSOCKETSTREAMBUFFER_H
|
||||
|
||||
#include "BasicTypes.h"
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
class CSocketStreamBuffer {
|
||||
public:
|
||||
CSocketStreamBuffer();
|
||||
~CSocketStreamBuffer();
|
||||
|
||||
// manipulators
|
||||
|
||||
// peek() returns a buffer of n bytes (which must be <= getSize()).
|
||||
// pop() discards the next n bytes.
|
||||
const void* peek(UInt32 n) throw();
|
||||
void pop(UInt32 n) throw();
|
||||
|
||||
// write() appends n bytes to the buffer
|
||||
void write(const void*, UInt32 n) throw();
|
||||
|
||||
// accessors
|
||||
|
||||
// return the number of bytes in the buffer
|
||||
UInt32 getSize() const throw();
|
||||
|
||||
private:
|
||||
static const UInt32 kChunkSize;
|
||||
|
||||
typedef std::vector<UInt8> Chunk;
|
||||
typedef std::list<Chunk> ChunkList;
|
||||
|
||||
ChunkList m_chunks;
|
||||
UInt32 m_size;
|
||||
};
|
||||
|
||||
#endif
|
||||
72
net/CTCPListenSocket.cpp
Normal file
72
net/CTCPListenSocket.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#include "CTCPListenSocket.h"
|
||||
#include "CTCPSocket.h"
|
||||
#include "CNetworkAddress.h"
|
||||
#include "CThread.h"
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
//
|
||||
// CTCPListenSocket
|
||||
//
|
||||
|
||||
CTCPListenSocket::CTCPListenSocket()
|
||||
{
|
||||
m_fd = socket(PF_INET, SOCK_STREAM, 0);
|
||||
if (m_fd == -1) {
|
||||
throw XSocketCreate();
|
||||
}
|
||||
}
|
||||
|
||||
CTCPListenSocket::~CTCPListenSocket()
|
||||
{
|
||||
try {
|
||||
close();
|
||||
}
|
||||
catch (...) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
void CTCPListenSocket::bind(
|
||||
const CNetworkAddress& addr) throw(XSocket)
|
||||
{
|
||||
if (::bind(m_fd, addr.getAddress(), addr.getAddressLength()) == -1) {
|
||||
if (errno == EADDRINUSE) {
|
||||
throw XSocketAddressInUse();
|
||||
}
|
||||
throw XSocketBind();
|
||||
}
|
||||
if (listen(m_fd, 3) == -1) {
|
||||
throw XSocketBind();
|
||||
}
|
||||
}
|
||||
|
||||
ISocket* CTCPListenSocket::accept() throw(XSocket)
|
||||
{
|
||||
for (;;) {
|
||||
struct sockaddr addr;
|
||||
socklen_t addrlen = sizeof(addr);
|
||||
CThread::testCancel();
|
||||
int fd = ::accept(m_fd, &addr, &addrlen);
|
||||
if (fd == -1) {
|
||||
CThread::testCancel();
|
||||
}
|
||||
else {
|
||||
return new CTCPSocket(fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CTCPListenSocket::close() throw(XIO)
|
||||
{
|
||||
if (m_fd == -1) {
|
||||
throw XIOClosed();
|
||||
}
|
||||
if (::close(m_fd) == -1) {
|
||||
throw XIOClose();
|
||||
}
|
||||
m_fd = -1;
|
||||
}
|
||||
24
net/CTCPListenSocket.h
Normal file
24
net/CTCPListenSocket.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef CTCPLISTENSOCKET_H
|
||||
#define CTCPLISTENSOCKET_H
|
||||
|
||||
#include "IListenSocket.h"
|
||||
|
||||
class CTCPListenSocket : public IListenSocket {
|
||||
public:
|
||||
CTCPListenSocket();
|
||||
~CTCPListenSocket();
|
||||
|
||||
// manipulators
|
||||
|
||||
// accessors
|
||||
|
||||
// IListenSocket overrides
|
||||
virtual void bind(const CNetworkAddress&) throw(XSocket);
|
||||
virtual ISocket* accept() throw(XSocket);
|
||||
virtual void close() throw(XIO);
|
||||
|
||||
private:
|
||||
int m_fd;
|
||||
};
|
||||
|
||||
#endif
|
||||
226
net/CTCPSocket.cpp
Normal file
226
net/CTCPSocket.cpp
Normal file
@@ -0,0 +1,226 @@
|
||||
#include "CTCPSocket.h"
|
||||
#include "CBufferedInputStream.h"
|
||||
#include "CBufferedOutputStream.h"
|
||||
#include "CNetworkAddress.h"
|
||||
#include "CLock.h"
|
||||
#include "CMutex.h"
|
||||
#include "CCondVar.h"
|
||||
#include "CThread.h"
|
||||
#include "TMethodJob.h"
|
||||
#include "CStopwatch.h"
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/poll.h>
|
||||
#include <netinet/in.h>
|
||||
#include <assert.h>
|
||||
|
||||
//
|
||||
// CTCPSocket
|
||||
//
|
||||
|
||||
CTCPSocket::CTCPSocket() throw(XSocket)
|
||||
{
|
||||
m_fd = socket(PF_INET, SOCK_STREAM, 0);
|
||||
if (m_fd == -1) {
|
||||
throw XSocketCreate();
|
||||
}
|
||||
init();
|
||||
}
|
||||
|
||||
CTCPSocket::CTCPSocket(int fd) throw() :
|
||||
m_fd(fd)
|
||||
{
|
||||
assert(m_fd != -1);
|
||||
|
||||
init();
|
||||
|
||||
// socket starts in connected state
|
||||
m_connected = kReadWrite;
|
||||
|
||||
// start handling socket
|
||||
m_thread = new CThread(new TMethodJob<CTCPSocket>(
|
||||
this, &CTCPSocket::service));
|
||||
}
|
||||
|
||||
CTCPSocket::~CTCPSocket()
|
||||
{
|
||||
try {
|
||||
close();
|
||||
}
|
||||
catch (...) {
|
||||
// ignore failures
|
||||
}
|
||||
|
||||
// clean up
|
||||
delete m_mutex;
|
||||
delete m_input;
|
||||
delete m_output;
|
||||
}
|
||||
|
||||
void CTCPSocket::bind(const CNetworkAddress& addr)
|
||||
throw(XSocket)
|
||||
{
|
||||
if (::bind(m_fd, addr.getAddress(), addr.getAddressLength()) == -1) {
|
||||
if (errno == EADDRINUSE) {
|
||||
throw XSocketAddressInUse();
|
||||
}
|
||||
throw XSocketBind();
|
||||
}
|
||||
}
|
||||
|
||||
void CTCPSocket::connect(const CNetworkAddress& addr)
|
||||
throw(XSocket)
|
||||
{
|
||||
CThread::testCancel();
|
||||
if (::connect(m_fd, addr.getAddress(), addr.getAddressLength()) == -1) {
|
||||
CThread::testCancel();
|
||||
throw XSocketConnect();
|
||||
}
|
||||
|
||||
// start servicing the socket
|
||||
m_connected = kReadWrite;
|
||||
m_thread = new CThread(new TMethodJob<CTCPSocket>(
|
||||
this, &CTCPSocket::service));
|
||||
}
|
||||
|
||||
void CTCPSocket::close() throw(XIO)
|
||||
{
|
||||
// shutdown I/O thread before close
|
||||
if (m_thread != NULL) {
|
||||
// flush if output buffer not empty and output buffer not closed
|
||||
bool doFlush;
|
||||
{
|
||||
CLock lock(m_mutex);
|
||||
doFlush = ((m_connected & kWrite) != 0);
|
||||
}
|
||||
if (doFlush) {
|
||||
m_output->flush();
|
||||
}
|
||||
|
||||
m_thread->cancel();
|
||||
m_thread->wait();
|
||||
delete m_thread;
|
||||
m_thread = NULL;
|
||||
}
|
||||
|
||||
CLock lock(m_mutex);
|
||||
if (m_fd != -1) {
|
||||
if (::close(m_fd) == -1) {
|
||||
throw XIOClose();
|
||||
}
|
||||
m_fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
IInputStream* CTCPSocket::getInputStream() throw()
|
||||
{
|
||||
return m_input;
|
||||
}
|
||||
|
||||
IOutputStream* CTCPSocket::getOutputStream() throw()
|
||||
{
|
||||
return m_output;
|
||||
}
|
||||
|
||||
void CTCPSocket::init() throw(XIO)
|
||||
{
|
||||
m_mutex = new CMutex;
|
||||
m_thread = NULL;
|
||||
m_connected = kClosed;
|
||||
m_input = new CBufferedInputStream(m_mutex,
|
||||
new TMethodJob<CTCPSocket>(
|
||||
this, &CTCPSocket::closeInput));
|
||||
m_output = new CBufferedOutputStream(m_mutex,
|
||||
new TMethodJob<CTCPSocket>(
|
||||
this, &CTCPSocket::closeOutput));
|
||||
}
|
||||
|
||||
void CTCPSocket::service(void*) throw(XThread)
|
||||
{
|
||||
assert(m_fd != -1);
|
||||
|
||||
// now service the connection
|
||||
struct pollfd pfds[1];
|
||||
pfds[0].fd = m_fd;
|
||||
for (;;) {
|
||||
{
|
||||
// choose events to poll for
|
||||
CLock lock(m_mutex);
|
||||
pfds[0].events = 0;
|
||||
if ((m_connected & kRead) != 0) {
|
||||
// still open for reading
|
||||
pfds[0].events |= POLLIN;
|
||||
}
|
||||
if ((m_connected & kWrite) != 0 && m_output->getSize() > 0) {
|
||||
// data queued for writing
|
||||
pfds[0].events |= POLLOUT;
|
||||
}
|
||||
}
|
||||
|
||||
// check for status
|
||||
CThread::testCancel();
|
||||
const int status = poll(pfds, 1, 50);
|
||||
CThread::testCancel();
|
||||
|
||||
// transfer data and handle errors
|
||||
if (status == 1) {
|
||||
if ((pfds[0].revents & (POLLERR | POLLNVAL)) != 0) {
|
||||
// stream is no good anymore so bail
|
||||
m_input->hangup();
|
||||
return;
|
||||
}
|
||||
|
||||
// read some data
|
||||
if (pfds[0].revents & POLLIN) {
|
||||
UInt8 buffer[4096];
|
||||
ssize_t n = read(m_fd, buffer, sizeof(buffer));
|
||||
if (n > 0) {
|
||||
CLock lock(m_mutex);
|
||||
m_input->write(buffer, n);
|
||||
}
|
||||
else if (n == 0) {
|
||||
// stream hungup
|
||||
m_input->hangup();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// write some data
|
||||
if (pfds[0].revents & POLLOUT) {
|
||||
CLock lock(m_mutex);
|
||||
|
||||
// get amount of data to write
|
||||
UInt32 n = m_output->getSize();
|
||||
if (n > 4096) {
|
||||
// limit write size
|
||||
n = 4096;
|
||||
}
|
||||
|
||||
// write data
|
||||
const void* buffer = m_output->peek(n);
|
||||
n = write(m_fd, buffer, n);
|
||||
|
||||
// discard written data
|
||||
if (n > 0) {
|
||||
m_output->pop(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CTCPSocket::closeInput(void*) throw()
|
||||
{
|
||||
// note -- m_mutex should already be locked
|
||||
shutdown(m_fd, 0);
|
||||
m_connected &= ~kRead;
|
||||
}
|
||||
|
||||
void CTCPSocket::closeOutput(void*) throw()
|
||||
{
|
||||
// note -- m_mutex should already be locked
|
||||
shutdown(m_fd, 1);
|
||||
m_connected &= ~kWrite;
|
||||
}
|
||||
49
net/CTCPSocket.h
Normal file
49
net/CTCPSocket.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#ifndef CTCPSOCKET_H
|
||||
#define CTCPSOCKET_H
|
||||
|
||||
#include "ISocket.h"
|
||||
#include "XThread.h"
|
||||
|
||||
class CMutex;
|
||||
template <class T>
|
||||
class CCondVar;
|
||||
class CThread;
|
||||
class CBufferedInputStream;
|
||||
class CBufferedOutputStream;
|
||||
|
||||
class CTCPSocket : public ISocket {
|
||||
public:
|
||||
CTCPSocket() throw(XSocket);
|
||||
CTCPSocket(int fd) throw();
|
||||
~CTCPSocket();
|
||||
|
||||
// manipulators
|
||||
|
||||
// accessors
|
||||
|
||||
// ISocket overrides
|
||||
virtual void bind(const CNetworkAddress&) throw(XSocket);
|
||||
virtual void connect(const CNetworkAddress&) throw(XSocket);
|
||||
virtual void close() throw(XIO);
|
||||
virtual IInputStream* getInputStream() throw();
|
||||
virtual IOutputStream* getOutputStream() throw();
|
||||
|
||||
private:
|
||||
void init() throw(XIO);
|
||||
void service(void*) throw(XThread);
|
||||
void closeInput(void*) throw();
|
||||
void closeOutput(void*) throw();
|
||||
|
||||
private:
|
||||
enum { kClosed = 0, kRead = 1, kWrite = 2, kReadWrite = 3 };
|
||||
|
||||
int m_fd;
|
||||
CBufferedInputStream* m_input;
|
||||
CBufferedOutputStream* m_output;
|
||||
|
||||
CMutex* m_mutex;
|
||||
CThread* m_thread;
|
||||
UInt32 m_connected;
|
||||
};
|
||||
|
||||
#endif
|
||||
27
net/IListenSocket.h
Normal file
27
net/IListenSocket.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef ILISTENSOCKET_H
|
||||
#define ILISTENSOCKET_H
|
||||
|
||||
#include "IInterface.h"
|
||||
#include "XIO.h"
|
||||
#include "XSocket.h"
|
||||
|
||||
class CNetworkAddress;
|
||||
class ISocket;
|
||||
|
||||
class IListenSocket : public IInterface {
|
||||
public:
|
||||
// manipulators
|
||||
|
||||
// bind the socket to a particular address
|
||||
virtual void bind(const CNetworkAddress&) throw(XSocket) = 0;
|
||||
|
||||
// wait for a connection
|
||||
virtual ISocket* accept() throw(XSocket) = 0;
|
||||
|
||||
// close the socket
|
||||
virtual void close() throw(XIO) = 0;
|
||||
|
||||
// accessors
|
||||
};
|
||||
|
||||
#endif
|
||||
35
net/ISocket.h
Normal file
35
net/ISocket.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#ifndef ISOCKET_H
|
||||
#define ISOCKET_H
|
||||
|
||||
#include "IInterface.h"
|
||||
#include "BasicTypes.h"
|
||||
#include "XSocket.h"
|
||||
#include "XIO.h"
|
||||
|
||||
class CNetworkAddress;
|
||||
class IInputStream;
|
||||
class IOutputStream;
|
||||
|
||||
class ISocket : public IInterface {
|
||||
public:
|
||||
// manipulators
|
||||
|
||||
// bind the socket to a particular address
|
||||
virtual void bind(const CNetworkAddress&) throw(XSocket) = 0;
|
||||
|
||||
// connect the socket
|
||||
virtual void connect(const CNetworkAddress&) throw(XSocket) = 0;
|
||||
|
||||
// close the socket. this will flush the output stream if it
|
||||
// hasn't been closed yet.
|
||||
virtual void close() throw(XIO) = 0;
|
||||
|
||||
// get the input and output streams for the socket. closing
|
||||
// these streams closes the appropriate half of the socket.
|
||||
virtual IInputStream* getInputStream() throw() = 0;
|
||||
virtual IOutputStream* getOutputStream() throw() = 0;
|
||||
|
||||
// accessors
|
||||
};
|
||||
|
||||
#endif
|
||||
28
net/Makefile
Normal file
28
net/Makefile
Normal file
@@ -0,0 +1,28 @@
|
||||
DEPTH=..
|
||||
include $(DEPTH)/Makecommon
|
||||
|
||||
#
|
||||
# target file
|
||||
#
|
||||
TARGET = net
|
||||
|
||||
#
|
||||
# source files
|
||||
#
|
||||
LCXXINCS = \
|
||||
-I$(DEPTH)/base \
|
||||
-I$(DEPTH)/mt \
|
||||
-I$(DEPTH)/io \
|
||||
$(NULL)
|
||||
CXXFILES = \
|
||||
XSocket.cpp \
|
||||
CNetworkAddress.cpp \
|
||||
CTCPSocket.cpp \
|
||||
CTCPListenSocket.cpp \
|
||||
$(NULL)
|
||||
|
||||
targets: $(LIBTARGET)
|
||||
|
||||
$(LIBTARGET): $(OBJECTS)
|
||||
if test ! -d $(LIBDIR); then $(MKDIR) $(LIBDIR); fi
|
||||
$(ARF) $(LIBTARGET) $(OBJECTS)
|
||||
84
net/XSocket.cpp
Normal file
84
net/XSocket.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
#include "XSocket.h"
|
||||
|
||||
//
|
||||
// XSocketAddress
|
||||
//
|
||||
|
||||
XSocketAddress::XSocketAddress(Error error,
|
||||
const CString& hostname, SInt16 port) throw() :
|
||||
m_error(error),
|
||||
m_hostname(hostname),
|
||||
m_port(port)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
XSocketAddress::Error XSocketAddress::getError() const throw()
|
||||
{
|
||||
return m_error;
|
||||
}
|
||||
|
||||
CString XSocketAddress::getHostname() const throw()
|
||||
{
|
||||
return m_hostname;
|
||||
}
|
||||
|
||||
SInt16 XSocketAddress::getPort() const throw()
|
||||
{
|
||||
return m_port;
|
||||
}
|
||||
|
||||
CString XSocketAddress::getWhat() const throw()
|
||||
{
|
||||
return "no address";
|
||||
/*
|
||||
return format("XSocketAddress", "no address: %1:%2",
|
||||
m_hostname.t_str(),
|
||||
CString::sprintf("%d", m_port).t_str());
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// XSocketErrno
|
||||
//
|
||||
|
||||
XSocketErrno::XSocketErrno() : MXErrno()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
XSocketErrno::XSocketErrno(int err) : MXErrno(err)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// XSocketBind
|
||||
//
|
||||
|
||||
CString XSocketBind::getWhat() const throw()
|
||||
{
|
||||
return format("XSocketBind", "cannot bind address");
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// XSocketConnect
|
||||
//
|
||||
|
||||
CString XSocketConnect::getWhat() const throw()
|
||||
{
|
||||
return format("XSocketConnect", "cannot connect socket");
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// XSocketCreate
|
||||
//
|
||||
|
||||
CString XSocketCreate::getWhat() const throw()
|
||||
{
|
||||
return format("XSocketCreate", "cannot create socket");
|
||||
}
|
||||
58
net/XSocket.h
Normal file
58
net/XSocket.h
Normal file
@@ -0,0 +1,58 @@
|
||||
#ifndef XSOCKET_H
|
||||
#define XSOCKET_H
|
||||
|
||||
#include "CString.h"
|
||||
#include "XBase.h"
|
||||
#include "BasicTypes.h"
|
||||
|
||||
class XSocket : public XBase { };
|
||||
|
||||
class XSocketAddress : public XSocket {
|
||||
public:
|
||||
enum Error { kUnknown, kNotFound, kNoAddress, kBadPort };
|
||||
|
||||
XSocketAddress(Error, const CString& hostname, SInt16 port) throw();
|
||||
|
||||
// accessors
|
||||
|
||||
virtual Error getError() const throw();
|
||||
virtual CString getHostname() const throw();
|
||||
virtual SInt16 getPort() const throw();
|
||||
|
||||
protected:
|
||||
// XBase overrides
|
||||
virtual CString getWhat() const throw();
|
||||
|
||||
private:
|
||||
Error m_error;
|
||||
CString m_hostname;
|
||||
SInt16 m_port;
|
||||
};
|
||||
|
||||
class XSocketErrno : public XSocket, public MXErrno {
|
||||
public:
|
||||
XSocketErrno();
|
||||
XSocketErrno(int);
|
||||
};
|
||||
|
||||
class XSocketBind : public XSocketErrno {
|
||||
protected:
|
||||
// XBase overrides
|
||||
virtual CString getWhat() const throw();
|
||||
};
|
||||
|
||||
class XSocketAddressInUse : public XSocketBind { };
|
||||
|
||||
class XSocketConnect : public XSocketErrno {
|
||||
protected:
|
||||
// XBase overrides
|
||||
virtual CString getWhat() const throw();
|
||||
};
|
||||
|
||||
class XSocketCreate : public XSocketErrno {
|
||||
protected:
|
||||
// XBase overrides
|
||||
virtual CString getWhat() const throw();
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user