This commit is contained in:
FluorescentCIAAfricanAmerican
2020-04-22 12:56:21 -04:00
commit 3bf9df6b27
15370 changed files with 5489726 additions and 0 deletions

View File

@ -0,0 +1,922 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//===========================================================================//
#include "NetChannel.h"
#include "UDP_Socket.h"
#include "tier1/utlbuffer.h"
#include "networksystem/inetworkmessage.h"
#include "networksystem.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
// Construction/Destruction
//-----------------------------------------------------------------------------
CNetChannel::CNetChannel()
{
m_pSocket = NULL; // invalid
remote_address.Clear();
last_received = 0;
connect_time = 0;
m_ConnectionState = CONNECTION_STATE_DISCONNECTED;
Q_strncpy( m_Name, "", sizeof(m_Name) );
m_MessageHandler = NULL;
m_StreamUnreliable.StartWriting(m_UnreliableDataBuffer, sizeof(m_UnreliableDataBuffer));
m_StreamUnreliable.SetDebugName( "netchan_t::unreliabledata" );
m_StreamReliable.StartWriting(m_ReliableDataBuffer, sizeof(m_ReliableDataBuffer));
m_StreamReliable.SetDebugName( "netchan_t::reliabledata" );
m_Rate = DEFAULT_RATE;
m_Timeout = SIGNON_TIME_OUT;
m_PacketDrop = 0;
// Prevent the first message from getting dropped after connection is set up.
m_nOutSequenceNr = 1; // otherwise it looks like a
m_nInSequenceNr = 0;
m_nOutSequenceNrAck = 0;
m_nOutReliableState = 0; // our current reliable state
m_nInReliableState = 0; // last remote reliable state
// FlowReset();
}
CNetChannel::~CNetChannel()
{
Shutdown( "NetChannel removed." );
}
//-----------------------------------------------------------------------------
// called to open a channel to a remote system
//-----------------------------------------------------------------------------
void CNetChannel::Setup( bool serverSide, const netadr_t *adr, CUDPSocket *sendSocket, char const *name, INetworkMessageHandler *handler )
{
Assert( name );
Assert( handler );
Assert( adr );
m_pSocket = sendSocket;
// remote_address may be NULL for fake channels (demo playback etc)
remote_address = *adr;
last_received = g_pNetworkSystemImp->GetTime();
connect_time = last_received;
Q_strncpy( m_Name, name, sizeof(m_Name) );
m_MessageHandler = handler;
m_StreamUnreliable.StartWriting(m_UnreliableDataBuffer, sizeof(m_UnreliableDataBuffer));
m_StreamUnreliable.SetDebugName( "netchan_t::unreliabledata" );
m_ReliableDataBufferMP.EnsureCapacity( NET_MAX_PAYLOAD );
m_StreamReliable.StartWriting( m_ReliableDataBufferMP.Base(), NET_MAX_PAYLOAD );
m_StreamReliable.SetDebugName( "netchan_t::reliabledata" );
m_Rate = DEFAULT_RATE;
m_Timeout = SIGNON_TIME_OUT;
m_PacketDrop = 0;
// Prevent the first message from getting dropped after connection is set up.
m_nOutSequenceNr = 1; // otherwise it looks like a
m_nInSequenceNr = 0;
m_nOutSequenceNrAck = 0;
m_nOutReliableState = 0; // our current reliable state
m_nInReliableState = 0; // last remote reliable state
m_nChokedPackets = 0;
m_fClearTime = 0.0;
m_ConnectionState = CONNECTION_STATE_CONNECTED;
// FlowReset();
// tell message handler to register know netmessages
m_MessageHandler->OnConnectionStarted( this );
}
void CNetChannel::Shutdown( const char *pReason )
{
// send discconect
if ( !m_pSocket )
return;
Clear(); // free all buffers (reliable & unreliable)
if ( pReason )
{
// send disconnect message
WriteSystemNetworkMessage( m_StreamUnreliable, net_disconnect );
m_StreamUnreliable.WriteString( pReason );
Transmit(); // push message out
}
m_pSocket = NULL; // signals that netchannel isn't valid anymore
remote_address.Clear();
m_ConnectionState = CONNECTION_STATE_DISCONNECTED;
if ( m_MessageHandler )
{
m_MessageHandler->OnConnectionClosing( this, pReason );
m_MessageHandler = NULL;
}
}
//-----------------------------------------------------------------------------
// Channel connection state
//-----------------------------------------------------------------------------
ConnectionStatus_t CNetChannel::GetConnectionState( )
{
return m_ConnectionState;
}
void CNetChannel::SetConnectionState( ConnectionStatus_t state )
{
m_ConnectionState = state;
}
/*
void CNetChannel::GetSequenceData( int &nOutSequenceNr, int &nInSequenceNr, int &nOutSequenceNrAck )
{
nOutSequenceNr = m_nOutSequenceNr;
nInSequenceNr = m_nInSequenceNr;
nOutSequenceNrAck = m_nOutSequenceNrAck;
}
void CNetChannel::SetSequenceData( int nOutSequenceNr, int nInSequenceNr, int nOutSequenceNrAck )
{
Assert( IsPlayback() );
m_nOutSequenceNr = nOutSequenceNr;
m_nInSequenceNr = nInSequenceNr;
m_nOutSequenceNrAck = nOutSequenceNrAck;
}
*/
void CNetChannel::SetTimeout(float seconds)
{
m_Timeout = seconds;
if ( m_Timeout > 3600.0f )
{
m_Timeout = 3600.0f; // 1 hour maximum
}
else if ( m_Timeout < CONNECTION_PROBLEM_TIME )
{
m_Timeout = CONNECTION_PROBLEM_TIME; // allow at least this minimum
}
}
void CNetChannel::SetDataRate(float rate)
{
m_Rate = clamp( rate, (float)MIN_RATE, (float)MAX_RATE );
}
const char * CNetChannel::GetName() const
{
return m_Name;
}
const char * CNetChannel::GetAddress() const
{
return remote_address.ToString();
}
/*
int CNetChannel::GetDropNumber() const
{
return m_PacketDrop;
}
*/
/*
===============
CNetChannel::CanSendPacket
Returns true if the bandwidth choke isn't active
================
*/
bool CNetChannel::CanSendPacket () const
{
return m_fClearTime < g_pNetworkSystemImp->GetTime();
}
/*
void CNetChannel::FlowReset( void )
{
Q_memset( m_DataFlow, 0, sizeof( m_DataFlow ) );
Q_memset( m_MsgStats, 0, sizeof( m_MsgStats ) );
}
void CNetChannel::FlowNewPacket(int flow, int seqnr, int acknr, int nChoked, int nSize )
{
netflow_t * pflow = &m_DataFlow[ flow ];
// if frame_number != ( current + 1 ) mark frames between as invalid
netframe_t *pframe = NULL;
if ( seqnr > pflow->currentindex )
{
for ( int i = pflow->currentindex+1; i <= seqnr; i++ )
{
pframe = &pflow->frames[ i & NET_FRAMES_MASK ];
pframe->time = GetTime(); // now
pframe->valid = false;
pframe->size = 0;
pframe->latency = -1.0f; // not acknowledged yet
pframe->choked = 0; // not acknowledged yet
Q_memset( &pframe->msggroups, 0, sizeof(pframe->msggroups) );
}
pframe->choked = nChoked;
pframe->size = nSize;
pframe->valid = true;
}
else
{
Assert( seqnr > pflow->currentindex );
}
pflow->totalpackets++;
pflow->currentindex = seqnr;
pflow->currentframe = pframe;
// updated ping for acknowledged packet
int aflow = (flow==FLOW_OUTGOING) ? FLOW_INCOMING : FLOW_OUTGOING;
if ( acknr <= (m_DataFlow[aflow].currentindex - NET_FRAMES_BACKUP) )
return; // acknowledged packet isn't in backup buffer anymore
netframe_t * aframe = &m_DataFlow[aflow].frames[ acknr & NET_FRAMES_MASK ];
if ( aframe->valid && aframe->latency == -1.0f )
{
// update ping for acknowledged packet, if not already acknowledged before
aframe->latency = GetTime() - aframe->time;
if ( aframe->latency < 0.0f )
aframe->latency = 0.0f;
}
}
void CNetChannel::FlowUpdate(int flow, int addbytes)
{
netflow_t * pflow = &m_DataFlow[ flow ];
pflow->totalbytes += addbytes;
if ( pflow->nextcompute > GetTime() )
return;
pflow->nextcompute = GetTime() + FLOW_INTERVAL;
int totalvalid = 0;
int totalinvalid = 0;
int totalbytes = 0;
float totallatency = 0.0f;
int totallatencycount = 0;
int totalchoked = 0;
float starttime = FLT_MAX;
float endtime = 0.0f;
netframe_t *pprev = &pflow->frames[ NET_FRAMES_BACKUP-1 ];
for ( int i = 0; i < NET_FRAMES_BACKUP; i++ )
{
// Most recent message then backward from there
netframe_t * pcurr = &pflow->frames[ i ];
if ( pcurr->valid )
{
if ( pcurr->time < starttime )
starttime = pcurr->time;
if ( pcurr->time > endtime )
endtime = pcurr->time;
totalvalid++;
totalchoked += pcurr->choked;
totalbytes += pcurr->size;
if ( pcurr->latency > -1.0f )
{
totallatency += pcurr->latency;
totallatencycount++;
}
}
else
{
totalinvalid++;
}
pprev = pcurr;
}
float totaltime = endtime - starttime;
if ( totaltime > 0.0f )
{
pflow->avgbytespersec *= FLOW_AVG;
pflow->avgbytespersec += ( 1.0f - FLOW_AVG ) * ((float)totalbytes / totaltime);
pflow->avgpacketspersec *= FLOW_AVG;
pflow->avgpacketspersec += ( 1.0f - FLOW_AVG ) * ((float)totalvalid / totaltime);
}
int totalPackets = totalvalid + totalinvalid;
if ( totalPackets > 0 )
{
pflow->avgloss *= FLOW_AVG;
pflow->avgloss += ( 1.0f - FLOW_AVG ) * ((float)(totalinvalid-totalchoked)/totalPackets);
if ( pflow->avgloss < 0 )
pflow->avgloss = 0;
pflow->avgchoke *= FLOW_AVG;
pflow->avgchoke += ( 1.0f - FLOW_AVG ) * ((float)totalchoked/totalPackets);
}
if ( totallatencycount>0 )
{
float newping = totallatency / totallatencycount ;
pflow->latency = newping;
pflow->avglatency*= FLOW_AVG;
pflow->avglatency += ( 1.0f - FLOW_AVG ) * newping;
}
}
*/
void CNetChannel::SetChoked( void )
{
m_nOutSequenceNr++; // sends to be done since move command use sequence number
m_nChokedPackets++;
}
bool CNetChannel::Transmit( bool onlyReliable /* =false */ )
{
if ( onlyReliable )
{
m_StreamUnreliable.Reset();
}
return ( SendDatagram( NULL ) != 0 );
}
/*
===============
CNetChannel::TransmitBits
tries to send an unreliable message to a connection, and handles the
transmition / retransmition of the reliable messages.
A 0 length will still generate a packet and deal with the reliable messages.
================
*/
int CNetChannel::SendDatagram( bf_write *datagram )
{
byte send_buf[ NET_MAX_MESSAGE ];
// first increase out sequence number
// check, if fake client, then fake send also
if ( remote_address.GetType() == NA_NULL )
{
// this is a demo channel, fake sending all data
m_fClearTime = 0.0; // no bandwidth delay
m_nChokedPackets = 0; // Reset choke state
m_StreamReliable.Reset(); // clear current reliable buffer
m_StreamUnreliable.Reset(); // clear current unrelaible buffer
m_nOutSequenceNr++;
return m_nOutSequenceNr-1;
}
// process all new and pending reliable data, return true if reliable data should
// been send with this packet
if ( m_StreamReliable.IsOverflowed() )
{
Msg ("%s:send reliable stream overflow\n" ,remote_address.ToString());
return 0;
}
bf_write send( "CNetChannel_TransmitBits->send", send_buf, sizeof(send_buf) );
// Prepare the packet header
// build packet flags
unsigned char flags = 0;
// start writing packet
send.WriteLong ( m_nOutSequenceNr );
send.WriteLong ( m_nInSequenceNr );
bf_write flagsPos = send; // remember flags byte position
send.WriteByte ( 0 ); // write correct flags value later
send.WriteByte ( m_nInReliableState );
if ( m_nChokedPackets > 0 )
{
flags |= PACKET_FLAG_CHOKED;
send.WriteByte ( m_nChokedPackets & 0xFF ); // send number of choked packets
}
/*
if ( SendSubChannelData( send ) )
{
flags |= PACKET_FLAG_RELIABLE;
}
*/
// Is there room for given datagram data. the datagram data
// is somewhat more important than the normal unreliable data
// this is done to allow some kind of snapshot behaviour
// weather all data in datagram is transmitted or none.
if ( datagram )
{
if( datagram->GetNumBitsWritten() < send.GetNumBitsLeft() )
{
send.WriteBits( datagram->GetData(), datagram->GetNumBitsWritten() );
}
else
{
DevMsg("CNetChannel::SendDatagram: data would overfow, ignoring\n");
}
}
// Is there room for the unreliable payload?
if ( m_StreamUnreliable.GetNumBitsWritten() < send.GetNumBitsLeft() )
{
send.WriteBits(m_StreamUnreliable.GetData(), m_StreamUnreliable.GetNumBitsWritten() );
}
else
{
DevMsg("CNetChannel::SendDatagram: Unreliable would overfow, ignoring\n");
}
m_StreamUnreliable.Reset(); // clear unreliable data buffer
// Deal with packets that are too small for some networks
while ( send.GetNumBytesWritten() < MIN_ROUTEABLE_PACKET )
{
// Go ahead and pad some bits as long as needed
WriteSystemNetworkMessage( send, net_nop );
}
// fill last bits in last byte with NOP if necessary
int nRemainingBits = send.GetNumBitsWritten() % 8;
int nHeaderSize = g_pNetworkSystemImp->GetGroupBitCount() + g_pNetworkSystemImp->GetTypeBitCount();
while ( nRemainingBits > 0 && nRemainingBits <= ( 8 - nHeaderSize ) )
{
WriteSystemNetworkMessage( send, net_nop );
nRemainingBits += nHeaderSize;
}
flagsPos.WriteByte( flags ); // write correct flags value
// Send the datagram
m_pSocket->SendTo( remote_address, send.GetData(), send.GetNumBytesWritten() );
// update stats
int nTotalSize = send.GetNumBytesWritten() + UDP_HEADER_SIZE;
// FlowNewPacket( FLOW_OUTGOING, m_nOutSequenceNr, m_nInSequenceNr, m_nChokedPackets, nTotalSize );
// FlowUpdate( FLOW_OUTGOING, nTotalSize );
float flTime = g_pNetworkSystemImp->GetTime();
if ( m_fClearTime < flTime )
{
m_fClearTime = flTime;
}
// calc cleantime when channel will be ready for next packet
Assert( m_Rate != 0.0f );
m_fClearTime += (float)( nTotalSize ) / (float) m_Rate;
m_nChokedPackets = 0;
m_nOutSequenceNr++;
return m_nOutSequenceNr-1; // return send seq nr
}
bool CNetChannel::ProcessControlMessage( int cmd, bf_read &buf )
{
switch( cmd )
{
case net_nop:
return true;
case net_disconnect:
{
char pReason[1024];
buf.ReadString( pReason, sizeof(pReason) );
Shutdown( pReason );
}
return false;
default:
Msg( "CNetChannel: received bad control cmd %i from %s.\n", cmd, remote_address.ToString() );
return false;
}
}
bool CNetChannel::ProcessMessages( bf_read &buf )
{
//int startbit = buf.GetNumBitsRead();
int nGroupCount = g_pNetworkSystemImp->GetGroupBitCount();
int nTypeCount = g_pNetworkSystemImp->GetTypeBitCount();
while ( true )
{
if ( buf.IsOverflowed() )
return false;
// Are we at the end?
if ( buf.GetNumBitsLeft() < ( nGroupCount + nTypeCount ) )
break;
unsigned int group = buf.ReadUBitLong( nGroupCount );
unsigned int type = buf.ReadUBitLong( nTypeCount );
if ( group == net_group_networksystem )
{
if ( !ProcessControlMessage( type, buf ) )
return g_pNetworkSystemImp->IsNetworkEventCreated(); // disconnect or error
continue;
}
// see if we have a registered message object for this type
INetworkMessage *pNetMessage = g_pNetworkSystemImp->FindNetworkMessage( group, type );
if ( !pNetMessage )
{
Msg( "Netchannel: unknown net message (%i:%i) from %s.\n", group, type, remote_address.ToString() );
Assert ( 0 );
return false;
}
// Attach it to the correct netchannel
pNetMessage->SetNetChannel( this );
// let message parse itself from buffe
const char *pGroupName = pNetMessage->GetGroupName();
const char *pMessageName = pNetMessage->GetName();
//int startbit = buf.GetNumBitsRead();
if ( !pNetMessage->ReadFromBuffer( buf ) )
{
Msg( "Netchannel: failed reading message %s [%s] from %s.\n", pMessageName, pGroupName, remote_address.ToString() );
Assert ( 0 );
return false;
}
// UpdateMessageStats( netmsg->GetGroup(), buf.GetNumBitsRead() - startbit );
// Create a network event
NetworkMessageReceivedEvent_t *pReceived = g_pNetworkSystemImp->CreateNetworkEvent< NetworkMessageReceivedEvent_t >( );
pReceived->m_nType = NETWORK_EVENT_MESSAGE_RECEIVED;
pReceived->m_pChannel = this;
pReceived->m_pNetworkMessage = pNetMessage;
return true; // ok fine
}
return false; // ok fine, but don't keep processing this packet
}
int CNetChannel::ProcessPacketHeader( bf_read& message )
{
// get sequence numbers
int sequence = message.ReadLong();
int sequence_ack= message.ReadLong();
int flags = message.ReadByte();
int relState = message.ReadByte(); // reliable state of 8 subchannels
int nChoked = 0; // read later if choked flag is set
//int i,j;
NOTE_UNUSED( relState );
if ( flags & PACKET_FLAG_CHOKED )
nChoked = message.ReadByte();
// discard stale or duplicated packets
if (sequence <= m_nInSequenceNr )
{
/*
if ( net_showdrop.GetInt() )
{
if ( sequence == m_nInSequenceNr )
{
Msg ("%s:duplicate packet %i at %i\n"
, remote_address.ToString ()
, sequence
, m_nInSequenceNr);
}
else
{
Msg ("%s:out of order packet %i at %i\n"
, remote_address.ToString ()
, sequence
, m_nInSequenceNr);
}
}
*/
return -1;
}
//
// dropped packets don't keep the message from being used
//
m_PacketDrop = sequence - (m_nInSequenceNr + nChoked + 1);
if ( m_PacketDrop > 0 )
{
/*
if ( net_showdrop.GetInt() )
{
Msg ("%s:Dropped %i packets at %i\n"
,remote_address.ToString(), m_PacketDrop, sequence );
}
*/
}
m_nInSequenceNr = sequence;
m_nOutSequenceNrAck = sequence_ack;
// Update data flow stats
// FlowNewPacket( FLOW_INCOMING, m_nInSequenceNr, m_nOutSequenceNrAck, nChoked, packet->size + UDP_HEADER_SIZE );
return flags;
}
//-----------------------------------------------------------------------------
// CNetChannel::ProcessPacket
//
// called when a new packet has arrived for this netchannel
// sequence numbers are extracted, fragments/file streams stripped
// and then the netmessages processed
//-----------------------------------------------------------------------------
bool CNetChannel::StartProcessingPacket( CNetPacket *packet )
{
if ( !m_MessageHandler )
return false;
netadr_t from = packet->m_From;
if ( remote_address.IsValid() && !from.CompareAdr ( remote_address ) )
return false;
// Update data flow stats
//FlowUpdate( FLOW_INCOMING, msg.TellPut() + UDP_HEADER_SIZE );
int flags = ProcessPacketHeader( packet->m_Message );
if ( flags == -1 )
return false; // invalid header/packet
/*
if ( net_showudp.GetInt() && net_showudp.GetInt() != 3 )
{
Msg ("UDP <- %s: sz=%i seq=%i ack=%i rel=%i tm=%f\n"
, GetName()
, packet->m_nSizeInBytes
, m_nInSequenceNr & 63
, m_nOutSequenceNrAck & 63
, flags & PACKET_FLAG_RELIABLE ? 1 : 0
, GetTime() );
}
*/
last_received = g_pNetworkSystemImp->GetTime();
// tell message handler that a new packet has arrived
m_MessageHandler->OnPacketStarted( m_nInSequenceNr, m_nOutSequenceNrAck );
if ( flags & PACKET_FLAG_RELIABLE )
{
/*
int i, bit = 1<<msg.ReadUBitLong( 3 );
for ( i=0; i<MAX_STREAMS; i++ )
{
if ( msg.ReadOneBit() != 0 )
{
if ( !ReadSubChannelData( msg, i ) )
return false; // error while reading fragments, drop whole packet
}
}
// flip subChannel bit to signal successfull receiving
FLIPBIT(m_nInReliableState, bit);
for ( i=0; i<MAX_STREAMS; i++ )
{
if ( !CheckReceivingList( i ) )
return false; // error while processing
}
*/
}
return true;
}
bool CNetChannel::ProcessPacket( CNetPacket *packet )
{
return ProcessMessages( packet->m_Message );
}
void CNetChannel::EndProcessingPacket( CNetPacket *packet )
{
// tell message handler that packet is completely parsed
if ( m_MessageHandler )
{
m_MessageHandler->OnPacketFinished();
}
}
bool CNetChannel::AddNetMsg( INetworkMessage *msg, bool bForceReliable )
{
if ( msg->IsReliable() || bForceReliable )
{
WriteNetworkMessage( m_StreamReliable, msg );
if ( m_StreamReliable.IsOverflowed() )
return false;
return msg->WriteToBuffer( m_StreamReliable );
}
WriteNetworkMessage( m_StreamUnreliable, msg );
if ( m_StreamUnreliable.IsOverflowed() )
return false;
return msg->WriteToBuffer( m_StreamUnreliable );
}
bool CNetChannel::AddData( bf_write &msg, bool bReliable )
{
// Always queue any pending reliable data ahead of the fragmentation buffer
if ( msg.GetNumBitsWritten() <= 0 )
return true;
bf_write * buf = bReliable ? &m_StreamReliable : &m_StreamUnreliable;
if ( msg.GetNumBitsWritten() > buf->GetNumBitsLeft() )
{
if ( bReliable )
{
Msg( "ERROR! SendData reliabe data too big (%i)", msg.GetNumBytesWritten() );
}
return false;
}
return buf->WriteBits( msg.GetData(), msg.GetNumBitsWritten() );
}
int CNetChannel::GetDataRate() const
{
return m_Rate;
}
bool CNetChannel::HasPendingReliableData( void )
{
return ( m_StreamReliable.GetNumBitsWritten() > 0 );
}
float CNetChannel::GetTimeConnected() const
{
float t = g_pNetworkSystemImp->GetTime() - connect_time;
return (t>0.0f) ? t : 0.0f ;
}
const netadr_t & CNetChannel::GetRemoteAddress() const
{
return remote_address;
}
bool CNetChannel::IsTimedOut() const
{
if ( m_Timeout == -1.0f )
return false;
else
return ( last_received + m_Timeout ) < g_pNetworkSystemImp->GetTime();
}
bool CNetChannel::IsTimingOut() const
{
if ( m_Timeout == -1.0f )
return false;
else
return (last_received + CONNECTION_PROBLEM_TIME) < g_pNetworkSystemImp->GetTime();
}
float CNetChannel::GetTimeSinceLastReceived() const
{
float t = g_pNetworkSystemImp->GetTime() - last_received;
return (t>0.0f) ? t : 0.0f ;
}
bool CNetChannel::IsOverflowed() const
{
return m_StreamReliable.IsOverflowed();
}
void CNetChannel::Clear()
{
Reset();
}
void CNetChannel::Reset()
{
// FlowReset();
m_StreamUnreliable.Reset(); // clear any pending unreliable data messages
m_StreamReliable.Reset(); // clear any pending reliable data messages
m_fClearTime = 0.0; // ready to send
m_nChokedPackets = 0;
}
CUDPSocket *CNetChannel::GetSocket()
{
return m_pSocket;
}
float CNetChannel::GetAvgData( int flow ) const
{
return 0.0f;
// return m_DataFlow[flow].avgbytespersec;
}
float CNetChannel::GetAvgPackets( int flow ) const
{
return 0.0f;
// return m_DataFlow[flow].avgpacketspersec;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *chan -
//-----------------------------------------------------------------------------
int CNetChannel::GetTotalData(int flow ) const
{
return 0;
// return m_DataFlow[flow].totalbytes;
}
/*
int CNetChannel::GetSequenceNr( int flow ) const
{
if ( flow == FLOW_OUTGOING )
{
return m_nOutSequenceNr;
}
else if ( flow == FLOW_INCOMING )
{
return m_nInSequenceNr;
}
return 0;
}
*/
float CNetChannel::GetLatency( int flow ) const
{
return 0.0f;
// return m_DataFlow[flow].latency;
}
float CNetChannel::GetAvgChoke( int flow ) const
{
return 0.0f;
//return m_DataFlow[flow].avgchoke;
}
float CNetChannel::GetAvgLatency( int flow ) const
{
return 0.0f;
//return m_DataFlow[flow].avglatency;
}
float CNetChannel::GetAvgLoss( int flow ) const
{
return 0.0f;
//return m_DataFlow[flow].avgloss;
}

274
networksystem/netchannel.h Normal file
View File

@ -0,0 +1,274 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================
// Should move to common/networksystem
#ifndef NETCHANNEL_H
#define NETCHANNEL_H
#ifdef _WIN32
#pragma once
#endif
#include "tier1/bitbuf.h"
#include "tier1/netadr.h"
#include "sm_Protocol.h"
#include "tier1/utlvector.h"
#include "networksystem/inetworksystem.h"
#include "tier1/mempool.h"
//-----------------------------------------------------------------------------
// Forward declarations
//-----------------------------------------------------------------------------
class CUDPSocket;
class CUtlBuffer;
class CNetPacket;
class CNetChannel;
class INetChannel;
// 0 == regular, 1 == file stream
enum
{
FRAG_NORMAL_STREAM = 0,
FRAG_FILE_STREAM,
MAX_STREAMS
};
#define NET_MAX_DATAGRAM_PAYLOAD 1400
#define NET_MAX_PAYLOAD_BITS 11 // 2^NET_MAX_PALYLOAD_BITS > NET_MAX_PAYLOAD
#define DEFAULT_RATE 10000
#define SIGNON_TIME_OUT 120.0f
#define CONNECTION_PROBLEM_TIME 15.0f
#define MAX_RATE 50000
#define MIN_RATE 100
#define FRAGMENT_BITS 8
#define FRAGMENT_SIZE (1<<FRAGMENT_BITS)
#define MAX_FILE_SIZE_BITS 26
#define MAX_FILE_SIZE ((1<<MAX_FILE_SIZE_BITS)-1) // maximum transferable size is 64MB
#define NET_MAX_PAYLOAD 4000
#define NET_MAX_MESSAGE 4096
#define MIN_ROUTEABLE_PACKET 16
#define MAX_ROUTEABLE_PACKET 1400 // Ethernet 1518 - ( CRC + IP + UDP header)
#define UDP_HEADER_SIZE 28
// each channel packet has 1 byte of FLAG bits
#define PACKET_FLAG_RELIABLE (1<<0) // packet contains subchannel stream data
#define PACKET_FLAG_CHOKED (1<<1) // packet was choked by sender
// shared commands used by all streams, handled by stream layer, TODO
abstract_class INetworkMessageHandler
{
public:
virtual void OnConnectionClosing( INetChannel *channel, char const *reason ) = 0;
virtual void OnConnectionStarted( INetChannel *channel ) = 0;
virtual void OnPacketStarted( int inseq, int outseq ) = 0;
virtual void OnPacketFinished() = 0;
protected:
virtual ~INetworkMessageHandler() {}
};
class INetChannelHandler
{
public:
virtual ~INetChannelHandler( void ) {};
virtual void ConnectionStart(INetChannel *chan) = 0; // called first time network channel is established
virtual void ConnectionClosing(const char *reason) = 0; // network channel is being closed by remote site
virtual void ConnectionCrashed(const char *reason) = 0; // network error occured
virtual void PacketStart(int incoming_sequence, int outgoing_acknowledged) = 0; // called each time a new packet arrived
virtual void PacketEnd( void ) = 0; // all messages has been parsed
virtual void FileRequested(const char *fileName, unsigned int transferID) = 0; // other side request a file for download
virtual void FileReceived(const char *fileName, unsigned int transferID) = 0; // we received a file
virtual void FileDenied(const char *fileName, unsigned int transferID) = 0; // a file request was denied by other side
};
// server to client
class CNetPacket
{
DECLARE_FIXEDSIZE_ALLOCATOR( CNetPacket );
public:
CNetPacket();
~CNetPacket();
void AddRef();
void Release();
public:
netadr_t m_From; // sender IP
CUDPSocket *m_pSource; // received source
float m_flReceivedTime; // received time
unsigned char *m_pData; // pointer to raw packet data
bf_read m_Message; // easy bitbuf data access
int m_nSizeInBytes; // size in bytes
private:
int m_nRefCount;// Reference count
};
abstract_class IConnectionlessPacketHandler
{
public:
virtual bool ProcessConnectionlessPacket( CNetPacket *packet ) = 0; // process a connectionless packet
protected:
virtual ~IConnectionlessPacketHandler( void ) {};
};
abstract_class ILookupChannel
{
public:
virtual INetChannel *FindNetChannel( const netadr_t& from ) = 0;
};
// FIXME: Make an INetChannel
class CNetChannel : public INetChannel
{
public:
explicit CNetChannel();
~CNetChannel();
// Methods of INetChannel
virtual ConnectionStatus_t GetConnectionState( );
virtual const netadr_t &GetRemoteAddress( void ) const;
void Setup( bool serverSide, const netadr_t *remote_address, CUDPSocket *sendSocket, char const *name, INetworkMessageHandler *handler );
void Reset();
void Clear();
void Shutdown( const char *reason );
CUDPSocket *GetSocket();
void SetDataRate( float rate );
void SetTimeout( float seconds );
bool StartProcessingPacket( CNetPacket *packet );
bool ProcessPacket( CNetPacket *packet );
void EndProcessingPacket( CNetPacket *packet );
bool CanSendPacket( void ) const;
void SetChoked( void ); // choke a packet
bool HasPendingReliableData( void );
// Queues data for sending:
// send a net message
bool AddNetMsg( INetworkMessage *msg, bool bForceReliable = false );
// send a chunk of data
bool AddData( bf_write &msg, bool bReliable = true );
// Puts data onto the wire:
int SendDatagram( bf_write *data ); // Adds data to unreliable payload and then calls transmits the data
bool Transmit( bool onlyReliable = false ); // send data from buffers (calls SendDataGram( NULL ) )
bool IsOverflowed( void ) const;
bool IsTimedOut( void ) const;
bool IsTimingOut() const;
// Info:
const char *GetName( void ) const;
const char *GetAddress( void ) const;
float GetTimeConnected( void ) const;
float GetTimeSinceLastReceived( void ) const;
int GetDataRate( void ) const;
float GetLatency( int flow ) const;
float GetAvgLatency( int flow ) const;
float GetAvgLoss( int flow ) const;
float GetAvgData( int flow ) const;
float GetAvgChoke( int flow ) const;
float GetAvgPackets( int flow ) const;
int GetTotalData( int flow ) const;
void SetConnectionState( ConnectionStatus_t state );
private:
int ProcessPacketHeader( bf_read &buf );
bool ProcessControlMessage( int cmd, bf_read &buf );
bool ProcessMessages( bf_read &buf );
ConnectionStatus_t m_ConnectionState;
// last send outgoing sequence number
int m_nOutSequenceNr;
// last received incoming sequnec number
int m_nInSequenceNr;
// last received acknowledge outgoing sequnce number
int m_nOutSequenceNrAck;
// state of outgoing reliable data (0/1) flip flop used for loss detection
int m_nOutReliableState;
// state of incoming reliable data
int m_nInReliableState;
int m_nChokedPackets; //number of choked packets
int m_PacketDrop;
// Reliable data buffer, send wich each packet (or put in waiting list)
bf_write m_StreamReliable;
byte m_ReliableDataBuffer[8 * 1024]; // In SP, we don't need much reliable buffer, so save the memory (this is mostly for xbox).
CUtlVector<byte> m_ReliableDataBufferMP;
// unreliable message buffer, cleared wich each packet
bf_write m_StreamUnreliable;
byte m_UnreliableDataBuffer[NET_MAX_DATAGRAM_PAYLOAD];
// don't use any vars below this (only in net_ws.cpp)
CUDPSocket *m_pSocket; // NS_SERVER or NS_CLIENT index, depending on channel.
int m_StreamSocket; // TCP socket handle
unsigned int m_MaxReliablePayloadSize; // max size of reliable payload in a single packet
// Address this channel is talking to.
netadr_t remote_address;
// For timeouts. Time last message was received.
float last_received;
// Time when channel was connected.
float connect_time;
// Bandwidth choke
// Bytes per second
int m_Rate;
// If realtime > cleartime, free to send next packet
float m_fClearTime;
float m_Timeout; // in seconds
char m_Name[32]; // channel name
// packet history
// netflow_t m_DataFlow[ MAX_FLOWS ];
INetworkMessageHandler *m_MessageHandler; // who registers and processes messages
};
#endif // NETCHANNEL_H

View File

@ -0,0 +1,178 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//===========================================================================//
#include "networkclient.h"
#include "UDP_Socket.h"
#include "NetChannel.h"
#include "UDP_Process.h"
#include <winsock.h>
#include "tier1/bitbuf.h"
#include "networksystem.h"
#include "sm_protocol.h"
//-----------------------------------------------------------------------------
// Construction/Destruction
//-----------------------------------------------------------------------------
CNetworkClient::CNetworkClient() : m_bConnected( false )
{
m_pSocket = new CUDPSocket;
}
CNetworkClient::~CNetworkClient()
{
delete m_pSocket;
}
//-----------------------------------------------------------------------------
// Init/Shutdown
//-----------------------------------------------------------------------------
bool CNetworkClient::Init( int nListenPort )
{
if ( !m_pSocket->Init( nListenPort ) )
{
Warning( "CNetworkClient: Unable to create socket!!!\n" );
return false;
}
return true;
}
void CNetworkClient::Shutdown()
{
m_pSocket->Shutdown();
}
#define SPEW_MESSAGES
#if defined( SPEW_MESSAGES )
#define SM_SPEW_MESSAGE( code, remote ) \
Warning( "Message: %s from '%s'\n", #code, remote );
#else
#define SM_SPEW_MESSAGE( code, remote )
#endif
// process a connectionless packet
bool CNetworkClient::ProcessConnectionlessPacket( CNetPacket *packet )
{
int code = packet->m_Message.ReadByte();
switch ( code )
{
case s2c_connect_accept:
{
SM_SPEW_MESSAGE( s2c_connect_accept, packet->m_From.ToString() );
m_NetChan.Setup( false, &packet->m_From, m_pSocket, "client", this );
m_bConnected = true;
}
break;
default:
{
Warning( "CSmServer::ProcessConnectionlessPacket: Unknown code '%i' from '%s'\n",
code, packet->m_From.ToString() );
}
break;
}
return true;
}
INetChannel *CNetworkClient::FindNetChannel( const netadr_t& from )
{
if ( from.CompareAdr( m_NetChan.GetRemoteAddress() ) )
return &m_NetChan;
return NULL;
}
void CNetworkClient::ReadPackets( void )
{
UDP_ProcessSocket( m_pSocket, this, this );
}
void CNetworkClient::SendUpdate()
{
if ( m_bConnected )
{
m_NetChan.SendDatagram( NULL );
}
}
void CNetworkClient::Disconnect()
{
if ( !m_bConnected )
return;
byte data[ 512 ];
bf_write buf( "CNetworkClient::Connect", data, sizeof( data ) );
WriteSystemNetworkMessage( buf, net_disconnect );
buf.WriteString( "client disconnected" );
m_NetChan.SendDatagram( &buf );
m_NetChan.SendDatagram( &buf );
m_NetChan.SendDatagram( &buf );
}
bool CNetworkClient::Connect( char const *server, int port /*=SM_SERVER_PORT*/ )
{
byte data[ 512 ];
bf_write buf( "CNetworkClient::Connect", data, sizeof( data ) );
buf.WriteLong( -1 );
buf.WriteByte( c2s_connect );
netadr_t remote;
remote.type = NA_IP;
remote.port = htons( (unsigned short)port );
// Resolve remote name
sockaddr sa;
if ( !Q_stricmp( server, "localhost" ) )
{
remote.ip[ 0 ] = 127;
remote.ip[ 1 ] = 0;
remote.ip[ 2 ] = 0;
remote.ip[ 3 ] = 1;
}
else
{
if ( !g_pNetworkSystemImp->StringToSockaddr( server, &sa ) )
{
Msg( "Unable to resolve '%s'\n", server );
return false;
}
remote.SetFromSockadr( &sa );
}
m_NetChan.SetConnectionState( CONNECTION_STATE_CONNECTING );
return m_pSocket->SendTo( remote, buf.GetData(), buf.GetNumBytesWritten() );
}
void CNetworkClient::OnConnectionClosing( INetChannel *channel, char const *reason )
{
Warning( "OnConnectionClosing '%s'\n", reason );
if ( channel == &m_NetChan )
{
m_NetChan.Shutdown( "received disconnect\n" );
m_bConnected = false;
}
}
void CNetworkClient::OnConnectionStarted( INetChannel *channel )
{
Warning( "OnConnectionStarted\n" );
}
void CNetworkClient::OnPacketStarted( int inseq, int outseq )
{
}
void CNetworkClient::OnPacketFinished()
{
}

View File

@ -0,0 +1,54 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//===========================================================================//
#ifndef CLIENT_H
#define CLIENT_H
#ifdef _WIN32
#pragma once
#endif
#include "NetChannel.h"
class CNetworkClient : public IConnectionlessPacketHandler, public INetworkMessageHandler, public ILookupChannel
{
public:
CNetworkClient();
virtual ~CNetworkClient();
bool Init( int nListenPort );
void Shutdown();
bool Connect( const char *server, int port );
void Disconnect();
// IConnectionlessPacketHandler
virtual bool ProcessConnectionlessPacket( CNetPacket *packet ); // process a connectionless packet
// INetworkMessageHandler
virtual void OnConnectionClosing( INetChannel *channel, char const *reason );
virtual void OnConnectionStarted( INetChannel *channel );
virtual void OnPacketStarted( int inseq, int outseq );
virtual void OnPacketFinished();
// ILookupChannel
virtual INetChannel *FindNetChannel( const netadr_t& from ) ;
INetChannel *GetNetChannel()
{
return &m_NetChan;
}
void ReadPackets();
void SendUpdate();
CUDPSocket *m_pSocket;
CNetChannel m_NetChan;
bool m_bConnected;
};
#endif // CLIENT_H

View File

@ -0,0 +1,224 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//===========================================================================//
#include "networkserver.h"
#include "networksystem.h"
#include "icvar.h"
#include "filesystem.h"
#include "UDP_Socket.h"
#include "sm_protocol.h"
#include "NetChannel.h"
#include "UDP_Process.h"
#include <winsock.h>
#include "networkclient.h"
//-----------------------------------------------------------------------------
//
// Implementation of CPlayer
//
//-----------------------------------------------------------------------------
CNetworkServer::CNetworkServer( )
{
m_pSocket = new CUDPSocket;
}
CNetworkServer::~CNetworkServer()
{
delete m_pSocket;
}
bool CNetworkServer::Init( int nServerPort )
{
if ( !m_pSocket->Init( nServerPort ) )
{
Warning( "CNetworkServer: Unable to create socket!!!\n" );
return false;
}
return true;
}
void CNetworkServer::Shutdown()
{
m_pSocket->Shutdown();
}
CNetChannel *CNetworkServer::FindNetChannel( const netadr_t& from )
{
CPlayer *pl = FindPlayerByAddress( from );
if ( pl )
return &pl->m_NetChan;
return NULL;
}
CPlayer *CNetworkServer::FindPlayerByAddress( const netadr_t& adr )
{
int c = m_Players.Count();
for ( int i = 0; i < c; ++i )
{
CPlayer *player = m_Players[ i ];
if ( player->GetRemoteAddress().CompareAdr( adr ) )
return player;
}
return NULL;
}
CPlayer *CNetworkServer::FindPlayerByNetChannel( INetChannel *chan )
{
int c = m_Players.Count();
for ( int i = 0; i < c; ++i )
{
CPlayer *player = m_Players[ i ];
if ( &player->m_NetChan == chan )
return player;
}
return NULL;
}
#define SPEW_MESSAGES
#if defined( SPEW_MESSAGES )
#define SM_SPEW_MESSAGE( code, remote ) \
Warning( "Message: %s from '%s'\n", #code, remote );
#else
#define SM_SPEW_MESSAGE( code, remote )
#endif
// process a connectionless packet
bool CNetworkServer::ProcessConnectionlessPacket( CNetPacket *packet )
{
int code = packet->m_Message.ReadByte();
switch ( code )
{
case c2s_connect:
{
SM_SPEW_MESSAGE( c2s_connect, packet->m_From.ToString() );
CPlayer *pl = FindPlayerByAddress( packet->m_From );
if ( pl )
{
Warning( "Player already exists for %s\n", packet->m_From.ToString() );
}
else
{
// Creates the connection
pl = new CPlayer( this, packet->m_From );
m_Players.AddToTail( pl );
// Now send the conn accepted message
AcceptConnection( packet->m_From );
}
}
break;
default:
{
Warning( "CNetworkServer::ProcessConnectionlessPacket: Unknown code '%i' from '%s'\n",
code, packet->m_From.ToString() );
}
break;
}
return true;
}
void CNetworkServer::AcceptConnection( const netadr_t& remote )
{
byte data[ 512 ];
bf_write buf( "CNetworkServer::AcceptConnection", data, sizeof( data ) );
buf.WriteLong( -1 );
buf.WriteByte( s2c_connect_accept );
m_pSocket->SendTo( remote, buf.GetData(), buf.GetNumBytesWritten() );
}
void CNetworkServer::ReadPackets( void )
{
UDP_ProcessSocket( m_pSocket, this, this );
int c = m_Players.Count();
for ( int i = c - 1; i >= 0 ; --i )
{
if ( m_Players[ i ]->m_bMarkedForDeletion )
{
CPlayer *pl = m_Players[ i ];
m_Players.Remove( i );
delete pl;
}
}
}
void CNetworkServer::SendUpdates()
{
int c = m_Players.Count();
for ( int i = 0; i < c; ++i )
{
m_Players[ i ]->SendUpdate();
}
}
void CNetworkServer::OnConnectionStarted( INetChannel *pChannel )
{
// Create a network event
NetworkConnectionEvent_t *pConnection = g_pNetworkSystemImp->CreateNetworkEvent< NetworkConnectionEvent_t >( );
pConnection->m_nType = NETWORK_EVENT_CONNECTED;
pConnection->m_pChannel = pChannel;
}
void CNetworkServer::OnConnectionClosing( INetChannel *pChannel, char const *reason )
{
Warning( "OnConnectionClosing '%s'\n", reason );
CPlayer *pPlayer = FindPlayerByNetChannel( pChannel );
if ( pPlayer )
{
pPlayer->Shutdown();
}
// Create a network event
NetworkDisconnectionEvent_t *pDisconnection = g_pNetworkSystemImp->CreateNetworkEvent< NetworkDisconnectionEvent_t >( );
pDisconnection->m_nType = NETWORK_EVENT_DISCONNECTED;
pDisconnection->m_pChannel = pChannel;
}
void CNetworkServer::OnPacketStarted( int inseq, int outseq )
{
}
void CNetworkServer::OnPacketFinished()
{
}
//-----------------------------------------------------------------------------
//
// Implementation of CPlayer
//
//-----------------------------------------------------------------------------
CPlayer::CPlayer( CNetworkServer *server, netadr_t& remote ) :
m_bMarkedForDeletion( false )
{
m_NetChan.Setup( true, &remote, server->m_pSocket, "player", server );
}
void CPlayer::Shutdown()
{
m_bMarkedForDeletion = true;
m_NetChan.Shutdown( "received disconnect\n" );
}
void CPlayer::SendUpdate()
{
if ( m_NetChan.CanSendPacket() )
{
m_NetChan.SendDatagram( NULL );
}
}

View File

@ -0,0 +1,86 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//===========================================================================//
#ifndef NETWORKSERVER_H
#define NETWORKSERVER_H
#include "networksystem/inetworksystem.h"
#include "netchannel.h"
//-----------------------------------------------------------------------------
// Forward declarations
//-----------------------------------------------------------------------------
class CPlayer;
//-----------------------------------------------------------------------------
// Server class
//-----------------------------------------------------------------------------
class CNetworkServer : public IConnectionlessPacketHandler, public INetworkMessageHandler, public ILookupChannel
{
public:
CNetworkServer( );
virtual ~CNetworkServer();
bool Init( int nServerPort );
void Shutdown();
// IConnectionlessPacketHandler
virtual bool ProcessConnectionlessPacket( CNetPacket *packet ); // process a connectionless packet
// INetworkMessageHandler
virtual void OnConnectionClosing( INetChannel *channel, char const *reason );
virtual void OnConnectionStarted( INetChannel *channel );
virtual void OnPacketStarted( int inseq, int outseq );
virtual void OnPacketFinished();
// ILookupChannel
virtual CNetChannel *FindNetChannel( const netadr_t& from ) ;
void ReadPackets();
void SendUpdates();
void AcceptConnection( const netadr_t& remote );
CPlayer *FindPlayerByAddress( const netadr_t& adr );
CPlayer *FindPlayerByNetChannel( INetChannel *chan );
CUDPSocket *m_pSocket;
CUtlVector< CPlayer * > m_Players;
};
//-----------------------------------------------------------------------------
// Represents a connected player to the server
//-----------------------------------------------------------------------------
class CPlayer
{
public:
CPlayer( CNetworkServer *server, netadr_t& remote );
void SendUpdate();
const netadr_t &GetRemoteAddress()
{
return m_NetChan.GetRemoteAddress();
}
char const *GetName()
{
return "Foozle";
}
void Shutdown();
CNetChannel m_NetChan;
bool m_bMarkedForDeletion;
};
#endif // NETWORKSERVER_H

View File

@ -0,0 +1,491 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//===========================================================================//
#include "networksystem.h"
#include "filesystem.h"
#include "UDP_Socket.h"
#include "sm_protocol.h"
#include "NetChannel.h"
#include "UDP_Process.h"
#include <winsock.h>
#include "networkclient.h"
#include "networkserver.h"
#include "networksystem/inetworkmessage.h"
#include "mathlib/mathlib.h"
#include "tier2/tier2.h"
//-----------------------------------------------------------------------------
// Singleton instance
//-----------------------------------------------------------------------------
static CNetworkSystem g_NetworkSystem;
CNetworkSystem *g_pNetworkSystemImp = &g_NetworkSystem;
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CNetworkSystem, INetworkSystem,
NETWORKSYSTEM_INTERFACE_VERSION, g_NetworkSystem );
//-----------------------------------------------------------------------------
// Constructor, destructor
//-----------------------------------------------------------------------------
CNetworkSystem::CNetworkSystem()
{
m_bWinsockInitialized = false;
m_bNetworkEventCreated = false;
m_bInMidPacket = false;
m_pServer = NULL;
m_pClient = NULL;
m_nGroupBits = 1;
m_nTypeBits = LargestPowerOfTwoLessThanOrEqual( net_num_messages );
}
CNetworkSystem::~CNetworkSystem()
{
}
//-----------------------------------------------------------------------------
// Initialization, shutdown
//-----------------------------------------------------------------------------
InitReturnVal_t CNetworkSystem::Init()
{
InitReturnVal_t nRetVal = BaseClass::Init();
if ( nRetVal != INIT_OK )
return nRetVal;
// initialize winsock 2.0
WSAData wsaData;
if ( WSAStartup( MAKEWORD(2,0), &wsaData ) != 0 )
{
Warning( "Error! Failed to load network socket library.\n");
return INIT_OK;
}
else
{
m_bWinsockInitialized = true;
}
LPHOSTENT lp = gethostbyname("localhost");
if ( !lp )
{
Warning( "Error! Failed to query local host info\n");
return INIT_OK;
}
m_LocalHostName = lp->h_name;
lp = gethostbyname( m_LocalHostName );
if ( !lp )
{
Warning( "Error! Failed to query local host info\n");
return INIT_OK;
}
sockaddr ip;
m_LocalAddressString = inet_ntoa( *((in_addr*)(lp->h_addr_list[0])) );
StringToSockaddr( m_LocalAddressString, &ip );
m_LocalAddress.SetFromSockadr( &ip );
return INIT_OK;
}
void CNetworkSystem::Shutdown()
{
if ( m_bWinsockInitialized )
{
WSACleanup();
}
CleanupNetworkMessages();
BaseClass::Shutdown();
}
//-----------------------------------------------------------------------------
// Connect, disconnect
//-----------------------------------------------------------------------------
bool CNetworkSystem::Connect( CreateInterfaceFn factory )
{
if ( !BaseClass::Connect( factory ) )
return false;
if ( !g_pFullFileSystem )
{
Warning( "The network system requires the filesystem to run!\n" );
return false;
}
return INIT_OK;
}
//-----------------------------------------------------------------------------
// Returns the current time
//-----------------------------------------------------------------------------
float CNetworkSystem::GetTime( void )
{
return Plat_FloatTime();
}
//-----------------------------------------------------------------------------
// Installs network message factories to be used with all connections
//-----------------------------------------------------------------------------
bool CNetworkSystem::RegisterMessage( INetworkMessage *pMessage )
{
if ( m_pServer || m_pClient )
{
Warning( "Cannot register messages after connection has started.\n" );
return false;
}
if ( pMessage->GetGroup() == 0 )
{
Warning( "Network message group 0 is reserved by the network system.\n" );
return false;
}
// Look for already registered message
if ( m_NetworkMessages.Find( pMessage ) >= 0 )
return false;
// Allocate more space in messages
int nGroupBits = LargestPowerOfTwoLessThanOrEqual( pMessage->GetGroup() );
int nTypeBits = LargestPowerOfTwoLessThanOrEqual( pMessage->GetType() );
m_nGroupBits = max( nGroupBits, m_nGroupBits );
m_nTypeBits = max( nTypeBits, m_nTypeBits );
m_NetworkMessages.AddToTail( pMessage );
return true;
}
void CNetworkSystem::CleanupNetworkMessages( )
{
int nCount = m_NetworkMessages.Count();
for ( int i = 0; i < nCount; ++i )
{
m_NetworkMessages[i]->Release();
}
m_NetworkMessages.RemoveAll();
}
//-----------------------------------------------------------------------------
// Finds a network message given a particular message type
//-----------------------------------------------------------------------------
INetworkMessage* CNetworkSystem::FindNetworkMessage( int group, int type )
{
int nCount = m_NetworkMessages.Count();
for (int i=0; i < nCount; i++ )
{
if ( ( m_NetworkMessages[i]->GetGroup() == group ) && ( m_NetworkMessages[i]->GetType() == type ) )
return m_NetworkMessages[i];
}
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CNetworkSystem::StringToSockaddr( const char *s, struct sockaddr *sadr )
{
struct hostent *h;
char *colon;
char copy[128];
Q_memset (sadr, 0, sizeof(*sadr));
((struct sockaddr_in *)sadr)->sin_family = AF_INET;
((struct sockaddr_in *)sadr)->sin_port = 0;
Q_strncpy (copy, s, sizeof( copy ) );
// strip off a trailing :port if present
for (colon = copy ; *colon ; colon++)
if (*colon == ':')
{
*colon = 0;
((struct sockaddr_in *)sadr)->sin_port = htons((short)atoi(colon+1));
}
if (copy[0] >= '0' && copy[0] <= '9' && Q_strstr( copy, "." ) )
{
*(int *)&((struct sockaddr_in *)sadr)->sin_addr = inet_addr(copy);
}
else
{
// if ( net_nodns )
// return false; // DNS names disabled
if ( (h = gethostbyname(copy)) == NULL )
return false;
*(int *)&((struct sockaddr_in *)sadr)->sin_addr = *(int *)h->h_addr_list[0];
}
return true;
}
//-----------------------------------------------------------------------------
// Returns the local address
//-----------------------------------------------------------------------------
const char* CNetworkSystem::GetLocalHostName( void ) const
{
return m_LocalHostName;
}
const char* CNetworkSystem::GetLocalAddress( void ) const
{
return m_LocalAddressString;
}
//-----------------------------------------------------------------------------
// Start, shutdown a server
//-----------------------------------------------------------------------------
bool CNetworkSystem::StartServer( unsigned short nServerListenPort )
{
if ( !m_bWinsockInitialized )
return false;
Assert( !m_pServer );
m_pServer = new CNetworkServer;
return m_pServer->Init( nServerListenPort );
}
void CNetworkSystem::ShutdownServer( )
{
if ( m_pServer )
{
m_pServer->Shutdown();
delete m_pServer;
m_pServer = NULL;
}
}
//-----------------------------------------------------------------------------
// Server update
//-----------------------------------------------------------------------------
void CNetworkSystem::ServerReceiveMessages()
{
if ( m_pServer )
{
m_pServer->ReadPackets();
}
}
void CNetworkSystem::ServerSendMessages()
{
if ( m_pServer )
{
m_pServer->SendUpdates();
}
}
//-----------------------------------------------------------------------------
// Start, shutdown a client
//-----------------------------------------------------------------------------
bool CNetworkSystem::StartClient( unsigned short nClientListenPort )
{
if ( !m_bWinsockInitialized )
return false;
Assert( !m_pClient );
m_pClient = new CNetworkClient;
return m_pClient->Init( nClientListenPort );
}
void CNetworkSystem::ShutdownClient( )
{
if ( m_pClient )
{
m_pClient->Shutdown();
delete m_pClient;
m_pClient = NULL;
}
}
//-----------------------------------------------------------------------------
// Server update
//-----------------------------------------------------------------------------
void CNetworkSystem::ClientReceiveMessages()
{
if ( m_pClient )
{
m_pClient->ReadPackets();
}
}
void CNetworkSystem::ClientSendMessages()
{
if ( m_pClient )
{
m_pClient->SendUpdate();
}
}
//-----------------------------------------------------------------------------
// Server update
//-----------------------------------------------------------------------------
INetChannel* CNetworkSystem::ConnectClientToServer( const char *pServer, int nServerListenPort )
{
if ( m_pClient )
{
if ( m_pClient->Connect( pServer, nServerListenPort ) )
return m_pClient->GetNetChannel();
}
return NULL;
}
void CNetworkSystem::DisconnectClientFromServer( INetChannel* pChannel )
{
if ( m_pClient && ( m_pClient->GetNetChannel() == pChannel ) )
{
m_pClient->Disconnect();
}
}
//-----------------------------------------------------------------------------
// Queues up a network packet
//-----------------------------------------------------------------------------
void CNetworkSystem::EnqueueConnectionlessNetworkPacket( CNetPacket *pPacket, IConnectionlessPacketHandler *pHandler )
{
int i = m_PacketQueue.AddToTail( );
PacketInfo_t& info = m_PacketQueue[i];
info.m_pPacket = pPacket;
info.m_pHandler = pHandler;
info.m_pNetChannel = NULL;
}
void CNetworkSystem::EnqueueNetworkPacket( CNetPacket *pPacket, CNetChannel *pNetChannel )
{
int i = m_PacketQueue.AddToTail( );
PacketInfo_t& info = m_PacketQueue[i];
info.m_pPacket = pPacket;
info.m_pHandler = NULL;
info.m_pNetChannel = pNetChannel;
}
//-----------------------------------------------------------------------------
// Network event iteration helpers
//-----------------------------------------------------------------------------
bool CNetworkSystem::StartProcessingNewPacket()
{
PacketInfo_t& info = m_PacketQueue[ m_nProcessingPacket ];
if ( info.m_pHandler )
{
UDP_ProcessConnectionlessPacket( info.m_pPacket, info.m_pHandler );
return false;
}
if ( !info.m_pNetChannel )
{
// Not an error that may happen during connect or disconnect
Warning( "Sequenced packet without connection from %s\n" , info.m_pPacket->m_From.ToString() );
return false;
}
return info.m_pNetChannel->StartProcessingPacket( info.m_pPacket );
}
bool CNetworkSystem::AdvanceProcessingNetworkPacket( )
{
m_PacketQueue[ m_nProcessingPacket ].m_pPacket->Release();
m_PacketQueue[ m_nProcessingPacket ].m_pPacket = NULL;
++m_nProcessingPacket;
bool bOverflowed = ( m_nProcessingPacket >= m_PacketQueue.Count() );
if ( bOverflowed )
{
m_PacketQueue.RemoveAll();
m_nProcessingPacket = 0;
}
return !bOverflowed;
}
//-----------------------------------------------------------------------------
// Network event iteration
//-----------------------------------------------------------------------------
NetworkEvent_t *CNetworkSystem::FirstNetworkEvent( )
{
Assert( !m_bInMidPacket && !m_nProcessingPacket );
m_nProcessingPacket = 0;
m_bInMidPacket = false;
return NextNetworkEvent();
}
NetworkEvent_t *CNetworkSystem::NextNetworkEvent( )
{
int nPacketCount = m_PacketQueue.Count();
if ( m_nProcessingPacket >= nPacketCount )
return NULL;
while( true )
{
// Continue processing the packet we're currently on
if ( m_bInMidPacket )
{
PacketInfo_t& info = m_PacketQueue[ m_nProcessingPacket ];
while( info.m_pNetChannel->ProcessPacket( info.m_pPacket ) )
{
Assert( m_bNetworkEventCreated );
m_bNetworkEventCreated = false;
return (NetworkEvent_t*)m_EventMessageBuffer;
}
info.m_pNetChannel->EndProcessingPacket( info.m_pPacket );
m_bInMidPacket = false;
if ( !AdvanceProcessingNetworkPacket() )
return NULL;
}
// Keep reading packets until we find one that either generates an event,
// one that encounters a packet we need to process, or we exhaust the event queue
while( !StartProcessingNewPacket() )
{
bool bOverflowed = !AdvanceProcessingNetworkPacket();
if ( m_bNetworkEventCreated )
{
m_bNetworkEventCreated = false;
return (NetworkEvent_t*)m_EventMessageBuffer;
}
if ( bOverflowed )
return NULL;
}
m_bInMidPacket = true;
}
}
//-----------------------------------------------------------------------------
// Network event creation
//-----------------------------------------------------------------------------
NetworkEvent_t* CNetworkSystem::CreateNetworkEvent( int nSizeInBytes )
{
Assert( nSizeInBytes <= sizeof( m_EventMessageBuffer ) );
// If this assertion fails, it means two or more network events were created
// before the main network event loop had a chance to inform external code
Assert( !m_bNetworkEventCreated );
m_bNetworkEventCreated = true;
return ( NetworkEvent_t* )m_EventMessageBuffer;
}
bool CNetworkSystem::IsNetworkEventCreated()
{
return m_bNetworkEventCreated;
}

View File

@ -0,0 +1,172 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//===========================================================================//
#ifndef NETWORKSYSTEM_H
#define NETWORKSYSTEM_H
#ifdef _WIN32
#pragma once
#endif
#include "networksystem/inetworksystem.h"
#include "tier1/utlvector.h"
#include "tier1/bitbuf.h"
#include "sm_protocol.h"
#include "networksystem/inetworkmessage.h"
#include "tier1/netadr.h"
#include "tier1/utlstring.h"
#include "tier2/tier2.h"
//-----------------------------------------------------------------------------
// Forward declarations
//-----------------------------------------------------------------------------
class CNetworkServer;
class CNetworkClient;
class IConnectionlessPacketHandler;
class CNetChannel;
enum SystemNetworkMessageType_t;
//-----------------------------------------------------------------------------
// Global interfaces
//-----------------------------------------------------------------------------
class CNetworkSystem;
extern CNetworkSystem *g_pNetworkSystemImp;
//-----------------------------------------------------------------------------
// Implementation of the network system
//-----------------------------------------------------------------------------
class CNetworkSystem : public CTier2AppSystem< INetworkSystem >
{
typedef CTier2AppSystem< INetworkSystem > BaseClass;
public:
// Constructor, destructor
CNetworkSystem();
virtual ~CNetworkSystem();
// Inherited from IAppSystem
virtual bool Connect( CreateInterfaceFn factory );
virtual InitReturnVal_t Init();
virtual void Shutdown();
// Inherited from INetworkSystem
virtual bool RegisterMessage( INetworkMessage *msg );
virtual bool StartServer( unsigned short nServerListenPort );
virtual void ShutdownServer( );
virtual void ServerReceiveMessages();
virtual void ServerSendMessages();
virtual bool StartClient( unsigned short nClientListenPort );
virtual void ShutdownClient( );
virtual void ClientSendMessages();
virtual void ClientReceiveMessages();
virtual INetChannel* ConnectClientToServer( const char *pServer, int nServerListenPort );
virtual void DisconnectClientFromServer( INetChannel* pChan );
virtual NetworkEvent_t *FirstNetworkEvent( );
virtual NetworkEvent_t *NextNetworkEvent( );
virtual const char* GetLocalHostName( void ) const;
virtual const char* GetLocalAddress( void ) const;
// Methods internal for use in networksystem library
// Method to allow systems to add network events received
NetworkEvent_t* CreateNetworkEvent( int nSizeInBytes );
template< class T > T* CreateNetworkEvent();
bool IsNetworkEventCreated();
// Finds a network message given a particular message type
INetworkMessage* FindNetworkMessage( int group, int type );
// Returns the number of bits to encode the type + group with
int GetTypeBitCount() const;
int GetGroupBitCount() const;
// Returns the current time
float GetTime( void );
// Converts a string to a socket address
bool StringToSockaddr( const char *s, struct sockaddr *sadr );
// Queues up a network packet
void EnqueueConnectionlessNetworkPacket( CNetPacket *pPacket, IConnectionlessPacketHandler *pHandler );
void EnqueueNetworkPacket( CNetPacket *pPacket, CNetChannel *pNetChannel );
private:
struct PacketInfo_t
{
CNetPacket *m_pPacket;
IConnectionlessPacketHandler *m_pHandler;
CNetChannel *m_pNetChannel;
};
// Network event iteration helpers
bool StartProcessingNewPacket();
bool AdvanceProcessingNetworkPacket( );
void CleanupNetworkMessages( );
bool m_bWinsockInitialized : 1;
bool m_bNetworkEventCreated : 1;
bool m_bInMidPacket : 1;
int m_nTypeBits;
int m_nGroupBits;
netadr_t m_LocalAddress;
CUtlString m_LocalAddressString;
CUtlString m_LocalHostName;
CNetworkServer *m_pServer;
CNetworkClient *m_pClient;
unsigned char m_EventMessageBuffer[256];
CUtlVector<PacketInfo_t> m_PacketQueue;
int m_nProcessingPacket;
CUtlVector<INetworkMessage*> m_NetworkMessages;
};
//-----------------------------------------------------------------------------
// Inline methods
//-----------------------------------------------------------------------------
template< class T >
T* CNetworkSystem::CreateNetworkEvent()
{
// Increase the size of m_EventMessageBuffer if this assertion fails
COMPILE_TIME_ASSERT( sizeof(T) <= sizeof( m_EventMessageBuffer ) );
return (T*)CreateNetworkEvent( sizeof(T) );
}
//-----------------------------------------------------------------------------
// Returns the number of bits to encode the type + group with
//-----------------------------------------------------------------------------
inline int CNetworkSystem::GetTypeBitCount() const
{
return m_nTypeBits;
}
inline int CNetworkSystem::GetGroupBitCount() const
{
return m_nGroupBits;
}
//-----------------------------------------------------------------------------
// Writes a system network message
//-----------------------------------------------------------------------------
inline void WriteSystemNetworkMessage( bf_write &msg, SystemNetworkMessageType_t type )
{
msg.WriteUBitLong( net_group_networksystem, g_pNetworkSystemImp->GetGroupBitCount() );
msg.WriteUBitLong( type, g_pNetworkSystemImp->GetTypeBitCount() );
}
inline void WriteNetworkMessage( bf_write &msg, INetworkMessage *pNetworkMessage )
{
msg.WriteUBitLong( pNetworkMessage->GetGroup(), g_pNetworkSystemImp->GetGroupBitCount() );
msg.WriteUBitLong( pNetworkMessage->GetType(), g_pNetworkSystemImp->GetTypeBitCount() );
}
#endif // NETWORKSYSTEM_H

View File

@ -0,0 +1,54 @@
//-----------------------------------------------------------------------------
// NETWORKSYSTEM.VPC
//
// Project Script
//-----------------------------------------------------------------------------
$Macro SRCDIR ".."
$Macro OUTBINDIR "$SRCDIR\..\game\bin"
$Include "$SRCDIR\vpc_scripts\source_dll_base.vpc"
$Configuration
{
$Compiler
{
$PreprocessorDefinitions "$BASE;NETWORKSYSTEM_EXPORTS"
}
$Linker
{
$AdditionalDependencies "$BASE winmm.lib ws2_32.lib"
}
}
$Project "Networksystem"
{
$Folder "Source Files"
{
$File "netchannel.cpp"
$File "netchannel.h"
$File "networkclient.cpp"
$File "networkclient.h"
$File "networkserver.cpp"
$File "networkserver.h"
$File "networksystem.cpp"
$File "networksystem.h"
$File "sm_protocol.h"
$File "udp_process.cpp"
$File "udp_process.h"
$File "udp_socket.cpp"
$File "udp_socket.h"
}
$Folder "Interface"
{
$File "$SRCDIR\common\networksystem\inetworkmessage.h"
$File "$SRCDIR\common\networksystem\inetworksystem.h"
}
$Folder "Link Libraries"
{
$Lib tier2
}
}

View File

@ -0,0 +1,49 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================
#ifndef SM_PROTOCOL_H
#define SM_PROTOCOL_H
#ifdef _WIN32
#pragma once
#endif
enum
{
CONNECTIONLESS_HEADER = 0xffffffff,
};
// Connectionless messages
enum
{
c2s_connect = 1,
c2s_num_messages
};
enum
{
s2c_connect_accept = 1,
s2c_connect_reject,
s2c_num_messages
};
enum NetworkMessageGroup_t
{
net_group_networksystem = 0
};
// Networksystem internal messages for use during valid connection
enum SystemNetworkMessageType_t
{
net_nop = 0, // nop command used for padding
net_disconnect = 1, // disconnect, last message in connection
net_num_messages
};
#endif // SM_PROTOCOL_H

View File

@ -0,0 +1,134 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//===========================================================================//
#include "tier1/utlbuffer.h"
#include "UDP_Socket.h"
#include "NetChannel.h"
#include "sm_protocol.h"
#include "networksystem.h"
//-----------------------------------------------------------------------------
// Allocates memory for packets
//-----------------------------------------------------------------------------
static CUtlMemoryPool s_PacketBufferAlloc( NET_MAX_MESSAGE, 8, CUtlMemoryPool::GROW_SLOW );
DEFINE_FIXEDSIZE_ALLOCATOR( CNetPacket, 32, CUtlMemoryPool::GROW_SLOW );
//-----------------------------------------------------------------------------
// Constructor, destructor
//-----------------------------------------------------------------------------
CNetPacket::CNetPacket()
{
m_pData = reinterpret_cast<byte*>( s_PacketBufferAlloc.Alloc() );
m_nRefCount = 1;
}
CNetPacket::~CNetPacket()
{
s_PacketBufferAlloc.Free( m_pData );
}
void CNetPacket::AddRef()
{
++m_nRefCount;
}
void CNetPacket::Release()
{
if ( --m_nRefCount <= 0 )
{
delete this;
}
}
bool UDP_ReceiveDatagram( CUDPSocket *pSocket, CNetPacket* pPacket )
{
Assert( pPacket );
netadr_t packetFrom;
CUtlBuffer buf( pPacket->m_pData, NET_MAX_MESSAGE );
if ( pSocket->RecvFrom( packetFrom, buf ) )
{
pPacket->m_From = packetFrom;
pPacket->m_nSizeInBytes = buf.TellPut();
return true;
}
return false;
}
CNetPacket *UDP_GetPacket( CUDPSocket *pSocket )
{
CNetPacket *pPacket = new CNetPacket;
// setup new packet
pPacket->m_From.SetType( NA_IP );
pPacket->m_From.Clear();
pPacket->m_flReceivedTime = g_pNetworkSystemImp->GetTime();
pPacket->m_pSource = pSocket;
pPacket->m_nSizeInBytes = 0;
pPacket->m_Message.SetDebugName("inpacket.message");
// then check UDP data
if ( !UDP_ReceiveDatagram( pSocket, pPacket ) )
{
delete pPacket;
return NULL;
}
Assert( pPacket->m_nSizeInBytes );
// prepare bitbuffer for reading packet with new size
pPacket->m_Message.StartReading( pPacket->m_pData, pPacket->m_nSizeInBytes );
return pPacket;
}
void UDP_ProcessSocket( CUDPSocket *pSocket, IConnectionlessPacketHandler *pHandler, ILookupChannel *pLookup )
{
CNetPacket *pPacket;
Assert( pSocket );
// Get datagrams from sockets
while ( ( pPacket = UDP_GetPacket ( pSocket ) ) != NULL )
{
/*
if ( Filter_ShouldDiscard ( pPacket->m_From ) ) // filterning is done by network layer
{
Filter_SendBan( pPacket->m_From ); // tell them we aren't listening...
continue;
}
*/
// Find the netchan it came from
if ( *(unsigned int *)pPacket->m_pData == CONNECTIONLESS_HEADER )
{
g_pNetworkSystemImp->EnqueueConnectionlessNetworkPacket( pPacket, pHandler );
}
else
{
INetChannel *pNetChan = pLookup->FindNetChannel( pPacket->m_From );
g_pNetworkSystemImp->EnqueueNetworkPacket( pPacket, static_cast<CNetChannel*>( pNetChan ) );
}
}
}
void UDP_ProcessConnectionlessPacket( CNetPacket *pPacket, IConnectionlessPacketHandler *pHandler )
{
// check for connectionless packet (0xffffffff) first
Assert( *(unsigned int *)pPacket->m_pData == CONNECTIONLESS_HEADER );
pPacket->m_Message.ReadLong(); // read the -1
/*
if ( net_showudp.GetInt() )
{
Msg("UDP <- %s: sz=%i OOB '%c'\n", info.m_pPacket->m_From.ToString(), pPacket->m_nSizeInBytes, pPacket->m_pData[4] );
}
*/
pHandler->ProcessConnectionlessPacket( pPacket );
}

View File

@ -0,0 +1,21 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================
#ifndef UDP_PROCESS_H
#define UDP_PROCESS_H
#ifdef _WIN32
#pragma once
#endif
class CUDPSocket;
class IConnectionlessPacketHandler;
class ILookupChannel;
class CNetPacket;
void UDP_ProcessSocket( CUDPSocket *socket, IConnectionlessPacketHandler *handler, ILookupChannel *lookup );
void UDP_ProcessConnectionlessPacket( CNetPacket *pPacket, IConnectionlessPacketHandler *pHandler );
#endif // UDP_PROCESS_H

View File

@ -0,0 +1,120 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//===========================================================================//
#include <winsock.h>
#include "udp_socket.h"
#include "tier1/utlbuffer.h"
#include "tier1/strtools.h"
class CUDPSocket::CImpl
{
public:
struct sockaddr_in m_SocketIP;
};
CUDPSocket::CUDPSocket( ) :
m_socketIP(),
m_Socket( INVALID_SOCKET ),
m_Port( 0 ),
m_pImpl( new CImpl )
{
Q_memset( &m_socketIP, 0, sizeof( m_socketIP ) );
}
CUDPSocket::~CUDPSocket()
{
delete m_pImpl;
}
bool CUDPSocket::Init( unsigned short nBindToPort )
{
m_Port = nBindToPort;
struct sockaddr_in address;
m_Socket = socket( PF_INET, SOCK_DGRAM, IPPROTO_UDP );
if ( m_Socket == INVALID_SOCKET )
return false;
address = m_pImpl->m_SocketIP;
address.sin_family = AF_INET;
address.sin_port = htons( m_Port );
address.sin_addr.S_un.S_addr = INADDR_ANY;
if ( SOCKET_ERROR == bind( m_Socket, ( struct sockaddr * )&address, sizeof( address ) ) )
{
return false;
}
// Set to non-blocking i/o
unsigned long value = 1;
if ( SOCKET_ERROR == ioctlsocket( m_Socket, FIONBIO, &value ) )
{
return false;
}
return true;
}
void CUDPSocket::Shutdown()
{
if ( m_Socket != INVALID_SOCKET )
{
closesocket( static_cast<unsigned int>( m_Socket ));
}
}
bool CUDPSocket::RecvFrom( netadr_t& packet_from, CUtlBuffer& data )
{
sockaddr from;
int nFromLen = sizeof( from );
int nMaxBytesToRead = data.Size() - data.TellPut();
char *pBuffer = (char*)data.PeekPut();
int nBytesRead = recvfrom( m_Socket, pBuffer, nMaxBytesToRead, 0, &from, &nFromLen );
if ( nBytesRead == SOCKET_ERROR )
{
int nError = WSAGetLastError();
if ( nError != WSAEWOULDBLOCK )
{
Warning( "Socket error '%i'\n", nError );
}
return false;
}
packet_from.SetFromSockadr( &from );
data.SeekPut( CUtlBuffer::SEEK_CURRENT, nBytesRead );
return true;
}
bool CUDPSocket::SendTo( const netadr_t &recipient, const CUtlBuffer& data )
{
return SendTo( recipient, (const byte *)data.Base(), (size_t)data.TellPut() );
}
bool CUDPSocket::SendTo( const netadr_t &recipient, const byte *data, size_t datalength )
{
sockaddr dest;
recipient.ToSockadr( &dest );
// Send data
int bytesSent = sendto
(
m_Socket,
(const char *)data,
(int)datalength,
0,
reinterpret_cast< const sockaddr * >( &dest ),
sizeof( dest )
);
if ( SOCKET_ERROR == bytesSent )
{
return false;
}
return true;
}

View File

@ -0,0 +1,45 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================
#ifndef UDP_SOCKET_H
#define UDP_SOCKET_H
#ifdef _WIN32
#pragma once
#endif
#include "tier1/netadr.h"
#include "tier0/basetypes.h"
class CUtlBuffer;
// Creates a non-blocking UPD socket
class CUDPSocket
{
public:
CUDPSocket();
~CUDPSocket();
bool Init( unsigned short bindToPort );
void Shutdown();
bool RecvFrom( netadr_t& packet_from, CUtlBuffer& data );
bool SendTo( const netadr_t &recipient, const CUtlBuffer& data );
bool SendTo( const netadr_t &recipient, const byte *data, size_t datalength );
bool IsValid() const { return m_Socket != 0; }
protected:
bool CreateSocket (void);
class CImpl;
CImpl *m_pImpl;
netadr_t m_socketIP;
unsigned int m_Socket;
unsigned short m_Port;
};
#endif // UDP_SOCKET_H