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