基于Windows Socket的安全通信(C++实现,附源码)

先了解一下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协议来保证连接的可靠性

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

 

添加成员变量及初始化

 

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" ;
}

 


代码地址:http://download.csdn.net/detail/kaitiren/7604097

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Kaitiren

你的鼓励是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值