⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mtudp.cpp

📁 <B>DirectX9.0 3D游戏编程</B>
💻 CPP
📖 第 1 页 / 共 2 页
字号:
/*******************************************************************
 *         Advanced 3D Game Programming using DirectX 9.0
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * copyright (c) 2003 by Peter A Walsh and Adrian Perez
 * See license.txt for modification and distribution information
 ******************************************************************/
// MTUDP.cpp: implementation of the MTUDP class.
//
//////////////////////////////////////////////////////////////////////

#include "MTUDP.h"
#include "cNetError.h"
#include "Macros.h"
#include <stdio.h>

//////////////////////////////////////////////////////////////////////

enum eMTUDPMsgType
{
  MTUDPMSGTYPE_ACKS         = 0,
  MTUDPMSGTYPE_RELIABLE     = 1,
  MTUDPMSGTYPE_UNRELIABLE   = 2,
  MTUDPMSGTYPE_CLOCK        = 3,

  MTUDPMSGTYPE_NUMMESSAGES  = 4,
};

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

MTUDP::MTUDP()
{
  d_bRunning = false;
  d_bSending = false;
  d_bListening = false;
  d_bIsClientOn = false;
  d_bIsServerOn = false;
  d_listenSocket = 0;
  d_sendSocket = 0;
  d_bytesTransfered = 0;
}


MTUDP::~MTUDP()
{
  StopListening();
  StopSending();
  Cleanup();
}


void MTUDP::Startup( unsigned short localListenPort, unsigned short foreignListenPort )
{
  Cleanup();

  // Start Winsock
  OutputDebugString( "MTUDP::Startup() - Initializing Winsock.\n" );

  WSAData wsaData;
  int     error;

  error = WSAStartup( MAKEWORD( 2, 2 ), &wsaData );
  if( error == SOCKET_ERROR )
  {
    char errorBuffer[ 100 ];

    error = WSAGetLastError();
    if( error == WSAVERNOTSUPPORTED )
    {
      sprintf( errorBuffer, "MTUDP::Startup() - WSAStartup() error.\nRequested v2.2, found only v%d.%d.", 
        LOBYTE( wsaData.wVersion ),
        HIBYTE( wsaData.wVersion ) );
      WSACleanup();
    }
    else
      sprintf( errorBuffer, "MTUDP::Startup() - WSAStartup() error %d", WSAGetLastError() );

    throw cNetError( errorBuffer );
  }

  d_localListenPort = localListenPort;
  d_foreignListenPort = foreignListenPort;
  d_bytesTransfered = 0;
  d_bRunning = true;
}


void MTUDP::StartListening()
{
  if( d_bListening == true ||
      d_bRunning == false )
    return;

  d_bListening = true;

  // Start the listen socket.
  OutputDebugString( "MTUPD::StartListening() - Creating the listen socket.\n" );

  d_listenSocket = socket( AF_INET, SOCK_DGRAM, 0 );
  if( d_listenSocket == INVALID_SOCKET )
  {
    char errorBuffer[ 100 ];

    sprintf( errorBuffer, "MTUPD::StartListening() - socket() error %d", WSAGetLastError() );
    throw cNetError( errorBuffer );
  }

  SOCKADDR_IN localAddr;
  int         result;

  memset( &localAddr, 0, sizeof( SOCKADDR_IN ) );
  localAddr.sin_family = AF_INET;
  localAddr.sin_addr.s_addr = htonl( INADDR_ANY );
  localAddr.sin_port = htons( d_localListenPort );

  result = bind( d_listenSocket, (sockaddr *)&localAddr, sizeof( SOCKADDR_IN ) );
  if( result == SOCKET_ERROR )
  {
    closesocket( d_listenSocket );

    char errorBuffer[ 100 ];

    sprintf( errorBuffer, "MTUPD::StartListening() - bind() error %d", WSAGetLastError() );
    throw cNetError( errorBuffer );
  }

  unsigned long int val;

  val = 1;  // Anything non-zero.
  result = ioctlsocket( d_listenSocket, FIONBIO, &val );
  if( result )
  {
    closesocket( d_listenSocket );

    char errorBuffer[ 100 ];

    sprintf( errorBuffer, "MTUPD::StartListening() - ioctlsocket() error %d", WSAGetLastError() );
    throw cNetError( errorBuffer );
  }


  // Start the thread.
  OutputDebugString( "MTUPD::StartListening() - Starting the listen thread.\n" );

  cThread::Begin();
}


