遊戲服務器端通信框架(C++與Socket)

  這是一個小型多個對戰的遊戲服務器端代碼,經過修改。

文件一:stdafx.h

//-------------------------------------------------------------------------
//stdafx.h中的函數爲全局共享
//
//版權所有 DreamShip
//
//-------------------------------------------------------------------------

#ifndef _STDAFX_
#define _STDAFX_

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream.h>
#include <winsock2.h>
#include<time.h>

#include "DSGameServer.h"

#include "DSObject.h"


//-------------------------------------------------------------------
// 字符解碼調用函數
// 在一串字符中尋找ds_Search中後的數據
// 有浮點型 整形 字符型 
//  
//
//------------------------------------------------------------------
float DS_ReturnFloat(char *ds_Dest,char ds_Search) ;
int   DS_ReturnInt(char *ds_Dest,char ds_Search) ;
int   DS_ReturnString(char *ds_Dest,char ds_Search,char *buf ) ;
int   DS_ReturnCharPosition( char *ds_Dest,char ds_Search );
//檢測當前耗時
void DS_PrintTime( long time );
void DS_GetCurrentTime( char timeBuf[] );
void DS_PrintCurrentTime( char *ds_Msg );

#endif

文件二:stdafx.cpp

//----------------------------------------------------------------------------------
//stdafx.cpp用於全局函數的定義
// 
//
//----------------------------------------------------------------------------------

#include "stdafx.h"

//-------------------------------------------------------------------
// 字符解碼調用函數
// 在一串字符中尋找ds_Search中後的數據
// 有浮點型 整形 字符型 
//  
//
//------------------------------------------------------------------
float DS_ReturnFloat(char *ds_Dest,char ds_Search)
{
	long time = timeGetTime();
	int i=0;
    
	while( ds_Dest[i] != '\0' )
	{
		
		if( ds_Dest[i] == ds_Search )
		{
			
			int j = 0;
			i++;
            char buf[50]; 
			while( ds_Dest[i] != '*' )
			{
		    	if( ds_Dest[i] =='\0' )
					return -100.0f ;
				buf[j]=ds_Dest[i] ;
				i++ ;
			    j++ ;
			}
			buf[j]='\0';
		
	       	if( buf[0] >= '0' && buf[0] <= '9' )
				return (float)atof(buf);
			else
				return -10000.0f;
		
		}

		i++ ;
	}
	printf("查找不成功所需時間:%d\n",timeGetTime()-time);
	return -10000.0f ;
}

//-------------------------------------------------------------------
// 字符解碼調用函數
// 在一串字符中尋找ds_Search中後的數據
// 有浮點型 整形 字符型 
//  
//
//------------------------------------------------------------------
int DS_ReturnInt(char *ds_Dest,char ds_Search)
{
	long time = timeGetTime();
	int i=0;

	while( ds_Dest[i] != '\0' )
	{
		
		if( ds_Dest[i] == ds_Search )
		{
			
			int j = 0;
			i++;
            char buf[50]; 
			while( ds_Dest[i] != '*' )
			{
			
				if( ds_Dest[i] =='\0' )
					return -100 ;

				buf[j]=ds_Dest[i] ;
				i++ ;
				j++ ;
				
			}
			buf[j]='\0';
		
			if( buf[0] >= '0' && buf[0] <= '9' )
			    return atoi(buf);
			else
				return -10000;
		}

		i++ ;
	}
	printf("查找不成功所需時間:%d\n",timeGetTime()-time);
	return -10000;
}

//-------------------------------------------------------------------
// 字符解碼調用函數
// 在一串字符中尋找ds_Search中後的數據
// 有浮點型 整形 字符型 
//  
//
//------------------------------------------------------------------
int DS_ReturnString(char *ds_Dest,char ds_Search,char *buf )
{
	long time = timeGetTime();
    int i=0;

	while( ds_Dest[i] != '\0' )
	{
		
		if( ds_Dest[i] == ds_Search )
		{
			
			int j = 0;
			i++;
            
			while( ds_Dest[i] != '*' )
			{
			
				if( ds_Dest[i] =='\0' )
					return -1 ;
				buf[j]=ds_Dest[i] ;
				i++ ;
				j++ ;
				
			}
			buf[j]='\0';
			return 1;
		}

		i++ ;
		
	}
	printf("查找不成功所需時間:%d\n",timeGetTime()-time);
	return 0;
}
//---------------------------------------------------------------------------
//用於獲取當前字符的位置
//
//
//----------------------------------------------------------------------------
int DS_ReturnCharPosition(char *ds_Dest,char ds_Search)
{
    long time = timeGetTime();
    int i=0;

	while( ds_Dest[i] != '\0' )
	{
		
		if( ds_Dest[i] == ds_Search )
		   return i ;
		i++ ;
		
	}
	printf("查找不成功所需時間:%d\n",timeGetTime()-time);
	return -1;
}
//---------------------------------------------------------------------------
//用於測試當前耗時
//
//
//----------------------------------------------------------------------------
void DS_PrintTime( long time )
{
  printf( " 程序耗時:%d \n " , timeGetTime()-time );
}
//-------------------------------------------------
//獲得當前時間
//
//
//------------------------------------------------------
void DS_GetCurrentTime(char timeBuf[])
{
    struct tm *p;
	long ltime;
	time(&ltime);
	p=localtime(&ltime);
	strftime(timeBuf,25,"%a %d %b %Y %H:%M:%S",p);
}
//---------------------------------------------------
//打印當前時間
//
//
//------------------------------------------
void DS_PrintCurrentTime( char *ds_Msg )
{
	char buf[29];
	struct tm *p;
	long ltime;
//	_strtime(buf);
//	printf("當前時間爲:\t\t\t\t%s\n", buf);
//	_strdate(buf);
//	printf("當前日期爲:\t\t\t\t%s\n", buf); 
	time(&ltime);
	p=localtime(&ltime);
	strftime(buf,29,"%a %d %b %Y %H:%M:%S GMT",p);
	printf("%s時間:%s\n",ds_Msg,buf);

}


