TCP例子


服务端

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <Winsock2.h>
#pragma comment(lib, "Ws2_32.lib")

/// <summary>
/// 初始化套接字库
/// </summary>
void initialization() {
WORD w_req = MAKEWORD(2, 2);//版本号
WSADATA wsadata;
int err;
err = WSAStartup(w_req, &wsadata);
if (err != 0) {
cout << "初始化套接字库失败!" << endl;
}
else {
cout << "初始化套接字库成功!" << endl;
}
//检测版本号
if (LOBYTE(wsadata.wVersion) != 2 HIBYTE(wsadata.wHighVersion) != 2) {
cout << "套接字库版本号不符!" << endl;
WSACleanup();
}
else {
cout << "套接字库版本正确!" << endl;
}
//填充服务端地址信息
}

DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
SOCKET sClient = (SOCKET)lpParameter;

while (true)
{
// 此处PACKAGE为自定义数据结构
PACKAGE pkgForm;
// 接收数据并对数据反序列化
int nRet = recv(sClient, (char*)&pkgForm, sizeof(PACKAGE), 0);
if (nRet == 0 nRet == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAECONNRESET)
{
printf("远程主机强迫关闭了一个现有的连接\r\n");
// 当客户端断开连接时,关闭socket并退出线程
closesocket(sClient);
return 0;
}
else
{
// 其他问题则跳过本次循环
continue;
}
}

// TODO: 进行包的数据分发由各自函数解析函数
// TODO: 需要将socket存入一个链表或动态数组中进行保存,以便消息广播等作用。在多线程中访问全局数据需要做线程同步

// 缓解cpu压力
Sleep(1);
}

return 0;
}

int main()
{
initialization();
//1)创建socket
SOCKET sockServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sockServer == INVALID_SOCKET)
{
cout << "socket 创建失败 \r\n" << endl;
return 0;
}

//绑定端口 127.0.0.1 回环地址
sockaddr_in siServer;
siServer.sin_family = AF_INET;
siServer.sin_port = htons(0x9527); // 服务器监听端口
siServer.sin_addr.S_un.S_addr = inet_addr("0.0.0.0"); // 服务器地址,也可以是0.0.0.0
int nRet = bind(sockServer, (sockaddr*)&siServer, sizeof(siServer));
if (nRet == SOCKET_ERROR)
{
cout << "绑定地址失败 \r\n" << endl;
return 0;
}
else
{
cout << "服务器启动完毕 \r\n" << endl;
}

if (listen(sockServer, SOMAXCONN) == SOCKET_ERROR)
{
cout << "端口监听失败 \r\n" << endl;
}
cout << "服务端正在监听连接,请稍候...." << endl;

// 循环读取消息
while (true)
{
//接受连接请求
sockaddr_in siAccept;
int nLen = sizeof(SOCKADDR_IN);
// 当有客户连接是阻塞取消
SOCKET sClient = ::accept(sockServer, (SOCKADDR*)&siAccept, &nLen);
if (sClient == SOCKET_ERROR) {
cout << "连接失败!" << endl;
WSACleanup();
return 0;
}
cout << "连接建立[" << inet_ntoa(siAccept.sin_addr) << ":" << ntohs(siAccept.sin_port) << "],准备接受数据" << endl;
// 连接建立成功则启动一个线程用来和客户端通讯
// 将客户端的socket传入线程中用来通讯
HANDLE hThread = CreateThread(NULL, 0, ThreadProc, (PVOID)sClient, 0, NULL);
if (hThread == NULL)
{
cout << "工作线程建立失败" << endl;
closesocket(sClient);
continue;
}
else
{
cout << "工作线程建立成功" << endl;
}
// 关闭线程句柄,但是线程仍在运行
CloseHandle(hThread);
Sleep(1);
}

//关闭套接字
closesocket(sockServer);
//释放DLL资源
WSACleanup();
}

客户端

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <Winsock2.h>
#pragma comment(lib, "Ws2_32.lib")

// 自定义用户消息,用来多线程中同步修改UI数据
#define WM_UPDATE_UI WM_USER + 0x1

/// <summary>
/// 初始化套接字库
/// </summary>
void initialization() {
WORD w_req = MAKEWORD(2, 2);//版本号
WSADATA wsadata;
int err;
err = WSAStartup(w_req, &wsadata);
if (err != 0) {
cout << "初始化套接字库失败!" << endl;
}
else {
cout << "初始化套接字库成功!" << endl;
}
//检测版本号
if (LOBYTE(wsadata.wVersion) != 2 HIBYTE(wsadata.wHighVersion) != 2) {
cout << "套接字库版本号不符!" << endl;
WSACleanup();
}
else {
cout << "套接字库版本正确!" << endl;
}
}