void MTUDP::StartSending()
{
  if( d_bSending == true ||
      d_bRunning == false )
    return;

  d_bSending = true;

  // Create send socket
  OutputDebugString( "MTUPD::StartSending() - Creating the send socket.\n" );

  d_sendSocket = socket( AF_INET, SOCK_DGRAM, 0 );
  if( d_sendSocket == INVALID_SOCKET )
  {
    char errorBuffer[ 100 ];

    sprintf( errorBuffer, "MTUPD::StartSending() - socket() error %d", WSAGetLastError() );
    throw cNetError( errorBuffer );
  }

  SOCKADDR_IN localAddr;
  int         result;

  memset( &localAddr, 0, sizeof( SOCKADDR_IN ) );
  localAddr.sin_family = AF_INET;
  localAddr.sin_addr.s_addr = htonl( INADDR_ANY );
  localAddr.sin_port = htons( 0 );

  result = bind( d_sendSocket, (sockaddr *)&localAddr, sizeof( SOCKADDR_IN ) );
  if( result == SOCKET_ERROR )
  {
    closesocket( d_sendSocket );

    char errorBuffer[ 100 ];

    sprintf( errorBuffer, "MTUPD::StartSending() - bind() error %d", WSAGetLastError() );
    throw cNetError( errorBuffer );
  }

  unsigned long int val;

  val = 1;  // Anything non-zero.
  result = ioctlsocket( d_sendSocket, FIONBIO, &val );
  if( result )
  {
    closesocket( d_sendSocket );

    char errorBuffer[ 100 ];

    sprintf( errorBuffer, "MTUPD::StartSending() - ioctlsocket() error %d", WSAGetLastError() );
    throw cNetError( errorBuffer );
  }
}


void MTUDP::StopListening()
{
  if( d_bListening == false )
    return;

  d_bListening = false;

  cThread::End();

  OutputDebugString( "MTUPD::StopListening() - Closing the listen socket.\n" );
  closesocket( d_listenSocket );
}


void MTUDP::StopSending()
{
  if( d_bSending == false )
    return;

  d_bSending = false;

  OutputDebugString( "MTUPD::StopSending() - Closing the send socket.\n" );
  closesocket( d_sendSocket );
}


void MTUDP::Cleanup()
{
  if( d_bRunning == false )
    return;

  d_bRunning = false;

  StopListening();
  StopSending();

  OutputDebugString( "MTUPD::Cleanup() - Cleaning up Winsock.\n" );

  d_hosts.clear();
  d_hostMap.clear();
  WSACleanup();
}
////////////////////////////////////////////////////////////////////////////////

void MTUDP::StartClient()
{
  d_bIsClientOn = true;
}


void MTUDP::StopClient()
{
  d_bIsClientOn = false;
}


void MTUDP::StartServer()
{
  d_bIsServerOn = true;
}


void MTUDP::StopServer()
{
  d_bIsServerOn = false;
}
////////////////////////////////////////////////////////////////////////////////