文件三:DSObject.h

//-------------------------------------------------------------
//DSObject.h
// 用於對系統的調度
//
//---------------------------------------------------------------

#ifndef _DSOBJECT_
#define _DSOBJECT_

//DSENEMY 指當前爲敵人 DSBULLET 指當前類型爲子彈
//DSPLAYER 指當前類型爲自已 DSHEAD 指當前類型爲頭結點 
enum OBJECT_TYPE { DSENEMY,DSBULLET,DSPLAYER,DSHEAD,DSNULL};
enum NETOBJECT_TYPE{ DSRED,DSBLUE,DSNETOTHERS,DSNETHEAD,DSNETNULL};

struct D3DXVECTOR3 
{
	float x ;
	float y ;
	float z ;
};

class DSObject
{
public :
	DSObject( OBJECT_TYPE ds_Type );
	DSObject();

	~DSObject();

   virtual HRESULT  DS_InitObject();//用於對象的初始化
   virtual void     DS_FrameMove( );//用於計算 
   virtual void     DS_RenderScene();//用於渲染
   virtual void     DS_CleanUp();//清空處理

   //用於網絡對象的幀刷新
   virtual HRESULT  DS_NetFrameMove();
   //用於該對象的顯示
   virtual void   DS_NetRenderScene();
   
   
public:
	DSObject        *ds_Previous; //前驅結點
	DSObject        *ds_Next;   //後繼尋點

	OBJECT_TYPE     ds_Type;  //對象的類型
	bool            ds_Active;//是否處於存活狀態
	int             ds_Health;//目前的生命值
	
	int             ds_ID ;//對象的ID號
	NETOBJECT_TYPE  ds_NetType ;//其網絡類型
    char            ds_Username[20]; //客戶機姓名

	float           ds_Radius;//碰撞半徑
	D3DXVECTOR3     ds_CurrentPos;//當前位置 
	int             ds_Score ;//記錄當前的戰績
};

#endif


文件四:DSObject.cpp

//-------------------------------------------------------------
//DSObject.cpp
// 用於對系統的調度
// 是DSEnemy DSPlayer DSBullet 的父類
//---------------------------------------------------------------
#include "stdafx.h"

//---------------------------------------------------------------
//函數名:DSObject()
//作用: 爲構造函數用於初始化參數
//----------------------------------------------------------------
DSObject::DSObject( OBJECT_TYPE ds_Type )
{
	this->ds_Type = ds_Type;
    ds_Active = true;
	this->ds_NetType = DSNETNULL ;
	this->ds_ID = -1000 ;
	strcpy(ds_Username,"非網絡用戶");
    ds_Previous = NULL ;
	ds_Next = NULL ;
	ds_Score = 0 ;
}

//---------------------------------------------------------------
//函數名:DSObject()
//作用: 爲構造函數用於初始化網絡參數
//----------------------------------------------------------------
DSObject::DSObject()
{
	 ds_Active = true;
	 strcpy(ds_Username,"網絡用戶");
	 ds_Previous = NULL ;
	 ds_Next = NULL ;
	 ds_Score = 0 ;
}
//---------------------------------------------------------------
//函數名:~DSObject()
//作用:析構函數
//----------------------------------------------------------------
DSObject::~DSObject()
{
	DS_CleanUp();
}

//---------------------------------------------------------------
//函數名:DS_InitObject()
//作用:
//----------------------------------------------------------------
HRESULT DSObject::DS_InitObject()
{	
	return S_OK;
}

//---------------------------------------------------------------
//函數名:DS_FrameMove()
//作用:
//----------------------------------------------------------------
void DSObject::DS_FrameMove(  )
{
   return ;	
} 

//---------------------------------------------------------------
//函數名:DS_CleanUp()
//作用:
//----------------------------------------------------------------
void DSObject::DS_CleanUp()
{
	return ;	
} 

//---------------------------------------------------------------
//函數名:DS_RenderScene()
//作用:
//----------------------------------------------------------------
void DSObject::DS_RenderScene(  ) 
{
	return ;	
} 
//---------------------------------------------------------------
//函數名:DS_NetFrameMove()
//作用:
//----------------------------------------------------------------
HRESULT DSObject::DS_NetFrameMove()
{
	return S_OK ;
}
//----------------------------------------------------------------
//DS_NetRenderScene()
//
//----------------------------------------------------------------
void DSObject::DS_NetRenderScene( )
{
	return ;
} 


文件五:DSGameServer.h

//-----------------------------------------------------------------------------
//  DSGameServer.h
//  用於遊戲中的網絡通訊架構
// 
//------------------------------------------------------------------------------

#ifndef _DSGAMESERVER_
#define _DSGAMESERVER_

//最大上限人數
#define MAX_NUM  150

//開放的端口號
#define DEFAULTPORT 12345
#include "DSObject.h"

//用於客戶機信息的綁定
//在此包括客戶機的套接字 在運行其間服務器所分配的ID號
//其鏈接的IP地址 服務器爲其分配的發送者線程號

struct dsClientInformation
{
	SOCKET ds_Sock ;//當前的套接字
	sockaddr_in ds_Client ;//其客戶機信息
	int ds_ID ;//服務器分配的ID號
	DWORD ds_RecvThreadID ;//服務器分配接收的線程號
//	DWORD ds_SendThreadID ;//服務器分配的發送線程號 
	bool ds_Active   ;
};

//遊戲服務器類
//

class DSGameServer
{

public:
	DSGameServer();
	~DSGameServer();

    int DS_ProcessGameServer( );//用於線程處理

	int DS_SendMessage( int ID ,char *buf );//向某一客戶機發送信息
	