// 多线程处理服务器返回消息
DWORD WINAPI RecvSockMessage(LPVOID lpParameter)
{
// 类中调用多线程的时候将类传入函数
CClientDlg* pThis = (CClientDlg*)lpParameter;

while (true)
{
// 反序列化
// PACKAGE同服务端
PACKAGE pkgForm;
// 接收服务端返回消息,若无返回则会阻塞
int nRet = recv(pThis->m_socketClient, (char*)&pkgForm, sizeof(PACKAGE), 0);
if (nRet == 0 nRet == SOCKET_ERROR)
{
continue;
}

// 线程安全的将每个封包传入类中的封包队列中
pThis->m_clsLock.Lock();
pThis->m_lstPakToHandle.push_back(pkgForm);
pThis->m_clsLock.Unlock();
// 发送用户自定义消息进行UI更新以及数据包处理
pThis->PostMessage(WM_UPDATE_UI);
Sleep(1);
}
return 0;
}

// 用户自定义消息的处理函数
afx_msg LRESULT CClientDlg::OnUpdateUI(WPARAM wParam, LPARAM lParam)
{
// 线程安全获取封包
m_clsLock.Lock();
auto pkgForm = m_lstPakToHandle.front();
// 获取一个包将队列中的包去除一个
m_lstPakToHandle.pop_front();
m_clsLock.Unlock();

// TODO 数据包分发函数进行处理

return 0;
}

// 在不通环境下将此段代码加入各自的初始化函数中
void InitSocket()
{
initialization();
// 初始服务器数据
m_serverInfo.m_wPort = htons(0x9527); // 服务器端口
m_serverInfo.m_addr.S_un.S_addr = inet_addr("127.0.0.1"); // 服务器IP

// 服务器连接结构体
SOCKADDR_IN sServerAddr;
sServerAddr.sin_family = AF_INET;
sServerAddr.sin_addr = m_serverInfo.m_addr;
sServerAddr.sin_port = m_serverInfo.m_wPort;

// 创建socket
// m_socketClient 为全局或类中成员,类型为SOCKET
m_socketClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (m_socketClient == INVALID_SOCKET)
{
AfxMessageBox("程序异常,请检查软件权限");
exit(0);
}

// 连接服务器
if (connect(m_socketClient, (SOCKADDR*)&sServerAddr, sizeof(SOCKADDR)) == SOCKET_ERROR) {
AfxMessageBox("服务器连接失败");
WSACleanup();
exit(0);
}

// 启动收数据线程
m_hThread = CreateThread(NULL, 0, RecvSockMessage, this, 0, NULL);
if (m_hThread == NULL)
{
AfxMessageBox("程序异常 \r\n");
exit(0);
}

// 当客户端和服务器连接成功后,可以使用如下代码获取客户端的本地端口
sockaddr_in si;
int nLen = sizeof(si);
getsockname(m_socketClient, (sockaddr*)&si, &nLen);
m_localUserInfo.m_wPort = si.sin_port;

// TODO: 发送自己的数据包,比如登录包
}

UDP例子


服务端

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <Winsock2.h>
#pragma comment(lib, "Ws2_32.lib")

/// <summary>
/// 初始化套接字库
/// </summary>
void initialization() {
WORD w_req = MAKEWORD(2, 2);//版本号
WSADATA wsadata;
int err;
err = WSAStartup(w_req, &wsadata);
if (err != 0) {
cout << "初始化套接字库失败!" << endl;
}
else {
cout << "初始化套接字库成功!" << endl;
}
//检测版本号
if (LOBYTE(wsadata.wVersion) != 2 HIBYTE(wsadata.wHighVersion) != 2) {
cout << "套接字库版本号不符!" << endl;
WSACleanup();
}
else {
cout << "套接字库版本正确!" << endl;
}
//填充服务端地址信息
}

int main()
{
initialization();
// 创建socket
SOCKET sockServer = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); // udp协议
if (sockServer == INVALID_SOCKET)
{
cout << "socket 创建失败 \r\n");
return 0;
}