DWORD MTUDP::ThreadProc()
{
  if( d_bListening == false )
    return 0;  // Quit already?!

  char            inBuffer[ MAX_UDPBUFFERSIZE ];
  timeval         waitTimeStr;
  SOCKADDR_IN     fromAddr;
  int             fromLen;
  unsigned short  result;
  FD_SET          set;

#if defined( _DEBUG )
  memset( inBuffer, 0xFF, MAX_UDPBUFFERSIZE );
#endif

  OutputDebugString( "MTUDP::ThreadProc() - Listening thread started.\n" );

  try
  {
    while( d_bListening == true )
    {
      // Listen to see if there is data waiting to be read.
      FD_ZERO( &set );
      FD_SET( d_listenSocket, &set );

      waitTimeStr.tv_sec = 0;
      waitTimeStr.tv_usec = 0;

      // Select tells us if there is data to be read.
      result = select( FD_SETSIZE, &set, NULL, NULL, &waitTimeStr );
      if( result == 0 )
	  {
		Sleep( 10 );
        continue;
	  }
      if( result == SOCKET_ERROR )
      {
        char errorBuffer[ 100 ];

        sprintf( errorBuffer, "MTUDP::ThreadProc() - select() error %d", WSAGetLastError() );
        throw cNetError( errorBuffer );
      }

      // Recvfrom gets the data and puts it in inBuffer.
      fromLen = sizeof( SOCKADDR );
      result = recvfrom( d_listenSocket, inBuffer, MAX_UDPBUFFERSIZE, 0, (SOCKADDR *)&fromAddr, &fromLen );
      if( result == 0 )
	  {
		Sleep( 10 );
        continue;
	  }
      if( result == SOCKET_ERROR )
      {
        char errorBuffer[ 100 ];

        sprintf( errorBuffer, "MTUDP::ThreadProc() - recvfrom() error %d", WSAGetLastError() );
        throw cNetError( errorBuffer );
      }

      d_bytesTransfered += result;

      // Put the received data in a mutex-protected queue here.
      ProcessIncomingData( inBuffer, result, ntohl( fromAddr.sin_addr.s_addr ), GetTickCount() );

#if defined( _DEBUG )
  memset( inBuffer, 0xFF, MAX_UDPBUFFERSIZE );
#endif
    }  // while
  }  // try
  catch( cNetError &err )
  {
    OutputDebugString( "MTUDP::ThreadProc() - Listening thread aborted!\n" );
    cThread::SetError( err.Text() );
  }

  OutputDebugString( "MTUDP::ThreadProc() - Listening thread ended.\n" );

  // Returns 1 if the close was not graceful.
  return d_bListening == true;
}


void MTUDP::ProcessIncomingData( char *pData, unsigned short length, DWORD address, DWORD receiveTime )
{
  // Find the host that sent this data.
  cHost              *pHost;
  HOSTLIST::iterator iHost;

  cMonitor::MutexOn();

  for( iHost = d_hosts.begin(); iHost != d_hosts.end(); ++iHost )
  {
    if( (*iHost)->GetAddress() == address )
      break;
  }

  if( iHost == d_hosts.end() )
  {
    DWORD hostID;

    hostID = HostCreate( address, d_foreignListenPort );
    if( hostID == 0 )
    {
      cMonitor::MutexOff();
      throw cNetError( "MTUDP::ProcessIncomingData() - Host creation failed." );
    }
    pHost = d_hostMap[ hostID ];
  }
  else
    pHost = *iHost;


  // Process the header for this packet.
  bool          bMessageArrived;
  unsigned char code;
  char          *ptr;

  bMessageArrived = false;
  ptr = pData;

  while( ptr < pData + length )
  {
    code = *ptr;
    ptr++;

    if( code == MTUDPMSGTYPE_ACKS )
    {
      // Process any ACKs in the packet.
      ptr += pHost->ProcessIncomingACKs( ptr, pData + length - ptr, receiveTime );
    }
    else if( code == MTUDPMSGTYPE_RELIABLE )
    {
      bMessageArrived = true;

      // Process reliable message in the packet.
      ptr += pHost->ProcessIncomingReliable( ptr, pData + length - ptr, receiveTime );
    }
    else if( code == MTUDPMSGTYPE_UNRELIABLE )
    {
      // Process UNreliable message in the packet.
      ptr += pHost->ProcessIncomingUnreliable( ptr, pData + length - ptr, receiveTime );
    }
    else if( code == MTUDPMSGTYPE_CLOCK )
    {
      ptr += ProcessIncomingClockData( ptr, pData + length - ptr, pHost, receiveTime );
    }
    else
    {
      char errorBuffer[ 100 ];

      cMonitor::MutexOff();
      sprintf( errorBuffer, "MTUDP::ProcessIncomingData() - unrecognized block type %d.", code );
      throw cNetError( errorBuffer );
    }
  }


  if( bMessageArrived == true )
  {
    // Send an ACK immediately. If this machine is the
    // server, also send a timestamp of the server clock.
    ReliableSendTo( NULL, 0, pHost->GetAddress() );
  }

  cMonitor::MutexOff();
}


