0ad/source/network/Network.h
janwas 8a52113e60 huge cleanup and conversion of most string handling (especially paths) to unicode
please note: format strings must be %hs for char* arguments and %ls for
wchar_t*

This was SVN commit r7161.
2009-11-03 21:46:35 +00:00

293 lines
7.0 KiB
C++

/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
/*
Network.h
OVERVIEW
Contains the public interfaces to the networking code.
CMessageSocket is a socket that sends and receives messages from the
network. The global interface for sending and receiving messages is
an IMessagePipeEnd.
CMessagePipe also uses IMessagePipeEnd as its public interface, meaning that
a CMessageSocket can be invisibly replaced with a CMessagePipe. Thus, the
difference between MP and SP games is the source of pipe ends.
Code that just wants to send messages will most likely only be confronted
with the message pipe end interface.
EXAMPLES
To create a queue pair for IPC communication:
CMessagePipe pipe;
StartThread1(pipe[0]);
StartThread2(pipe[1]);
The argument type for StartThreadX would be "IMessagePipeEnd &".
NOTES ON THREAD SAFETY
All operations on an IMessagePipeEnd are fully thread-secure. Multiple access
to other interfaces of a CMessageSocket is not secure (but the IMessagePipeEnd
interface to a CMessageSocket is still fully thread secure)
MORE INFO
*/
#ifndef INCLUDED_NETWORK_NETWORK
#define INCLUDED_NETWORK_NETWORK
//--------------------------------------------------------
// Includes / Compiler directives
//--------------------------------------------------------
#include "ps/Pyrogenesis.h"
#include "ps/ThreadUtil.h"
#include "StreamSocket.h"
#include "NetMessage.h"
#include <deque>
#include <map>
//-------------------------------------------------
// Typedefs and Macros
//-------------------------------------------------
#define BLOCK_SIZE 4096
#define ALIGN_UP( _n, _block ) ( _n + _block - (_n % _block ) )
#define ALIGN_BLOCK( _n ) ALIGN_UP( _n, BLOCK_SIZE )
typedef CLocker<std::deque <CNetMessage *> > CLockedMessageDeque;
//-------------------------------------------------
// Error Codes
//-------------------------------------------------
DECLARE_ERROR( CONFLICTING_OP_IN_PROGRESS );
//-------------------------------------------------
// Declarations
//-------------------------------------------------
class IMessagePipeEnd;
class CMessagePipe;
class CMessageSocket;
class IMessagePipeEnd
{
public:
/**
* Push a message on the output queue. It will be freed when popped of the
* queue, not by the caller. The pointer must point to memory that can be
* safely freed by delete.
*/
virtual void Push(CNetMessage *msg)=0;
/**
* Try to pop a message from the input queue
*
* @return A pointer to the popped message, or NULL if the queue was empty
*/
virtual CNetMessage *TryPop()=0;
/**
* Wait for a message on the input queue
*
* Inputs
* pMsg: A pointer to a message struct to store the popped message
*
* Returns
* Void. The function returns successfully or blocks indefinitely.
*/
// virtual void WaitPop(CNetMessage *)=0;
};
/**
* A message pipe with two ends, communication flowing in both directions.
* The two ends are indexed with the [] operator or the GetEnd() method.
* Each end has two associated queues, one input and one output queue. The
* input queue of one End is the output queue of the other End and vice versa.
*/
class CMessagePipe
{
private:
friend struct End;
struct End: public IMessagePipeEnd
{
CMessagePipe *m_pPipe;
CLockedMessageDeque *m_pIn;
CLockedMessageDeque *m_pOut;
inline End()
{}
inline End(CMessagePipe *pPipe, CLockedMessageDeque *pIn, CLockedMessageDeque *pOut):
m_pPipe(pPipe), m_pIn(pIn), m_pOut(pOut)
{}
virtual ~End() {}
virtual void Push(CNetMessage *);
virtual CNetMessage *TryPop();
//virtual void WaitPop(CNetMessage *);
};
CLockedMessageDeque m_Queues[2];
End m_Ends[2];
// pthread_cond_t m_CondVar;
pthread_mutex_t m_CondMutex;
public:
CMessagePipe();
/**
* Return one of the two ends of the pipe
*/
inline IMessagePipeEnd &operator [] (int idx)
{
return GetEnd(idx);
}
/**
* Return one of the two ends of the pipe
*/
inline IMessagePipeEnd &GetEnd(int idx)
{
debug_assert(idx==1 || idx==0);
return m_Ends[idx];
}
};
class CServerSocket: public CSocketBase
{
protected:
/**
* The default implementation of this method accepts an incoming connection
* and calls OnAccept() with the accepted internal socket instance.
*
* NOTE: Subclasses should never overload this method, overload OnAccept()
* instead.
*/
virtual void OnRead();
virtual void OnWrite();
virtual void OnClose(PS_RESULT errorCode);
public:
virtual ~CServerSocket();
/**
* There is an incoming connection in the queue. Examine the SocketAddress
* and call Accept() or Reject() to accept or reject the incoming
* connection
*
* @see CSocketBase::Accept()
* @see CSocketBase::Reject()
*/
virtual void OnAccept(const CSocketAddress &)=0;
};
class CErrorMessage: public CNetMessage
{
public:
PS_RESULT m_Error;
ESocketState m_State;
inline CErrorMessage():
CNetMessage(NMT_ERROR)
{}
inline CErrorMessage(PS_RESULT error, ESocketState state):
CNetMessage(NMT_ERROR),
m_Error(error),
m_State(state)
{}
virtual CStr ToString() const;
};
struct CCloseRequestMessage: public CNetMessage
{
inline CCloseRequestMessage(): CNetMessage(NMT_CLOSE_REQUEST)
{}
virtual CStr ToString() const;
};
struct CConnectCompleteMessage: public CNetMessage
{
inline CConnectCompleteMessage(): CNetMessage(NMT_CONNECT_COMPLETE)
{}
virtual CStr ToString() const;
};
/**
* Implements a Message Pipe over an Async IO stream socket.
*
* All methods that this class exposes are thread safe and may be called from
* any thread.
*/
class CMessageSocket: protected CStreamSocket, public IMessagePipeEnd
{
bool m_IsWriting;
u8 *m_pWrBuffer;
size_t m_WrBufferSize;
bool m_ReadingData;
u8 *m_pRdBuffer;
size_t m_RdBufferSize;
CLockedMessageDeque m_InQ; // Messages read from socket
CLockedMessageDeque m_OutQ;// Messages to write to socket
// pthread_cond_t m_InCond;
// pthread_cond_t m_OutCond;
void StartWriteNextMessage();
void StartReadHeader();
void StartReadMessage();
protected:
virtual void ReadComplete(PS_RESULT);
virtual void WriteComplete(PS_RESULT);
virtual void OnClose(PS_RESULT);
virtual void ConnectComplete(PS_RESULT);
virtual void OnMessage(CNetMessage *pMsg);
public:
CMessageSocket(CSocketInternal *pInt);
CMessageSocket();
virtual ~CMessageSocket();
virtual void Push(CNetMessage *);
virtual CNetMessage *TryPop();
/**
* See CStreamSocket::BeginConnect.
*/
PS_RESULT BeginConnect(const char *address, int port);
};
#endif