10.3.1 服务端

  服务端实现步骤如下。

1 打开VC6,单击【File|New】命令,弹出【New】对话框。

2)在【Projects】选项卡中,选择【MFC AppWizard(exe)】选项,创建一个MFC应用程序。在【Projectname】文本框中,输入项目名称“server”,如图所示。

3)单击“OK”,在创建MFC应用程序向导对话框选择应用程序类型,这里选择“Dilag based”如图所示。然后单击“Finish”完成。

                                                     

(4)IDD_SERVER_DIALOG对话框窗体添加编辑框(Edit Box)控件。

(5) Ctrl+W 键或单击【View|ClassWizard】命令,启动“MFC ClassWizard”,对话框,选择【Member Variables】选项。如图所示。

6)选中“IDC_EDIT1”控件,单击“Add Variable…”按钮。添加CString变量m_str,如图所示。

(7)IDD_SERVER_DIALOG对话框窗体添加列表框(List Box)控件。

(8) Ctrl+W 键或单击【View|ClassWizard】命令,启动“MFC ClassWizard”,对话框,选择【Member Variables】选项。如图所示。

9)选中“IDC_LIST”控件,单击“Add Variable…”按钮。添加控件变量m_list,如图所示。

10)在IDD_SERVER_DIALOG对话框窗体,添加两个命令按钮(Button)控件。全部控件和布局如图所示。

11)添加自定义结构体,用户保存客户端SSL链接句柄。在serverDlg.h添加如下代码

// CServerDlg dialog

typedef struct descriptor{

         int     fd;

         SSL  *ssl;

}descriptor_t;

12)添加服务器环境的宏定义以及保存客户端链接信息的全局变量。在serverDlg.cpp文件添加如下代码:

#define CERTF  "cert.cer"                           //服务器证书

#define KEYF   "key.pem"                          //服务器私钥

#define ROOTCERTF "root.cer"                  //根证书

#define MAX_CLIENT 100                                     //允许链接的最大客户端数

#define WM_CLIENT_MSG WM_USER+102    //自定义的消息

SSL_CTX* ctx;                                                 //SSL上下文句柄

descriptor_t Clients[MAX_CLIENT];  //保存客户端链接句柄的数组