	int DS_CheckSocket();//檢測當前可用的ID號
    void DS_CleanSocket( int ID );//清空ID號的套接字
	void DS_SendAll( char *buf,int ID = -1  ) ;//向所有的用戶發送信息

public:
	static DWORD WINAPI DS_ListenThread(void *data);//接收線程
	
    SOCKET ds_ListenSocket;	        // 等待接收數據的socket
	sockaddr_in ds_Server;			// 綁定地址

	dsClientInformation ds_AcceptSocket[MAX_NUM] ;//客戶機的關聯消息

//以下是對網絡數據的處理
public:
	//對內核鏈表的操作
    int	DS_AddObject( DSObject *ds_New );
	int DS_RemoveObject( DSObject *ds_New );
    void DS_Print();

	//對網絡數據的處理(可能爲多流數據所以需要輪循處理)
	int DS_ProcessData( char *ds_NetData );
	
	//對所到的數據的識別與解析
	int DS_ParseMsgSTC( char *ds_Msg );

	//玩家結點的創建與維護
	DSObject* DS_PlayerProcess( int ID,char *ds_Msg,int type = 0 );

	int DS_CheckState(int ID);//檢測當前的狀態

public:
	DSObject *ds_Head ;//包留頭

	int ds_EnemyNum ;
	int ds_ObjectNum ;
	int ds_RedNum ;
	int ds_BlueNum ;

	int ds_RedCurrentNum ;//記錄遊戲中爲紅方的數量
	int ds_BlueCurrentNum ;//記錄遊戲中爲藍方的數量

	bool ds_IsPassword ;//是否有密碼
	char ds_Password[50];//密碼
};

#endif


文件六:DSGameServer.cpp

#include "stdafx.h"

DSGameServer *ds_GameDebugServer ;
//-----------------------------------------------------------------------------
//函數名:
//描述:用於系統的初始化
//   主要是Win32通訊的初始化
//
//-----------------------------------------------------------------------------

DSGameServer::DSGameServer()
{
	WSADATA wsaData;

	//加載當前協議
	if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0)
	{
		printf("無法加載套接字協議棧\n");
		return ;
	}

	//設置偵聽套接字
	ds_ListenSocket=socket(AF_INET,SOCK_STREAM,0);

	if(ds_ListenSocket==INVALID_SOCKET)
	{
		printf("套接字初始化出現錯誤;錯誤號:%d\n",WSAGetLastError());
		return ;
	}

	//設定服務器的信息
	ds_Server.sin_family=AF_INET;
	ds_Server.sin_port=htons( DEFAULTPORT );
	ds_Server.sin_addr.s_addr=htonl(INADDR_ANY);

	//對服務器的信息進行綁定
	if(bind(ds_ListenSocket,(LPSOCKADDR)&ds_Server,sizeof(ds_Server))==SOCKET_ERROR)
	{
		printf("綁定出現問題錯誤號碼:%d\n",WSAGetLastError());
		return ;
	}

	//進入偵聽狀態
	if(listen(ds_ListenSocket,5)==SOCKET_ERROR)
	{
		printf("監聽出現問題錯誤號碼:%d\n",WSAGetLastError());
		return ;
	}

	//將所有信息進行初始化
	for(int i=0;i<MAX_NUM;i++)
		ds_AcceptSocket[i].ds_Sock = NULL;

   	
	printf("網絡通訊初始化成功\n");


	ds_GameDebugServer = this ;

	if( ds_GameDebugServer == NULL )
	{
		printf("當前創建Server出錯\n");
	    return ;
	}

	ds_Head = new DSObject() ;
    ds_Head->ds_NetType = DSNETHEAD ;
    ds_Head->ds_Type = DSHEAD ;
	ds_Head->ds_ID = -1 ;
	
	ds_EnemyNum = 0 ;
	ds_ObjectNum = 0;
	ds_RedNum = 0 ;
	ds_BlueNum = 0 ;
	
	ds_IsPassword = false ;
	strcpy(ds_Password,"");
	
}

//-----------------------------------------------------------------------------
//函數名:析構函數
//描述:用於資源的釋放
//
//-----------------------------------------------------------------------------
DSGameServer::~DSGameServer( )
{
	if( ds_ListenSocket != NULL )
	  closesocket( ds_ListenSocket );
	
	WSACleanup();
} 

//-----------------------------------------------------------------------------
//函數名:
//描述:用於檢測當前沒有用的ID號
//
//-----------------------------------------------------------------------------
int DSGameServer::DS_CheckSocket( )
{
	for(int i=0;i<MAX_NUM;i++)
	{
		if( ds_AcceptSocket[i].ds_Sock == NULL )
			return i;
	}

	return -1;
} 

