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