(13BOOL CServerDlg::OnInitDialog()函数中添加如下代码初始化服务器环境。

         // TODO: Add extra initialization here

         //初始化OpenSSL环境

         SSL_load_error_strings();

         SSLeay_add_ssl_algorithms();

         SSL_METHOD *meth;

         meth = SSLv23_server_method();    //使用SSL V2V3协议

         ctx = SSL_CTX_new (meth);               //初始化SSL上下文

         if (!ctx)

         {

                   return FALSE;

         }

         //设置服务器证书

         if (SSL_CTX_use_certificate_file(ctx, CERTF, SSL_FILETYPE_PEM) <= 0)

         {

                   return FALSE;

         }

         //设置服务器私钥

         if (SSL_CTX_use_PrivateKey_file(ctx, KEYF, SSL_FILETYPE_PEM) <= 0)

         {

                   return FALSE;

         }

         //检查服务器私钥和证书是否匹配

         if (!SSL_CTX_check_private_key(ctx))

         {

                   return FALSE;

         }

         //初始化客户端链接结构体数组

         for(int i=0;i<MAX_CLIENT;i++)

         {

                   Clients[i].fd=0;

                   Clients[i].ssl=NULL;

         }

         return TRUE;  // return TRUE  unless you set the focus to a control

(14void CServerDlg::OnStart()函数即“启动”按钮中添加如下代码,创建线程,启动服务。

//启动服务

void CServerDlg::OnStart()

{

         unsigned long           idThread;

         int port=8443;

         //创建线程,启动服务

         CreateThread(NULL,0,

                   (LPTHREAD_START_ROUTINE)StartServer,

                   (void *)port, 0 ,&idThread);

}

(15StartServer函数为启动线程处理函数,其代码如下:

//启动服务线程函数

void StartServer(void* void_parm)

{

        

         WSADATA              wsaData;

         int err;

         int listen_sd;

        

         unsigned long           idThread;

         struct sockaddr_in sa_serv;

         HANDLE            hd;

         int port = (int) void_parm;

         //初始化windows socket

         if (WSAStartup(MAKEWORD(1, 1), &wsaData))

         {

                   char *msg = (char *)malloc(128);

                   strcpy(msg,"初始化Socket失败!");

                   SendMessage(AfxGetMainWnd()->GetSafeHwnd(),WM_CLIENT_MSG,0,(long )msg);

                   return;

         }

         //新建socket句柄

         listen_sd = socket (AF_INET, SOCK_STREAM, 0);

         //初始化sockaddr_in结构体,设置TCP协议和端口

         memset (&sa_serv, '/0', sizeof(sa_serv));

         sa_serv.sin_family      = AF_INET;

         sa_serv.sin_addr.s_addr = INADDR_ANY;

         sa_serv.sin_port        = htons (port);        

         //绑定端口

         err = bind(listen_sd, (struct sockaddr*) &sa_serv,

                   sizeof (sa_serv));         

         if(err < 0)

         {

                   char *msg = (char *)malloc(128);

                   strcpy(msg,"绑定Socket失败!");

                   SendMessage(AfxGetMainWnd()->GetSafeHwnd(),WM_CLIENT_MSG,listen_sd,(long )msg);

                   return;

         }

         //侦听,tcp连接

         err = listen (listen_sd, 5); 

         char *msg = (char *)malloc(128);

         strcpy(msg,"启动服务成功!");

         SendMessage(AfxGetMainWnd()->GetSafeHwnd(),WM_CLIENT_MSG,listen_sd,(long )msg);

         for(;;)

         {

                   //启动接收连接线程

                   hd=CreateThread(NULL,0,

                            (LPTHREAD_START_ROUTINE)AcceptThreadProc,

                            (void *)listen_sd, 0 ,&idThread);

                   //等待该线程执行完毕,开启下一个线程,继续等待接收连接。

                   WaitForSingleObject(hd, INFINITE);

          }

        

}

(16AcceptThreadProc函数为接收链接线程处理函数,其代码如下:

//接收连接线程函数

void AcceptThreadProc( void* void_parm )

{

         int sockFd = (int) void_parm;

         HANDLE hd;

         unsigned long idThread;

         int clientFd;

         //接收连接

         clientFd = accept(sockFd, NULL, NULL);

         if(clientFd < 0){

                   if(errno == EINTR)    /* interrupted system call */

                            return;

                   ExitThread(0);

         }

         //接收客户端连接后,开启线程处理客户端事务即接收客户端消息

         hd=CreateThread(NULL,0,

                   (LPTHREAD_START_ROUTINE)client,

                   (void *)clientFd, 0 ,&idThread);

}

(17client函数为接收客户端消息线程处理函数,其代码如下:

//处理客户端事务线程函数,接收客户端消息

void  client( void* void_parmint )

{

         int clientFd = (int) void_parmint;

         int     maxFd;

         int len;

         int flag=0;

         fd_set       writeFds, readFds, excFds;

         char buffer[8192];

         descriptor_t clientDesc;

         clientDesc.fd = clientFd;

         clientDesc.ssl = NULL;

         SSL           *ssl;

         //新建SSL连接句柄

         if((ssl = SSL_new(ctx)) == NULL)

         {

                   return;

         }

         //设置SSL连接Socket句柄

         SSL_set_fd(ssl, clientFd);

         //接收SSL连接

         if(SSL_accept(ssl) <= 0)

         {

                   return;

         }

         clientDesc.ssl = ssl;

         //把客户端链接句柄保存到全局变量中。

         for(int i=0;i<MAX_CLIENT;i++)

         {

                   if(Clients[i].fd==0)

                   {

                            Clients[i].fd=clientFd;

                            Clients[i].ssl=ssl;

                            flag = 1;

                            break;

                   }

         }

         if(!flag)

         {

                   //已经达到链接最大限制

                   SSL_write(ssl,"服务器已满!",strlen("服务器已满!"));

                   SSL_shutdown(ssl);

                   SSL_free(ssl);

                   return;

         }

         maxFd = clientFd;

         for(;;)

         {

                   FD_ZERO(&writeFds);

                   FD_ZERO(&readFds);

                   FD_ZERO(&excFds);

                   FD_SET(clientFd, &readFds);

                   //异步的方式等待客户端数据

                   int nfd = select(maxFd + 1, &readFds, &writeFds, &excFds, NULL);

                   if(nfd <= 0)

                   {

                            if(errno == EINTR)    /* interrupted system call */

                                     continue;

                            return;

                   }

                   if(FD_ISSET(clientFd, &readFds))//有客户端数据需要读取

                   {

                            //接收客户端数据

                            len = SSL_read(clientDesc.ssl,buffer,sizeof(buffer));

                            if(len <=0)

                            {

                                     char *msg = (char *)malloc(128);

                                     strcpy(msg,"客户端退出!");

                                     SendMessage(AfxGetMainWnd()->GetSafeHwnd(),WM_CLIENT_MSG,clientFd,(long )msg);

                                     return;

                            }

                            buffer[len]='/0';

                            char *msg = (char *)malloc(len +1);

                            strcpy(msg,buffer);

                            //发送WM_CLIENT_MSG消息到主窗体,把接收到的消息显示到列表框

                            SendMessage(AfxGetMainWnd()->GetSafeHwnd(),WM_CLIENT_MSG,clientFd,(long )msg);

                   }

         }

         return;

}

(18)添加处理WM_CLIENT_MSG消息的函数OnClientMsg()。其代码如下:

//处理线程WM_CLIENT_MSG消息,显示到列表框中

LRESULT CServerDlg::OnClientMsg(WPARAM wParam,LPARAM lParam)

{

         CString msg;

         msg.Format("Socket:%d  %s",wParam,(char *)lParam);

         m_list.InsertString(0,msg);

         free((void*)lParam);

         return 0L;

}

(19void CServerDlg:: OnSend ()函数即“Send”按钮中添加如下代码,向所有已经链接的客户端发送消息。

//向客户端发送消息

void CServerDlg::OnSend()

{

         UpdateData();

         //遍续所有已经链接客户端,发送消息

         for(int i=0;i<MAX_CLIENT;i++)

         {

                   if(Clients[i].fd != 0)

                   {

                            //发送消息

                            SSL_write(Clients[i].ssl,m_str.GetBuffer(0),m_str.GetLength());

                            m_str.ReleaseBuffer();

                   }

         }

         m_list.InsertString(0,m_str);

         m_str.Empty();

         UpdateData(FALSE);

         ((CEdit *)GetDlgItem(IDC_EDIT1))->SetActiveWindow();

}

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