unsigned short MTUDP::GetUnreliableData( char *pBuffer, unsigned short maxLen, HOSTHANDLE *pHostID )
{
  if( pBuffer == NULL ||
      pHostID == NULL )
    throw cNetError( "MTUPD::GetUnreliableData() - Invalid parameters." );

  if( maxLen == 0 )
    return 0;

  cDataPacket        *pPacket;
  HOSTLIST::iterator iHost;

  pPacket = NULL;

  cMonitor::MutexOn();

  // Do our list<> stuff here.
  // Is there any queued, ordered data?
  for( iHost = d_hosts.begin(); iHost != d_hosts.end(); ++iHost )
  {
    pPacket = (*iHost)->GetUnreliableInQueue().GetPacket();
    if( pPacket != NULL )
      break;
  }


  if( pPacket != NULL )
  {
    unsigned short length;

    length = pPacket->d_length > maxLen ? maxLen : pPacket->d_length;
    memcpy( pBuffer, pPacket->d_data, length );

    delete pPacket;

    *pHostID = (*iHost)->GetAddress();

    cMonitor::MutexOff();
    return length;
  }

  cMonitor::MutexOff();
  return 0;
}


unsigned short MTUDP::GetReliableData( char *pBuffer, unsigned short maxLen, HOSTHANDLE *pHostID )
{
  if( pBuffer == NULL ||
      pHostID == NULL )
    throw cNetError( "MTUPD::GetReliableData() - Invalid parameters." );

  if( maxLen == 0 )
    return 0;

  cDataPacket        *pPacket;
  HOSTLIST::iterator iHost;

  pPacket = NULL;

  cMonitor::MutexOn();

  // Do our list<> stuff here.
  // Is there any queued, ordered data?
  for( iHost = d_hosts.begin(); iHost != d_hosts.end(); ++iHost )
  {
    pPacket = (*iHost)->GetInQueue().GetPacket();
    if( pPacket != NULL )
      break;
  }

  if( pPacket != NULL )
  {
    unsigned short length;

    length = pPacket->d_length > maxLen ? maxLen : pPacket->d_length;
    memcpy( pBuffer, pPacket->d_data, length );

    delete pPacket;

    *pHostID = (*iHost)->GetAddress();

    cMonitor::MutexOff();
    return length;
  }

  cMonitor::MutexOff();
  return 0;
}


// ResendData is only called from ResendPackets() and is therefore thread safe.
void MTUDP::ResendData( cDataPacket *pPacket, cHost *pHost )
{
  if( pHost == NULL ||
      pPacket == NULL )
    throw cNetError( "MTUDP::ResendData() - Invalid parameters." );

  char            outBuffer[ MAX_UDPBUFFERSIZE ];
  unsigned short  count;
  DWORD           packetID;

  count = 0;
  memset( outBuffer, 0, MAX_UDPBUFFERSIZE );


  // Attach the ACKs.
  if( pHost->GetInQueue().GetCount() != 0 )
  {
    // Flag indicating this block is a set of ACKs.
    outBuffer[ count ] = MTUDPMSGTYPE_ACKS;
    count++;

    count += pHost->AddACKMessage( &outBuffer[ count ], MAX_UDPBUFFERSIZE );
  }


  // Attach the message data.
  char            *pStr;
  unsigned short  length;

  packetID = pPacket->d_id;
  length = pPacket->d_length;
  pStr = pPacket->d_data;


  // Flag indicating this block is a reliable message.
  outBuffer[ count ] = MTUDPMSGTYPE_RELIABLE;
  count++;

  // Append the message
  memcpy( &outBuffer[ count ], &packetID, sizeof( DWORD ) );
  count += sizeof( DWORD );
  memcpy( &outBuffer[ count ], &length, sizeof( unsigned short ) );
  count += sizeof( unsigned short );
  memcpy( &outBuffer[ count ], pStr, length );
  count += length;


  // Attach the previous reliable message, just to ensure that it gets there.
  cDataPacket secondPacket;

  if( pHost->GetOutQueue().GetPreviousPacket( packetID, &secondPacket ) == true )
  {
    // Flag indicating this block is a reliable message.
    outBuffer[ count ] = MTUDPMSGTYPE_RELIABLE;
    count++;

    // Append the message
    memcpy( &outBuffer[ count ], &secondPacket.d_id, sizeof( DWORD ) );
    count += sizeof( DWORD );
    memcpy( &outBuffer[ count ], &secondPacket.d_length, sizeof( unsigned short ) );
    count += sizeof( unsigned short );
    memcpy( &outBuffer[ count ], secondPacket.d_data, secondPacket.d_length );
    count += secondPacket.d_length;
  }


  count += AddClockData( &outBuffer[ count ], MAX_UDPBUFFERSIZE - count, pHost );


  d_bytesTransfered += count;


⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -