基于Windows Socket的安全通信

基于Windows Socket的安全通信_第1张图片

基于Windows Socket的安全通信_第2张图片

图示:红色为控件的ID, 蓝色为映射的变量名

 

下面要添加Socket通信功能了

先了解一下Socket的相关函数原型

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//加载套接字库
int PASCAL FAR WSAStartup( WORD wVersionRequired, LPWSADATA lpWSAData);
//释放套接字库资源
int PASCAL FAR WSACleanup( void );
//创建套接字
SOCKET PASCAL FAR socket ( int af, int type, int protocol);
//关闭套接字
int PASCAL FAR closesocket (SOCKET s);
//绑定一个IP地址和端口
int PASCAL FAR bind (SOCKET s, const struct sockaddr FAR *addr, int namelen);
//将套接字置为监听状态
int PASCAL FAR listen (SOCKET s, int backlog);
//接受客户端连接请求,并返回新创建的套接字
SOCKET PASCAL FAR accept (SOCKET s, struct sockaddr FAR *addr, int FAR *addrlen);
//尝试将本地套接字连接至服务器
int PASCAL FAR connect (SOCKET s, const struct sockaddr FAR *name, int namelen);
//发送数据
int PASCAL FAR send (SOCKET s, const char FAR * buf, int len, int flags);
//接收数据
int PASCAL FAR recv (SOCKET s, char FAR * buf, int len, int flags);

 

使用Socket的程序在使用Socket之前必须调用WSAStartup函数来绑定Socket库

在Constructor中添加如下代码

 

?
1
2
3
4
5
6
7
8
9
int error;
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD(2, 1); //加载2.1版本的Socket库
if (error = WSAStartup(wVersionRequested, &wsaData))
{
     AfxMessageBox( "Link Socket Library Failed!" );
     exit (0);
}

应用程序完成对Socket的使用后应当调用WSACleanup函数来释放Socket库占用的系统资源

 

在析构函数冲添加如下代码

 

?
1
WSACleanup();

 

 

Socket通信流程

实现安全通信,应采用面向连接的TCP/IP协议来保证连接的可靠性

面向连接的套接字的系统调用时序图

基于Windows Socket的安全通信_第3张图片

 

添加成员变量及初始化

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//服务器端:
  SOCKET Listener,toClient; //用于监听的套接字和连接至客户端的套接字(只是为了实现通信模型,所以不考虑多客户端)
   bool listening, connected; //指示监听和连接的状态
  AES aes; //加密/解密模块
  
CTestSocketServerDlg::CTestSocketServerDlg(CWnd* pParent):
     CDialog(CTestSocketServerDlg::IDD, pParent), 
     aes((unsigned char *) "0123456789abcdef" ),
     listening( false ),
     connected( false )
{
     //Constructor of Server
}
  
//客户端:
  SOCKET toServer; //连接至服务器端的套接字
   bool connected; //指示连接状态
  AES aes; //加密/解密模块
  
CTestSocketClientDlg::CTestSocketClientDlg(CWnd* pParent):
     CDialog(CTestSocketClientDlg::IDD, pParent), 
     aes((unsigned char *) "0123456789abcdef" ),
     connected( false )
{
     //Constructor of Client
}

 

为“Start/Stop”按钮注册单击事件处理服务器端初始化及关闭操作

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
void CTestSocketServerDlg::OnBtnStart() 
{
     if (connected || listening) //若正在监听或已连接则关闭服务器
     {
         connected = false ;
         listening = false ;
         closesocket(toClient);
         closesocket(Listener);
         m_chat += "Socket Server Stopped!\r\n" ;
         UpdateData( false );
         return ;
     }
  
     UpdateData( true );
     //创建监听Socket
     struct protoent *ppe;
     ppe = getprotobyname( "tcp" );
     if ((Listener = socket(PF_INET, SOCK_STREAM, ppe->p_proto)) == INVALID_SOCKET)
     {
         m_chat += "Initialize Socket Listener Failed!\r\n" ;
         UpdateData( false );
         return ;
     }
  
     //绑定IP及端口
     struct sockaddr_in saddr;
     saddr.sin_family = AF_INET;
     saddr.sin_port = htons(m_port);
     saddr.sin_addr.s_addr = htonl(INADDR_ANY);
     if (bind(Listener, ( struct sockaddr *)&saddr, sizeof (saddr)))
     {
         m_chat += "Bind to IPEndPoint Failed! (Port in use?)\r\n" ;
         UpdateData( false );
         return ;
     }
     //开始监听,队列长度1(不考虑多客户端)
     if (listen(Listener, 1))
     {
         m_chat += "Listen Failed!\r\n" ;
         UpdateData( false );
         return ;
     }
     m_chat += "Socket Server Started!\r\n" ;
     UpdateData( false );
  
     listening = true ;
     AfxBeginThread(Wait4Client, this ); //另起线程等待客户端连接
}

 

