白桦的天空

第一次的心动,永远的心痛!
  首页  :: 新随笔  :: 联系 :: 管理
用vc++实现服务器端

  在vc++中,有一个CSocket类,可以用来实现服务器端的功能。事实上,对于每一个客户端,服务器端必须有一个Socket对象与之相连,即是说如果有n个客户请求连接,则需要有n个socket对象,有n+1个客户请求连接,就必须有n+1个socket对象。所以,我们需要动态改变socket对象的个数,我们的设计思路如下:

①从CSocket类派生出一个CListenSocket类,并创建一个CListenSocket类的对象,专门用来监听客户端的请求,再从CSocket类派生出一个CClientSocket类,专门用来和客户端保持连接。

②一旦CListenSocket类的对象接收到一个请求,马上创建出一个CClientSocket类的对象,并让这个对象与客户端保持连接,而且我们要创建一个链表,每当增加一个CClientSocket类的对象时,就将它加入到链表中。

③任何一个CClientSocket类的对象的对象收到一个从flash传送过来的信息,马上让所有的CClientSocket类的对象将这个信息传送到客户端,实现信息的转发。

  下面,请大家跟着我一步一步来实现:

1.通过AppWizard生成一个基于对话框的应用程序GameSvr,在向导的第二步选择Windows Sockets的支持,其余步骤均采用默认值。

2.给CGameSvrDlg类添加一个变量int i;用来限制连接数,如下:

protected:  int i=0;

3.通过ClassWizard生成基于CSocket的新类CListenSocket,用来监听请求。

4.给CListenSocket类声明一个主对话框CGameSvrDlg指针类型的私有成员变量,如下:

protected:

  CGameSvrDlg *m_pDlg;

5.给CListenSocket类重载一个构造函数:

CListenSocket::CListenSocket(CGameSvrDlg* pDlg)
{
  m_pDlg=pDlg;
}

  并且在CListenSocket类的cpp文件开始处增加如下语句:

#include "CGameSvrDlg.h"

6.通过ClassWizard响应CListenSocket类的OnAccept函数,表示有客户端连接,其代码如下:

void CListenSocket::OnAccept(int nErrorCode)
{
  CSocket::OnAccept(nErrorCode);
  //主对话框处理连接信息
  if(m_pDlg){
    m_pDlg->ProcessPendingAccept();
  }
}

7.通过ClassWizard生成CClientSocket类用来与客户端通信;

8.给CClientSocket类声明一个主对话框CGameSvrDlg指针类型的私有成员变量,如下:

protected:  CGameSvrDlg *m_pDlg;

9.给CClientSocket类重载一个构造函数:

CClientSocket::CClientSocket(CGameSvrDlg* pDlg)
{
  m_pDlg=pDlg;
}

  并且在CClientSocket类的cpp文件开始处增加如下语句:

#include "CGameSvrDlg.h"

10.通过ClassWizard响应CClientSocket类的OnReceive()函数,表示有数据来了,其代码如下:

void CClientSocket::OnReceive(int nErrorCode)
{
  CSocket::OnReceive(nErrorCode);
  //主对话框处理连接信息
  if(m_pDlg){
    m_pDlg->ProcessPendingRead(this);
}

11.在主对话框CGameSvrDlg的头文件中增加两个私有成员变量,如下:

CListenSocket* m_pSocket;
CPtrList m_connectionList;
//客户端Socket链表并且在CGameSvrDlg的头文件开始处增加以下代码:
class CListenSocket;
class CClientSocket;

12.给主对话框CGameSvrDlg增加处理客户端连接信息的私有成员函数ProcessPendingAccept(),其定义如下:

void CGameSvrDlg::ProcessPendingAccept()
{
  if(i<8){
    char s[11];
    sprintf(s,"<id>%d</id>",i);
    m_pSocket->send(s,strlen(s),0);
    CClientSocket *pSocket=new CClientSocket(this);
    if(m_pSocket->Accept(*pSocket)){
      //将该Socket保存在链表中
      m_connectionList.AddTail(pSocket);
    }else{
      delete pSocket;
    }
  }else{
    char s[16];
    s="<err>full</err>";
    m_pSocket->send(s,strlen(s),0);
  }
  i++;
}

13.给主对话框CGameSvrDlg增加更新所有客户端的私有成员函数UpdateClients,其定义如下:

void CGameSvrDlg::UpdateClients(char *buffer,int nBufferSize)
{
  for(POSITION pos=m_connectionList.GetHeadPosition();pos!=NULL;)
  {
    CClientSocket *pSocket=(CClientSocket*)m_connectionList.GetNext(pos);
    
    if(buffer!=NULL)pSocket->send(buffer,nBufferSize,0);
  }
}14.给主对话框CGameSvrDlg增加接收数据的私有成员函数ProcessPendingRead,其定义如下:
 
void CGameSvrDlg::ProcessPendingRead(CClientSocket *pSocket)
{
  char buffer[BUFFER_SIZE];
  int nReceived=pSocket->Receive(buffer,BUFFER_SIZE,0);
  buffer[nReceived]=0;
  //将数据发给每一个用户
  UpdateClients(buffer,nReceived);
}


  并且在对话框CGameSvrDlg类的头文件开始处定义缓冲区的大小,如下:

#define BUFFER_SIZE 100

15.在主对话框CGameSvrDlg的OnInitialUpdate函数中添加如下代码:

BOOL CGameSvrDlg::OnInitDialog(){
  CDialog::OnInitDialog();
  //其他代码
  //……
  m_pSocket=new CListenSocket(this);
  if(m_pSocket->Creat(1024)){
    if(m_pSocket->Listen())
    return TURE;
    }else
      return FALSE;
  return TURE;
}

  OK,我们做完全部的工作了,也许你要问,我们的这个游戏能够干什么呢?不错,这个游戏的功能实在太简单,也许它能做的唯一的事情就是……赛跑!但是我写这篇教程的目的是为了抛砖引玉,希望大家能够写出更多更精彩的网络游戏来……