标签:style blog http color 使用 width
先了解一下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
基于Windows Socket的安全通信(C++实现,附源码),布布扣,bubuko.com
基于Windows Socket的安全通信(C++实现,附源码)
标签:style blog http color 使用 width
原文地址:http://blog.csdn.net/kaitiren/article/details/37518317