接收来自客户端的连接请求

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
UINT Wait4Client( LPVOID pParam)
{
     CTestSocketServerDlg * c = (CTestSocketServerDlg *) pParam;
     struct sockaddr_in caddr;
     int caddrlen = sizeof (caddr);
     c->toClient = accept(c->Listener, ( struct sockaddr *)&caddr, &caddrlen);
  
     if (c->toClient == INVALID_SOCKET) //异常处理
     {
         if (!c->listening) return 0; //服务器端主动关闭,则直接退出
         c->m_chat += "Connect Failed!\r\n" ;
         c->UpdateData( false );
         return -1;
    
     else 
     {
         c->connected = true ; //连接建立,另起线程用于接收信息
         AfxBeginThread(ReceiveMessage, c);
         c->m_chat += "Client: " ;
         c->m_chat += inet_ntoa(caddr.sin_addr);
         c->m_chat += " Connected!\r\n" ;
         c->m_ip = inet_ntoa(caddr.sin_addr);
         c->UpdateData( false );
     }
     return 0;
}

 

客户端只需要创建Socket并尝试与服务器连接

为“Connect/Disconnect”按钮注册单击事件

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
void CTestSocketClientDlg::OnBtnConnect() 
{
     if (connected) //如果已连接,则断开
     {
         connected = false ;
         closesocket(toServer);
         m_chat += "Disconnect to Server!\r\n" ;
         UpdateData( false );
         return ;
     }
  
     UpdateData( true );
     //创建Socket
     struct protoent *ppe;
     ppe = getprotobyname( "tcp" );
     if ((toServer = socket(PF_INET, SOCK_STREAM, ppe->p_proto)) == INVALID_SOCKET)
     {
         m_chat += "Initialize Socket Listener Failed!\r\n" ;
         UpdateData( false );
         return ;
     }
     //尝试连接服务器
     struct sockaddr_in saddr;
     saddr.sin_family = AF_INET;
     saddr.sin_port = htons(m_port);
     saddr.sin_addr.s_addr = inet_addr(m_ip);
     if (connect(toServer, ( struct sockaddr *)&saddr, sizeof (saddr)))
     {
         m_chat += "Connect Failed!\r\n" ;
         UpdateData( false );
         return ;
     }
     m_chat += "Server: " ;
     m_chat += inet_ntoa(saddr.sin_addr);
     m_chat += " Connected!\r\n" ;
     connected = true ;
     UpdateData( false );
     AfxBeginThread(ReceiveMessage, this ); //连接建立,另起线程用于接收信息
}

 

用于循环接收信息的线程

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
UINT ReceiveMessage( LPVOID pParam)
{
     CTestSocketServerDlg * c = (CTestSocketServerDlg *) pParam;
     char buffer[1024];
     int error; //记录recv函数返回值,即接收的字节数,也作异常代码
     while (error = recv(c->toClient, buffer, 1024, 0))
     {
         if (error == 0 || error == SOCKET_ERROR) break ;
         c->PrintData( "Received Data" , (unsigned char *)buffer, error);
         c->aes.InvCipher(( void *)buffer, error); //解密,恢复明文
         c->PrintData( "Unencrypted Data" , (unsigned char *)buffer, error);
         c->m_chat += "Client:" ;
         c->m_chat += buffer;
         c->m_chat += "\r\n" ;
         c->UpdateData( false );
     }
     c->m_ip = "Not Connected..." ;
     c->UpdateData( false );
     if (!c->connected) return 0; //服务器端主动关闭,直接返回
     closesocket(c->toClient);
     c->connected = false ;
     c->m_chat += "Client Disconnected...\r\n" ;
     c->UpdateData( false );
     AfxBeginThread(Wait4Client, c);
     return 0;
}

 

为“Send”按钮注册单击事件,处理数据的加密发送

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void CTestSocketServerDlg::OnBtnSend() 
{
     if (!connected) return ;
     UpdateData( true );
     if (m_message == "" ) return ;
      
     int len = m_message.GetLength()+1 >= 1024 ? 1024 : m_message.GetLength()+1;
     len = len%16 ? len+16-len%16 : len;
     char buffer[1024];
     strcpy (buffer,m_message.GetBuffer(0)); //将message拷贝至buffer数组中
     m_message.ReleaseBuffer();
     PrintData( "Input Data" , (unsigned char *)buffer, len);
     aes.Cipher(( void *)buffer); //对数据进行加密
     if (send(toClient, buffer, len, 0) == SOCKET_ERROR) //发送密文
     {
         m_chat += "Send Failed!(Socket Exception?)\r\n" ;
         UpdateData( false );
         return ;
     }
     PrintData( "Encrypted Data" , (unsigned char *)buffer, len);
     m_chat += "Server:" + m_message + "\r\n" ;
     m_message = "" ;
     UpdateData( false );
}

 

发送和接收的时候都用到了一个函数PrintData,用于将明文或密文以16进制输出以便作演示

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
void CTestSocketServerDlg::PrintData( char * title, unsigned char * buffer, int length)
{
     int i;
     CString temp( "" );
     m_chat += "(" ;
     m_chat += title;
     m_chat += ":" ;
     for (i=0; i<length; i++)
     {
         temp.Format( "%s%X " ,*(buffer+i)>15? "" : "0" ,*(buffer+i));
         m_chat += temp;
     }
     m_chat += ")\r\n" ;
}

 

贴出的代码都是服务器端的,客户端代码类似,最大区别就是类名不同,不做赘述

 

运行效果

基于Windows Socket的安全通信_第4张图片

你可能感兴趣的:(基于Windows Socket的安全通信)