现在的位置: 首页 > 综合 > 正文

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

2019年08月26日 ⁄ 综合 ⁄ 共 7040字 ⁄ 字号 评论关闭

先了解一下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

抱歉!评论已关闭.