客户端实现步骤如下。
(1) 打开VC6,单击【File】|【New】命令,弹出【New】对话框。
(2)在【Projects】选项卡中,选择【MFC AppWizard(exe)】选项,创建一个MFC应用程序。在【Projectname】文本框中,输入项目名称“client”,如图10.18所示。
图10.18 新建VC工程
(3)单击“OK”,在创建MFC应用程序向导对话框选择应用程序类型,这里选择“Dilag based”如图10.19所示。然后单击“Finish”完成。
(4)在IDD_CLIENT_DIALOG对话框窗体添加编辑框(Edit Box)控件。
(5) 按 “Ctrl+W” 键或单击【View】|【ClassWizard】命令,启动“MFC ClassWizard”,对话框,选择【Member Variables】选项。如图所示。
(6)选中“IDC_EDIT1”控件,单击“Add Variable…”按钮。添加CString变量m_str,如图所示。
(7)在IDD_ CLIENT _DIALOG对话框窗体添加列表框(List Box)控件。
(8) 按 “Ctrl+W” 键或单击【View】|【ClassWizard】命令,启动“MFC ClassWizard”,对话框,选择【Member Variables】选项。如图所示。
(9)选中“IDC_LIST”控件,单击“Add Variable…”按钮。添加控件变量m_list,如图所示。
(10)在IDD_ CLIENT _DIALOG对话框窗体,添加三个命令按钮(Button)控件。全部控件和布局如图所示。
(11)添加自定义结构体,用户保存SSL链接句柄。在serverDlg.cpp添加如下代码:
typedef struct descriptor{
int fd;
SSL *ssl;
}descriptor_t;
SSL_CTX* ctx;
descriptor_t client;
(12)BOOL CClientDlg::OnInitDialog()函数中添加如下代码初始化客户端环境。
// TODO: Add extra initialization here
//初始化OpenSSL环境
client.fd=0;
client.ssl=NULL;
SSLeay_add_ssl_algorithms();
SSL_load_error_strings();
ctx = SSL_CTX_new (SSLv23_client_method());//设置SSL协议版本
return TRUE; // return TRUE unless you set the focus to a control
(13)BOOL CClientDlg:: OnConnect ()函数即“连接”,按钮中添加如下代码连接服务器。
//链接服务器
void CClientDlg::OnConnect()
{
unsigned long idThread;
DWORD dwIP=0;
if(client.fd==0)//判断是否已经连接
{
CIP ipaddr;//让用户输入客户端IP
if(ipaddr.DoModal() == IDOK)
{
dwIP=inet_addr(ipaddr.IP.GetBuffer(0));
}
//创建线程,连接服务器,并接收服务器端消息
CreateThread(NULL,0,
(LPTHREAD_START_ROUTINE)ClientThreadProc,
(void *)dwIP, 0 ,&idThread);
}
else
{
m_list.InsertString(0,"已经连接!");
}
}
(14) ClientThreadProc函数连接服务器端并接收服务器消息的线程处理函数,其代码如下
//连接服务器端并接收服务器消息的线程函数
void ClientThreadProc(void* void_parm)
{
WSADATA wsaData;
int err;
int sd;
struct sockaddr_in sa;
SSL* ssl;
X509* server_cert;
char* str;
char buffer [8912];
int maxFd;
fd_set writeFds, readFds, excFds;
DWORD dwIP;
dwIP = (DWORD)void_parm;
//初始化windows socket
if (WSAStartup(MAKEWORD(1, 1), &wsaData))
{
return;
}
//新建socket
sd = socket (AF_INET, SOCK_STREAM, 0);
memset (&sa, '/0', sizeof(sa));
//设置服务端IP地址、和端口
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = dwIP;
sa.sin_port = htons(8443);
//连接服务器
err = connect(sd, (struct sockaddr*) &sa,
sizeof(sa));
if(err < 0)
{
char *msg = (char *)malloc(128);
strcpy(msg,"连接服务器失败!");
SendMessage(AfxGetMainWnd()->GetSafeHwnd(),WM_CLIENT_MSG,sd,(long )msg);
return;
}
//新建SSL句柄
ssl = SSL_new (ctx);
//设置socket句柄到SSL句柄
SSL_set_fd (ssl, sd);
//SSL连接
err = SSL_connect (ssl);
if(err != 1)
{
char *msg = (char *)malloc(128);
strcpy(msg,"连接服务器失败!");
SendMessage(AfxGetMainWnd()->GetSafeHwnd(),WM_CLIENT_MSG,sd,(long )msg);
return;
}
server_cert = SSL_get_peer_certificate (ssl);
str = X509_NAME_oneline (X509_get_subject_name (server_cert),0,0);
OPENSSL_free (str);
str = X509_NAME_oneline (X509_get_issuer_name (server_cert),0,0);
OPENSSL_free (str);
X509_free (server_cert);
printf ("SSL connection using %s/n", SSL_get_cipher (ssl));
maxFd = sd;
client.fd=sd;
client.ssl=ssl;
char *msg = (char *)malloc(128);
strcpy(msg,"连接服务器成功!");
SendMessage(AfxGetMainWnd()->GetSafeHwnd(),WM_CLIENT_MSG,sd,(long )msg);
for(;;)
{
FD_ZERO(&writeFds);
FD_ZERO(&readFds);
FD_ZERO(&excFds);
FD_SET(sd, &readFds);
//select 异步等待服务器
int nfd = select(maxFd + 1, &readFds, &writeFds, &excFds, NULL);
if(nfd <= 0)
{
if(errno == EINTR) /* interrupted system call */
continue;
return;
}
if(FD_ISSET(sd, &readFds))
{
//接收服务器消息
int len = SSL_read(ssl,buffer,sizeof(buffer));
if(len <=0)
{
char *msg = (char *)malloc(128);
strcpy(msg,"服务器退出!");
SendMessage(AfxGetMainWnd()->GetSafeHwnd(),WM_CLIENT_MSG,sd,(long )msg);
return;
}
buffer[len]='/0';
char *msg = (char *)malloc(len +1);
strcpy(msg,buffer);
//通过WM_CLIENT_MSG消息,发送到主窗体
SendMessage(AfxGetMainWnd()->GetSafeHwnd(),WM_CLIENT_MSG,sd,(long )msg);
}
}
return;
}
(15)void CClientDlg:: OnSend ()函数即“Send”按钮中添加如下代码,向服务器端发送消息。
//发送消息到服务器
void CClientDlg::OnSend()
{
UpdateData();
if(client.fd !=0)
{
//发送消息
SSL_write(client.ssl,m_str.GetBuffer(0),m_str.GetLength());
m_str.ReleaseBuffer();
}
else
{
return;
}
//插入发送的消息的列表框
m_list.InsertString(0,m_str);
m_str.Empty();
UpdateData(FALSE);
((CEdit *)GetDlgItem(IDC_EDIT1))->SetActiveWindow();
}
(16)void CClientDlg:: OnDisconnect ()函数即“取消”按钮中添加如下代码,断开与服务器的链接。
//断开链接
void CClientDlg::OnDisconnect()
{
if(client.fd !=0)
{
closesocket(client.fd);
SSL_shutdown(client.ssl);
SSL_free(client.ssl);
client.ssl=NULL;
client.fd=0;
m_list.InsertString(0,"已断开和服务器的链接!");
}
}
(17)添加处理WM_CLIENT_MSG消息的函数OnClientMsg ()。其代码如下:
//处理WM_CLIENT_MSG消息
LRESULT CClientDlg::OnClientMsg(WPARAM wParam,LPARAM lParam)