先了解一下Socket的相关函数原型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
//加载套接字库 int PASCAL WORD wVersionRequired, //释放套接字库资源 int PASCAL void ); //创建套接字 SOCKET int af, int type, int protocol); //关闭套接字 int PASCAL //绑定一个IP地址和端口 int PASCAL const struct sockaddr int namelen); //将套接字置为监听状态 int PASCAL int backlog); //接受客户端连接请求,并返回新创建的套接字 SOCKET struct sockaddr int FAR //尝试将本地套接字连接至服务器 int PASCAL const struct sockaddr int namelen); //发送数据 int PASCAL const char FAR int len, int flags); //接收数据 int PASCAL char FAR int len, int flags); |
使用Socket的程序在使用Socket之前必须调用WSAStartup函数来绑定Socket库
在Constructor中添加如下代码
1
2
3
4
5
6
7
8
9
|
int error; WORD wVersionRequested; WSADATA wVersionRequested //加载2.1版本的Socket库 if (error { AfxMessageBox( "Link ); 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 //用于监听的套接字和连接至客户端的套接字(只是为了实现通信模型,所以不考虑多客户端) bool listening, //指示监听和连接的状态 AES //加密/解密模块 CTestSocketServerDlg::CTestSocketServerDlg(CWnd* CDialog(CTestSocketServerDlg::IDD, aes((unsigned char *) "0123456789abcdef" ), listening( false ), connected( false ) { //Constructor } //客户端: SOCKET //连接至服务器端的套接字 bool connected; //指示连接状态 AES //加密/解密模块 CTestSocketClientDlg::CTestSocketClientDlg(CWnd* CDialog(CTestSocketClientDlg::IDD, aes((unsigned char *) "0123456789abcdef" ), connected( false ) { //Constructor } |
为“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 //若正在监听或已连接则关闭服务器 { connected false ; listening false ; closesocket(toClient); closesocket(Listener); m_chat "Socket ; UpdateData( false ); return ; } UpdateData( true ); //创建监听Socket struct protoent ppe "tcp" ); if ((Listener { m_chat "Initialize ; UpdateData( false ); return ; } //绑定IP及端口 struct sockaddr_in saddr.sin_family saddr.sin_port saddr.sin_addr.s_addr if (bind(Listener, struct sockaddr sizeof (saddr))) { m_chat "Bind ; UpdateData( false ); return ; } //开始监听,队列长度1(不考虑多客户端) if (listen(Listener, { m_chat "Listen ; UpdateData( false ); return ; } m_chat "Socket ; 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 struct sockaddr_in int caddrlen sizeof (caddr); c->toClient struct sockaddr if (c->toClient //异常处理 { if (!c->listening) return 0; //服务器端主动关闭,则直接退出 c->m_chat "Connect ; c->UpdateData( false ); return -1; } else { c->connected true ; //连接建立,另起线程用于接收信息 AfxBeginThread(ReceiveMessage, c->m_chat "Client: ; c->m_chat c->m_chat " ; c->m_ip 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 ; UpdateData( false ); return ; } UpdateData( true ); //创建Socket struct protoent ppe "tcp" ); if ((toServer { m_chat "Initialize ; UpdateData( false ); return ; } //尝试连接服务器 struct sockaddr_in saddr.sin_family saddr.sin_port saddr.sin_addr.s_addr if (connect(toServer, struct sockaddr sizeof (saddr))) { m_chat "Connect ; UpdateData( false ); return ; } m_chat "Server: ; m_chat m_chat " ; 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 char buffer[1024]; int error; //记录recv函数返回值,即接收的字节数,也作异常代码 while (error { if (error break ; c->PrintData( "Received , char *)buffer, c->aes.InvCipher(( void *)buffer, //解密,恢复明文 c->PrintData( "Unencrypted , char *)buffer, c->m_chat "Client:" ; c->m_chat c->m_chat "\r\n" ; c->UpdateData( false ); } c->m_ip "Not ; c->UpdateData( false ); if (!c->connected) return 0; //服务器端主动关闭,直接返回 closesocket(c->toClient); c->connected false ; c->m_chat "Client ; c->UpdateData( false ); AfxBeginThread(Wait4Client, 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 len char buffer[1024]; strcpy (buffer,m_message.GetBuffer(0)); //将message拷贝至buffer数组中 m_message.ReleaseBuffer(); PrintData( "Input , char *)buffer, aes.Cipher(( void *)buffer); //对数据进行加密 if (send(toClient, //发送密文 { m_chat "Send ; UpdateData( false ); return ; } PrintData( "Encrypted , char *)buffer, m_chat "Server:" + "\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 * char * int length) { int i; CString "" ); m_chat "(" ; m_chat m_chat ":" ; for (i=0; { temp.Format( "%s%X ,*(buffer+i)>15? "" : "0" ,*(buffer+i)); m_chat } m_chat ")\r\n" ; } |
代码地址:http://download.csdn.net/detail/kaitiren/7604097