//--------------------------------------------------------------------
//
//描述:爲每一個新加入的玩家創建一個接收線程
//      同時若人數達到上限 則進入相應的處理
//
//------------------------------------------------------------------------
int DSGameServer::DS_ProcessGameServer()
{

    while(true)
	{
	 
		//獲取當前可用的套接字
	 int index = DS_CheckSocket();

	 sockaddr_in ds_Client ;//非正常的客戶信息
	 
	 int ds_Len = sizeof( ds_Client );
	 
	 if( index != -1 )
	 {		
        
      	ds_AcceptSocket[index].ds_Sock=accept(ds_ListenSocket,(struct sockaddr*)&ds_AcceptSocket[index].ds_Client,&ds_Len);
		ds_AcceptSocket[index].ds_ID = index ;
		ds_AcceptSocket[index].ds_Active = false ;//用以標識是否爲經過校驗

		if( ds_AcceptSocket[index].ds_Sock == INVALID_SOCKET )
		{
			printf("連接出現錯誤代碼: %d\n",WSAGetLastError());
			break;
		}
		printf("\n<<<<<<<<<<<<<<有新玩家到>>>>>>>>>\n新玩家的IP地址爲:[%s],端口號:[%d]\n",
			inet_ntoa(ds_AcceptSocket[index].ds_Client.sin_addr),
			ntohs(ds_AcceptSocket[index].ds_Client.sin_port));
        
		// 提示客戶輸入密碼
		
		char ds_PassBuf[100];
		sprintf(ds_PassBuf,"#IP i%d*p0*",ds_AcceptSocket[index].ds_ID );
		DS_SendMessage(ds_AcceptSocket[index].ds_ID,ds_PassBuf);
       
		//創建接收者線程
		int ThreadID;	// 線程id
	
	    //調用createthread創建線程
	   
	    ThreadID = (int)CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)(DSGameServer::DS_ListenThread), (void *)&ds_AcceptSocket[index], 0, &ds_AcceptSocket[index].ds_RecvThreadID);
	    ThreadID = ThreadID ? 0 : 1;	// 如果成功,則返回爲0

	    if(ThreadID)	// ThreadID如果不爲0,則線程創建失敗
		{
	    	printf("線程創建失敗\n");
		    ExitThread(ds_AcceptSocket[index].ds_RecvThreadID );
		}
      
       printf("********新玩家ID[%d]的接收線程創建成功*********\n",index );
	
	 }
	else 
	 {
		
        SOCKET ds_Accept=accept(ds_ListenSocket,(struct sockaddr*)&ds_Client,&ds_Len);
		  
		 if(ds_Accept==INVALID_SOCKET)
		  {
			printf("連接出現錯誤代碼: %d\n",WSAGetLastError());
			break;
		  }

		printf("\n非正常請求的玩家IP地址爲:[%s],端口號:[%d]\n",inet_ntoa(ds_Client.sin_addr),
			ntohs(ds_Client.sin_port));

        send( ds_Accept,"#FF i0*m當前用戶已滿*",strlen("#FF m當前用戶已滿 "),0 );
		
		closesocket( ds_Accept );
		
		printf("<<<<<<<<<<非法連接玩家已斷開>>>>>>>>>>>>>>..\n");
	 
	 }
	
	}
	return 0;
}
//-----------------------------------------------------------------------------
//函數名:爲接收線程
//描述:用select模式對IO進行管理
// 首先判斷該線程是否可讀 若可讀則取出其上的信息
//-----------------------------------------------------------------------------
DWORD WINAPI DSGameServer::DS_ListenThread(void *data)
{

  dsClientInformation *ds_GameSocket = (dsClientInformation *)data ;

  while(true)
  {
	  if( ds_GameSocket->ds_Sock == NULL )
	  {
        ds_GameDebugServer->DS_CleanSocket( ds_GameSocket->ds_ID );
		continue ;
	  }
     //接收數據
	  char buf[1024] ;
	  fd_set ds_Read;//基於select模式對IO進行管理
	  
	  FD_ZERO(&ds_Read);
	  FD_SET(ds_GameSocket->ds_Sock,&ds_Read);

	  select(0,&ds_Read,NULL,NULL,NULL) ;
	  
      if(FD_ISSET(ds_GameSocket->ds_Sock,&ds_Read) )	  
	   {
	     int result = recv( ds_GameSocket->ds_Sock, buf,sizeof(buf),0 ) ;
	     
		 if ( result > 0 )
		 {
		    buf[result] = 0 ;
		    printf("\n玩家ID[%d]消息到: %s",ds_GameSocket->ds_ID,buf) ;
			ds_GameDebugServer->DS_SendAll(buf,ds_GameSocket->ds_ID) ;
			ds_GameDebugServer->DS_ProcessData( buf ); 
            fflush(0) ;
		 }
		 else
		 {
		   ds_GameDebugServer->DS_CleanSocket( ds_GameSocket->ds_ID ); 
		 }
		 
		}
	 }

  return 1;
} 

//-----------------------------------------------------------------------------
//函數名:用於發送信息 
//描述:
//
//-----------------------------------------------------------------------------
int DSGameServer::DS_SendMessage( int ID , char *buf )
{
   	if( ID<0 )
		return 0 ;

	 printf("向玩家ID%d 發送的信息是:%s\n",ID,buf);
	 int iSend = send(ds_AcceptSocket[ID].ds_Sock,buf,strlen(buf),0 ) ;
	  
	 if( iSend == SOCKET_ERROR )//==WSAECONNABORTED || iSend == WSAECONNRESET
	 {
	 	printf("向ID[%d]發送DS_SendMessage出現錯誤\n",ID) ;
		//DS_CleanSocket( ID );
		ds_AcceptSocket[ID].ds_Sock = NULL ;
	 }

    return 1;
} 
//----------------------------------------------------
//全發送
//
//----------------------------------------------------
void DSGameServer::DS_SendAll(char *buf,int ID )
{
	printf("向全體成員發送:%s\n",buf);
	for(int i=0 ; i<MAX_NUM ; i++ )
	{
		if( ds_AcceptSocket[i].ds_Sock != NULL && i != ID && ds_AcceptSocket[ID].ds_Active )
			DS_SendMessage(i,buf);
	}
}
//-----------------------------------------------------------------
//用於對象的清空處理
//
//------------------------------------------------------------------
void DSGameServer::DS_CleanSocket( int ID )
{
	if( ID < 0 )
		return ;

	char send[20];
	sprintf(send,"#DD i%d*",ID);
	DS_SendAll( send,ID );
  	
	printf("<<<<<<<<<<<<玩家[%d]退出遊戲>>>>>>>>>>>\n",ID);
	
	DSObject *p = ds_Head->ds_Next ;
	
	while( p )
	{
		if( p->ds_ID == ID )
		{
		  printf("正在清空其在內核中的內容\n");
		  DS_RemoveObject( p );
		  break ;
		}
		p = p->ds_Next ;
	}

	ds_AcceptSocket[ID].ds_Active = false ;
	closesocket( ds_AcceptSocket[ID].ds_Sock );
    ds_AcceptSocket[ID].ds_Sock = NULL ;
		
	printf("正在關閉他的接收線程爲%d\n",ds_AcceptSocket[ID].ds_RecvThreadID );
	
    ExitThread(ds_AcceptSocket[ID].ds_RecvThreadID );
	printf("***************************************\n");
	printf("<<<<<<<<<<<<<<關閉成功>>>>>>>>>>>>>>>>>>>>>>>\n");
}
//---------------------------------------------------
// 將對象加入系統的鏈表中
//  第一:找到尾結點
//  第二:將尾結點的next域指向要加入的結點
//        將新結點的previous域指向尾結點
//  第三:若要加入的爲敵人對象則敵人的數量加一 
//        總的對象數加一
//-----------------------------------------------------------------------
int DSGameServer::DS_AddObject( DSObject *ds_New )
{
	if( ds_New == NULL )
		return 0;

	DSObject *p;
	p = ds_Head;
   
  //找到尾結點
	while( p->ds_Next != NULL )
		p = p->ds_Next ;

	//  將尾結點的next域指向要加入的結點
    //  將新結點的previous域指向尾結點
	p->ds_Next = ds_New ;
	ds_New->ds_Previous = p ;
   
	p = NULL ;
	free( p );
    
	//  若要加入的爲敵人對象則敵人的數量加一 
    //  總的對象數加一
	if( ds_New->ds_Type == DSENEMY )
       ds_EnemyNum++;

	if(ds_New->ds_NetType == DSRED )
	{
		ds_RedCurrentNum ++ ;
		ds_RedNum ++ ;
	}
	if(ds_New->ds_NetType == DSBLUE )
	{
		ds_BlueCurrentNum++ ;
		ds_BlueNum ++ ;
	}

	ds_ObjectNum++;

	return 1;
}

