服务端主要包括以下主要函数过程:初始化SSL服务器证书函数、启动服务线程函数、接收客户端消息线程处理函数、发送消息到客户端函数。
1.初始化SSL服务器证书
此过程的代码添加在OnInitDialog方法中。
处理过程如下:
(1)初始化OpenSSL算法库
SSL_load_error_strings();
SSLeay_add_ssl_algorithms();
(2)初始化SSL服务器端协议算法
meth = SSLv23_server_method(); //使用SSL V2或V3协议
ctx = SSL_CTX_new (meth); //初始化SSL上下文
(3)设置服务器证书
SSL_CTX_use_certificate_file(ctx, CERTF, SSL_FILETYPE_PEM);
(4)设置服务器私钥
SSL_CTX_use_PrivateKey_file(ctx, KEYF, SSL_FILETYPE_PEM);
(5)检查服务器私钥和证书是否匹配
if (!SSL_CTX_check_private_key(ctx))
{
return FALSE;
}
(6)初始化保存客户端链接的全局变量数组
for(int i=0;i<MAX_CLIENT;i++)
{
Clients[i].fd=0;
Clients[i].ssl=NULL;
}
处理流程如图所示:
2.启动服务线程处理函数
启动服务的线程函数void StartServer(void* void_parm),通过CreateThread函数调用。StartServer函数处理过程如下:
(1)初始化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;
}
(2)初始化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);
(3)绑定端口
err = bind(listen_sd, (struct sockaddr*) &sa_serv,sizeof (sa_serv));
(4)侦听TCP链接
err = listen (listen_sd, 5);
(5)启动接收连接线程
for(;;)
{
//启动接收连接线程
hd=CreateThread(NULL,0,
(LPTHREAD_START_ROUTINE)AcceptThreadProc,
(void *)listen_sd, 0 ,&idThread);
//等待该线程执行完毕,开启下一个线程,继续等待接收连接。
WaitForSingleObject(hd, INFINITE);
}
其处理过程流程图如图所示:
3.接收客户端消息线程处理函数
接收客户端消息线程处理函数void client( void* void_parmint )。处理过程如下:
(1)新建SSL链接句柄
if((ssl = SSL_new(ctx)) == NULL)
{
return;
}
(2)设置SSL链接Socket句柄
SSL_set_fd(ssl, clientFd);
(3)接收SSL链接,并把客户端链接句柄保存到全局变量中。
if(SSL_accept(ssl) <= 0)
{
return;
}
for(int i=0;i<MAX_CLIENT;i++)
{
if(Clients[i].fd==0)
{
Clients[i].fd=clientFd;
Clients[i].ssl=ssl;
flag = 1;
break;
}
}
(4)异步select方式等待客户端数据
for(;;)
{
FD_ZERO(&writeFds);
FD_ZERO(&readFds);
FD_ZERO(&excFds);
FD_SET(clientFd, &readFds);
//异步的方式等待客户端数据
int nfd = select(maxFd + 1, &readFds, &writeFds, &excFds, NULL);
。。。
}
(5)如果有数据,则读取客户端数据
if(FD_ISSET(clientFd, &readFds))//有客户端数据需要读取
{
//接收客户端数据
len = SSL_read(clientDesc.ssl,buffer,sizeof(buffer));
。。。
}
(6)把客户端消息显示在窗口列表库中
SendMessage(AfxGetMainWnd()->GetSafeHwnd(),WM_CLIENT_MSG,clientFd,(long )msg);
其处理过程流程图如图所示:
4.发送消息到客户端函数
发送到客户端只要调用SSL_write函数即可。在void CServerDlg::OnSend()函数中向已经链接的所有客户端发送消息。
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();
}
}