首页 > 代码库 > 基于Windows Socket的安全通信(C++实现,附源码)

基于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((unsignedchar *)"0123456789abcdef"),
    listening(false),
    connected(false)
{
    //Constructor of Server
}
 
//客户端:
  SOCKET toServer;//连接至服务器端的套接字
  bool connected;//指示连接状态
  AES aes;//加密/解密模块
 
CTestSocketClientDlg::CTestSocketClientDlg(CWnd* pParent):
    CDialog(CTestSocketClientDlg::IDD, pParent),
    aes((unsignedchar *)"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