//-----------------------------------------------------------------------------
//第一:若當前鏈表爲空 則轉到//*************
//第二:若爲非空則遍歷查找若找到該結點則
//     1.若該結點位於鏈尾 則 p->ds_Previous->ds_Next = NULL
//     2.若在鏈表頭於尾之間則
//            p->ds_Previous->ds_Next = p->ds_Next ;
//			  p->ds_Next->ds_Previous = p->ds_Previous ;
//第三:若該結點爲敵人則敵人數量減一
//      總對象數減一 
//-----------------------------------------------------------------------------

int DSGameServer::DS_RemoveObject( DSObject *ds_New )
{
	// 若對象這空則
	if( ds_New == NULL )
		return 0;

	DSObject *p;
	p = ds_Head->ds_Next ;

	//循環查找
	while( p != NULL )
	{
		//如果找到則清空該結點
		if( p == ds_New )
		{
			//若在鏈表頭於尾之間
			if(ds_New->ds_Next != NULL )
            {
			  p->ds_Previous->ds_Next = p->ds_Next ;
			  p->ds_Next->ds_Previous = p->ds_Previous ;
			}
			//若該結點位於鏈尾
			else
			 p->ds_Previous->ds_Next = NULL ;

			//清空該結點
			p->ds_Previous = NULL ;
			p->ds_Next = NULL ; 
			ds_New = NULL ;

			//若該結點爲敵人對象則
			if(p->ds_Type == DSENEMY )
				ds_EnemyNum-- ;

		    if( p->ds_NetType == DSRED )
			{
               ds_RedCurrentNum --;
		       ds_RedNum -- ;
			}

	        if( p->ds_NetType == DSBLUE )
			{
			   ds_BlueCurrentNum -- ;
		       ds_BlueNum -- ;
			}

			ds_ObjectNum--;
		
			free(p);
            return 1 ; 
		}
    	p = p->ds_Next ;
	}

//******************************
	//若當前鏈表爲空 或沒找到
	free( p );
    return 0;
}
//--------------------------------------------------------------------
//打印當前客戶信息
//------------------------------------------------------------------
void DSGameServer::DS_Print()
{
    DSObject *p;
	p = ds_Head->ds_Next ;

	printf("ID   類型  生命值  存活  戰績 <x   y   z  > \n ");
	
	while( p )
	{
	  printf(" %d    %d    %d     %d   %d    <%f,%f,%f>\n ",p->ds_ID,p->ds_NetType,
		  p->ds_Health,p->ds_Active, p->ds_Score,p->ds_CurrentPos.x,p->ds_CurrentPos.y,p->ds_CurrentPos.z ); 
	   
		p = p->ds_Next ;
	}

	p = NULL ;
	free(p);
	printf("紅方:%d 藍方: %d  總人數: %d\n",ds_RedNum,ds_BlueNum,ds_ObjectNum);
}
//---------------------------------------------------------------------------------
//
//內部解碼器
//  用於多發流量的解碼
//
//對於所要傳輸的數據是以#開頭,若要在某一緩衝區內接收到了
//多個命令則啓動輪循檢測以實現數據的準確接收
//
//--------------------------------------------------------------------------------
int DSGameServer::DS_ProcessData( char *ds_NetData )
{
  char ds_Dest[1024] ;
  
  strcpy( ds_Dest,ds_NetData );
  
  printf("%s\n",ds_Dest);

  while(true)
  {
    int ds_Count = DS_ReturnCharPosition(ds_Dest,'#');
  
    //查找工作結束
    if( ds_Count == -1 )
	  return 0 ;

    char ds_Msg[100] ;
    ds_Msg[0] = '#' ;
    int count = ds_Count +1 ;
    int temp = 1 ;

     while( ds_Dest[count] != '#' && ds_Dest[count] != '\0' )
	 {
	    ds_Msg[temp++] = ds_Dest[count++] ;
	 }
  
      ds_Msg[temp] ='\0';
     
	  //對命令進行解碼
	  printf("解析的信息爲:%s\n",ds_Msg);

	  if( ds_Msg[temp-1] == '*' )
           DS_ParseMsgSTC(ds_Msg);
     
     ds_Dest[ds_Count] = '@';
  }
  return 1 ;
} 
//------------------------------------------------------------------------------------
//檢測當前狀態
//可能有問題要注意此處的用法
//ID暫時用不到
//------------------------------------------------------------------------------------
int DSGameServer::DS_CheckState(int ID)
{

	if(ds_RedNum >0 && ds_BlueNum >0 )
		return 0 ;
  	   
	  char send[20];

	  //如果藍方勝
	  if(ds_RedNum <= 0)
	      sprintf(send,"#ST i0*mBlue Success!*");
		 
	  //如果紅方勝
	  else if(ds_BlueNum <= 0)
	      sprintf(send,"#ST i1*mBlue Success!*");

	  DS_SendAll(send);

	  //對新一局進行初始化工作
	  float x = 12.0f ;
	
	  float z = 12.0f ;

	  DSObject *p ;

	  p = ds_Head->ds_Next ;

	  while( p )
	  {
		  p->ds_Active = true ;
		  p->ds_Health = 100 ;
		  srand( timeGetTime() );

		  p->ds_CurrentPos.y =rand()%3+4.0f ;

		  if( p->ds_NetType == DSRED )
		  {
			  p->ds_CurrentPos.x = x+rand()%20 ;
			  p->ds_CurrentPos.z = z+rand()%20 ;
		  }
		  else
		  {
              p->ds_CurrentPos.x = -x-rand()%20 ;
			  p->ds_CurrentPos.z = -z-rand()%20 ;
		  }

		  p = p->ds_Next ;
	  }
	  p = NULL ;
	  free( p ) ;
	  printf("執行完成了新一局的初始化\n");
	  DS_Print();
	  return 1 ;
	  
} 
//----------------------------------------------------------------------------------------
//客戶機解碼分析器
//主要分析服務器的返回信息
// 首先提取頭三個字符進行標頭分析
//
//---------------------------------------------------------------------------------------
int DSGameServer::DS_ParseMsgSTC( char *ds_Msg )
{
	//第一步校驗
    //檢測數據的長度是否合適 <#DD i0*>
	//按照編碼要求最小應爲7個單位長度
    if( strlen(ds_Msg) < 7 )
	{
        printf("當前沒有要處理的信息結構不對\n"); 
		return -2 ;
	}

    long time = timeGetTime() ;

//提取頭三個字符並保存在 ds_Temp 變量中
	char ds_Temp[3];
	for( int i = 0 ; i<3 ; i++ )
		ds_Temp[i] = ds_Msg[i] ;
	ds_Temp[3] = '\0' ;
    
//進入第二步校驗 先檢測ID號是否正確
	int ID ;
	if( (ID = DS_ReturnInt(ds_Msg,'i') ) == -10000 )
	{
		printf("當前要求處理的信息有誤ID號不能爲-10000\n");
		return -2 ;
	}

	//表示新客戶機的信息
	//格式爲:<#OP i2 x0.32f y2.5f z23.5f t0 uliubing %523667 >
	//用務器檢測密碼若正確則向其傳送其他客戶機的資料 
	//並同時向其他客戶機傳送新機的信息<#NC i3 x12.0f y23.0f z23.02f t1 >
	//若錯誤則斷開併發送<#FP 密碼錯誤 >
	if( strcmp(ds_Temp ,"#OP" ) == 0 )
	{
		printf("<<<<<<<<<<<正在進行校驗工作>>>>>>>>>>>>>>>>>\n");

		//進行密碼的校驗
		if( ds_GameDebugServer->ds_IsPassword )
		{
			char password[50];

			//如果獲取密碼錯誤則置爲空
			if( DS_ReturnString(ds_Msg,'?',password ) == 0 )
				strcpy(password,"");
			
			if( strcmp(ds_GameDebugServer->ds_Password,password ) != 0 )
			{
              ds_GameDebugServer->DS_SendMessage( ID,"#FP i0*m密碼錯誤*");
			  ds_GameDebugServer->DS_CleanSocket( ID ); 
			  return -2 ;
			}
		}

      // 如果通過驗證則
	  // 1更新其在服務器的信息加入內核管理
      //2 向其他客戶機傳送新機消息
	  //3 向其傳送其他客戶機消息
      DSObject *p ;
	  if ( ( p = ds_GameDebugServer->DS_PlayerProcess(ID,ds_Msg,1 ) ) == NULL )
	  {
		  printf("加入該玩家ID[%d]出現失敗\n",ID );
		  return -2 ;
	  }
       

       DS_PrintCurrentTime("內核管理打印");
	   ds_GameDebugServer->DS_Print();
	   printf("***************************************************\n");
	  //向其他客戶機發送新機器的信息
	  char ds_NewInfor[300];
	  
	  sprintf(ds_NewInfor,"#NC i%d*t%d*x%f*y%f*z%f*u%s*",
		  p->ds_ID,p->ds_NetType,p->ds_CurrentPos.x,
		  p->ds_CurrentPos.y,p->ds_CurrentPos.z,
		  p->ds_Username );
	  ds_GameDebugServer->DS_SendAll( ds_NewInfor,ID );
     
	   DS_PrintCurrentTime("向全體發送打印");
	   ds_GameDebugServer->DS_Print();
	   printf("***************************************************\n");
	   
	  //向新玩家發送其他客戶機的信息
       p = ds_Head->ds_Next ;

	   while( p )
	   {
		   if( p->ds_ID != ID )
		   {
		     char ds_NewInfor[300];
	  
	         sprintf(ds_NewInfor,"#NC i%d*t%d*x%f*y%f*z%f*u%s*h%d*s%d*",
		              p->ds_ID,p->ds_NetType,p->ds_CurrentPos.x,
		              p->ds_CurrentPos.y,p->ds_CurrentPos.z,
		              p->ds_Username,p->ds_Health,p->ds_Score );

		      ds_GameDebugServer->DS_SendMessage( ID,ds_NewInfor );
		      ZeroMemory(ds_NewInfor,300);
		   }

           p = p->ds_Next ;
	   }

	   p = NULL ;
	   free( p );

	   //爲可用狀態
       ds_AcceptSocket[ID].ds_Active = true ;

	   DS_PrintCurrentTime("向個人打印");
	   ds_GameDebugServer->DS_Print();
	   printf("***************************************************\n");
	   return 1 ; 
	}



	//表示某一玩家要更新信息
	//其格式爲:<#CU i3*x12.0f*y23.0f*z23.02f*h12*s0*>
	//ID號 座標 當前生命值
	//由於網絡在設計過程中可能存在一些問題 可能的新用戶到時在此這前沒有收到故在此
	//在進行一次檢測
	else if( strcmp(ds_Temp ,"#CU" ) == 0 ) 
	{
       	printf("<<<<<<<<<<<<<<正在進行用戶信息刷新工作>>>>>>>>>>>>>>>>>>>>\n") ;
	
		DSObject *p ;
	    
		if( ( p = ds_GameDebugServer->DS_PlayerProcess(ID,ds_Msg,0) ) == NULL )
		{
			printf("更新失敗\n");
			return -2 ;
		}
        
		ds_GameDebugServer->DS_SendAll( ds_Msg,ID );
		
        return 1 ;
	}

	//表示某一玩家死亡
	//其格式爲:<#CD i3*s0*>
	//ID號  戰績
	//將其 Active = false ;
	
	else if( strcmp(ds_Temp ,"#CD" ) == 0 ) 
	{
        printf("<<<<<<<<<<<<<<<<<玩家掛了>>>>>>>>>>>>>>>>>>>>>>>>>\n");
        DS_Print();
		printf("*****************************************************\n");
		DSObject *p ;
	    
		if( ( p = ds_GameDebugServer->DS_PlayerProcess(ID,ds_Msg,0) ) == NULL )
		{
			printf("更新失敗\n");
			return -2 ;
		}

		p->ds_Health = 0 ;
		p->ds_Active = false ;
		//此處加上檢測當前人數 以便判定那方勝
		//應有一個返回類型
		
        ds_GameDebugServer->DS_SendAll( ds_Msg,ID );
		//位置應放在這
		if (p->ds_NetType == DSRED )
			ds_RedNum -- ;
		else if(p->ds_NetType == DSBLUE )
			ds_BlueNum-- ;
        
		if(ds_RedNum >0 && ds_BlueNum >0 )
		 return 0 ;
  	   
	  char send[60];

	  //如果藍方勝
	  if(ds_RedNum <= 0)
	      sprintf(send,"#ST i0*mBlue Success!*");
		 
	  //如果紅方勝
	  else if(ds_BlueNum <= 0)
	      sprintf(send,"#ST i1*mRed Success!*");

	 printf("向全體成員發送:%s\n",send);
	  for(int i=0 ; i<MAX_NUM ; i++ )
	  {
		if( ds_AcceptSocket[i].ds_Sock != NULL )
			DS_SendMessage(i,send);
	  }

	  printf("***************%s***********\n",send);

	  //對新一局進行初始化工作
	  float x = 12.0f ;
	
	  float z = 12.0f ;

	  p = NULL ;

	  p = ds_Head->ds_Next ;

	  while( p )
	  {
		  p->ds_Active = true ;
		  p->ds_Health = 100 ;
		  srand( timeGetTime() );

		  p->ds_CurrentPos.y =rand()%3+4.0f ;

		  if( p->ds_NetType == DSRED )
		  {
			  p->ds_CurrentPos.x = x+rand()%20 ;
			  p->ds_CurrentPos.z = z+rand()%20 ;
		  }
		  else
		  {
              p->ds_CurrentPos.x = -x-rand()%20 ;
			  p->ds_CurrentPos.z = -z-rand()%20 ;
		  }

		  p = p->ds_Next ;
	  }
	  p = NULL ;
	  free( p ) ;
	  printf("執行完成了新一局的初始化\n");
	  DS_Print(); 
    	return 1 ;
	}
	//顯示當前被擊中 <#HC i0*c0*I8*>
	//計算被擊中的次數 若損失的生命值大於當前的生命值則死亡 
	else if( strcmp(ds_Temp,"#HC") == 0)
	{
	     printf("<<<<<<<<<<<<<<<<<<<<<<<某一玩家被擊中>>>>>>>>>>>>>>>>>>>>>\n");
		 
		 ds_GameDebugServer->DS_PlayerProcess(ID,ds_Msg,3);
         return 1 ;
	}

   //表示某一玩家斷開連接
	//其格式爲:<#DD i4 >
	//將其所有的消息刪除
	
	else if( strcmp(ds_Temp ,"#DD" ) == 0 ) 
	{
	
		printf("<<<<<<<<<<<<<<<<<<<<<<<<玩家ID[%d]要斷開連接>>>>>>>>>>>>>>>>>>>>>>>>\n",ID);
		//此處應發送此信息
		//此處加上檢測當前人數 以便判定那方勝
        ds_GameDebugServer->DS_CleanSocket( ID ) ;
		ds_GameDebugServer->DS_SendAll( ds_Msg,ID ) ;
  
	   
    	return 1 ;
	}
    
	else if( strcmp(ds_Temp,"#BN") == 0 )
	{
        //當前在新一局中有客戶機信息到
		//將要更新的位置信息以#NS處理
	  DSObject *p ;
	  p = ds_Head->ds_Next ;

	   while( p )
	   {
		   char ds_NewInfor[300];
	  
	       sprintf(ds_NewInfor,"#NS i%d*t%d*x%f*y%f*z%f*h%d*s%d*u%s*",
		              p->ds_ID,p->ds_NetType,p->ds_CurrentPos.x,
		              p->ds_CurrentPos.y,p->ds_CurrentPos.z,
		              p->ds_Health,p->ds_Score,p->ds_Username );

		      ds_GameDebugServer->DS_SendMessage( ID,ds_NewInfor );
		      ZeroMemory(ds_NewInfor,300);
		   p = p->ds_Next ;
	   }

	   p = NULL ;
	   free( p );
	   //  在向其發送可以開局了
       ds_GameDebugServer->DS_SendMessage( ID,"#OK i0*" );
		return 1 ;
	}
	//表示當前爲聊天信息
	//格式:<#CH i0*t0<1><2>*I2*m*你好 >
	//0爲羣發 1爲向同伴發送 2爲向某一人發送 i爲發送方
	
    else if( strcmp(ds_Temp,"#CH" ) == 0 )
	{
		printf("<<<<<<<<<<<<<<<<<<<<<<<<<聊天信息>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
		return 1 ;
	}

	return 0 ;
}
//-----------------------------------------------------------------------------------
//玩家結點的創建與維護
//type = 0 ;是對有權限可以對玩家的信息進行更新 
//type = 1 ;是對有權限可以對新玩家結點的創建
//type = 2 ; 是對自身結點的創建
//------------------------------------------------------------------------------------
DSObject* DSGameServer::DS_PlayerProcess( int ID,char *ds_Msg,int type )
{
 
 //記數器若找到某一結點則變爲1
   int count = 0 ;
	//將第一個結點
   DSObject *p = ds_Head ;

   while( p )
   {
	   if( p->ds_ID == ID )
	   {
		   count = 1 ;
		   break ;
	   }

	   p = p->ds_Next ;
   }
   
   //若找到該結點且可以更新
   
   if( count == 1 &&  ( type == 1 || type == 0 ) )  //type !=2 && type!=3
   {
	   int   ds_tempInt ;//用於保存整形數據
	   float ds_tempFloat ;//用於保存浮點形數據
	 
	   //若戰績信息正確則更新 否則不處理
	   if( ( ds_tempInt = DS_ReturnInt(ds_Msg,'s') ) != -10000 )
		   p->ds_Score = ds_tempInt ;

      //若生命值信息正確則更新 否則不處理
	   if( ( ds_tempInt = DS_ReturnInt(ds_Msg,'h') ) != -10000 )
		   p->ds_Health  = ds_tempInt ;

	   //若X位置正確則更新
	   if( ( ds_tempFloat = DS_ReturnFloat(ds_Msg,'x') ) != -10000.0f )
		   p->ds_CurrentPos.x = ds_tempFloat ;

	   //若y位置正確則更新
	   if( ( ds_tempFloat = DS_ReturnFloat(ds_Msg,'y') ) != -10000.0f )
		   p->ds_CurrentPos.y = ds_tempFloat ;

	    //若z位置正確則更新
	   if( ( ds_tempFloat = DS_ReturnFloat(ds_Msg,'z') ) != -10000.0f )
		   p->ds_CurrentPos.z = ds_tempFloat ;
	   
	   return p ;

   }

   else if( count == 0 && type == 1 )
   {
	   int   ds_tempInt ;//用於保存整形數據
	   float ds_tempFloat ;//用於保存浮點形數據

	   DSObject *ds_New = new DSObject( );

       ds_New->ds_ID = ID ;
	    //若戰績信息正確則更新 否則不處理
	   if( ( ds_tempInt = DS_ReturnInt(ds_Msg,'s') ) != -10000 )
		   ds_New->ds_Score = ds_tempInt ;

      //若生命值信息正確則更新 否則不處理
	   if( ( ds_tempInt = DS_ReturnInt(ds_Msg,'h') ) != -10000 )
		   ds_New->ds_Health  = ds_tempInt ;

	   //若生命值信息正確則更新 否則不處理
	   if( ( ds_tempInt = DS_ReturnInt(ds_Msg,'t') ) != -10000 )
		   ds_New->ds_NetType  =(NETOBJECT_TYPE) ds_tempInt ;

	   //若X位置正確則更新
	   if( ( ds_tempFloat = DS_ReturnFloat(ds_Msg,'x') ) != -10000.0f )
		   ds_New->ds_CurrentPos.x = ds_tempFloat ;

	   //若y位置正確則更新
	   if( ( ds_tempFloat = DS_ReturnFloat(ds_Msg,'y') ) != -10000.0f )
		   ds_New->ds_CurrentPos.y = ds_tempFloat ;

	    //若z位置正確則更新
	   if( ( ds_tempFloat = DS_ReturnFloat(ds_Msg,'z') ) != -10000.0f )
		   ds_New->ds_CurrentPos.z = ds_tempFloat ;

	   if( DS_ReturnString(ds_Msg,'u',ds_New->ds_Username ) == 0 )
		   strcpy(ds_New->ds_Username,"匿名玩家");

	   DS_AddObject( ds_New );
	  // ds_New = NULL ;
	   //free( ds_New );

	   return ds_New ;
   }

   //創建自身的結點
   else if( count == 0 && type ==2 )
   {
     DSObject *ds_Player = new DSObject();
     ds_Player->ds_ID = ID ;
	 ds_Player->ds_NetType = DSRED ;
	 ds_Player->ds_Type = DSPLAYER ;
	 
	 DS_AddObject(ds_Player);
	 return ds_Player ; 
   }

   //用於擊中處理
   else if( count == 1 && type == 3 )
   {
     int ds_tempInt ;

	 if( ( ds_tempInt = DS_ReturnInt(ds_Msg,'c') ) != -10000 )
		   p->ds_Health -= ds_tempInt*25 ;
	 
	 p = NULL ;
	 free( p );
	 return NULL ;
   }

    return NULL ;
}


文件七:DSMain.cpp

#include "stdafx.h"

void main()
{
    DSGameServer *ds_GameServer  = new DSGameServer();
	DS_PrintCurrentTime("遊戲服務器啓動");
	printf("*******************DreamShip製作*****************************************\n");
	printf("*******************版權所有**********************************************\n");
	ds_GameServer->DS_ProcessGameServer(); 
}


將以上文件保存成源代碼,再VC++6.0中進行編譯,注意編譯之前在Link中加上ws2_32.lib庫。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章