//绑定服务器IP和端口
sockaddr_in siServer;
siServer.sin_family = AF_INET;
siServer.sin_port = htons(0x9527);
siServer.sin_addr.S_un.S_addr = inet_addr("0.0.0.0");
//
int nRet = bind(sockServer, (sockaddr*)&siServer, sizeof(siServer));
if (nRet == SOCKET_ERROR)
{
printf("绑定地址失败 \r\n");
return 0;
}
else
{
printf("服务器启动完毕 \r\n");
}

// 维护一个在线列表
list<USERINFO> lstOnlineClient;

// 循环读取消息
while (true)
{
sockaddr_in siClient = { 0 };
int nLen = sizeof(siClient);
// 反序列化
PACKAGE pkgForm;
int nRet = recvfrom(sockServer, (char*)&pkgForm, sizeof(PACKAGE), 0, (sockaddr*)&siClient, &nLen);
if (nRet == 0 nRet == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAECONNRESET)
{
printf("远程主机强迫关闭了一个现有的连接\r\n");
continue;
}
else
{
continue;
}
}

// TODO: 数据处理
}

// 释放资源
closesocket(sockServer);
WSACleanup();
}

客户端

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <Winsock2.h>
#pragma comment(lib, "Ws2_32.lib")

#define WM_UPDATE_UI WM_USER + 0x1

/// <summary>
/// 初始化套接字库
/// </summary>
void initialization() {
WORD w_req = MAKEWORD(2, 2);//版本号
WSADATA wsadata;
int err;
err = WSAStartup(w_req, &wsadata);
if (err != 0) {
cout << "初始化套接字库失败!" << endl;
}
else {
cout << "初始化套接字库成功!" << endl;
}
//检测版本号
if (LOBYTE(wsadata.wVersion) != 2 HIBYTE(wsadata.wHighVersion) != 2) {
cout << "套接字库版本号不符!" << endl;
WSACleanup();
}
else {
cout << "套接字库版本正确!" << endl;
}
//填充服务端地址信息
}

// 多线程处理服务器返回消息
DWORD WINAPI RecvSockMessage(LPVOID lpParameter)
{
// 类中调用多线程的时候将类传入函数
CClientDlg* pThis = (CClientDlg*)lpParameter;

while (true)
{
sockaddr_in siClient = { 0 };
int nLen = sizeof(siClient);
// 反序列化
// PACKAGE同服务端
PACKAGE pkgForm;
// 接收服务端返回消息,若无返回则会阻塞
int nRet = recvfrom(pThis->m_socketClient, (char*)&pkgForm, sizeof(PACKAGE), 0, (sockaddr*)&siClient, &nLen);
if (nRet == 0 nRet == SOCKET_ERROR)
{
continue;
}

// 线程安全的将每个封包传入类中的封包队列中
pThis->m_clsLock.Lock();
pThis->m_lstPakToHandle.push_back(pkgForm);
pThis->m_clsLock.Unlock();
// 发送用户自定义消息进行UI更新以及数据包处理
pThis->PostMessage(WM_UPDATE_UI);
Sleep(1);
}
return 0;
}

// 用户自定义消息的处理函数
afx_msg LRESULT CClientDlg::OnUpdateUI(WPARAM wParam, LPARAM lParam)
{
// 线程安全获取封包
m_clsLock.Lock();
auto pkgForm = m_lstPakToHandle.front();
// 获取一个包将队列中的包去除一个
m_lstPakToHandle.pop_front();
m_clsLock.Unlock();

// TODO 数据包分发函数进行处理

return 0;
}

// 在不通环境下将此段代码加入各自的初始化函数中
void InitSocket()
{
initialization();
// 初始服务器数据
m_serverInfo.m_wPort = htons(0x9527); // 服务器端口
m_serverInfo.m_addr.S_un.S_addr = inet_addr("127.0.0.1"); // 服务器IP

// 创建socket
// m_socketClient 为全局或类中成员,类型为SOCKET
m_socketClient = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (m_socketClient == INVALID_SOCKET)
{
AfxMessageBox("程序异常,请检查软件权限");
exit(0);
}

// 启动收数据线程
m_hThread = CreateThread(NULL, 0, RecvSockMessage, this, 0, NULL);
if (m_hThread == NULL)
{
AfxMessageBox("程序异常 \r\n");
exit(0);
}

// 当客户端发送一次封包后,可以使用如下代码获取客户端的本地端口
sockaddr_in si;
int nLen = sizeof(si);
getsockname(m_socketClient, (sockaddr*)&si, &nLen);
m_localUserInfo.m_wPort = si.sin_port;

// TODO: 发送自己的数据包,比如登录包
}