基于Clion IDE的CPP项目构建与运行
工具基本信息
1.CLion
CLion:
A cross-platform IDE for C and C++
CLion takes a lot of the toil out of C++, allowing me to concentrate on the interesting part: problem solving.
- 下载地址
1
https://www.jetbrains.com/clion/download/#section=windows
2.MingW
A complete runtime environment for GCC & LLVMfor 32 and 64 bit Windows
- 下载地址(x64、posix、.seh版本)
1
https://sourceforge.net/projects/mingw-w64/files/mingw-w64/mingw-w64-release/
- 如图
3.CMake
CMake允许开发者编写一种平台无关的 CMakeList.txt 文件来定制整个编译流程,然后再根据目标用户的平台进一步生成所需的本地化 Makefile 和工程文件
- 基本指令
1 | cmake_minimum_required:指定运行此配置文件所需的 CMake 的最低版本; |
- 举例
1 | # CMake 最低版本号要求 |
工具安装构建
CLion
1.新建cpp项目
如图
2.编写测试代码
- 1.src/test.cpp
定义socket套机字,构建socket服务端;实现登录、登出、数据转发等功能
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
// 客户�?信息结构�?
struct SockInfo {
sockaddr_in addr;
int fd;
};
// 登录状态记录
typedef struct
{
char UserName[32];
int SocketID;
}loginPool;
// ------------------------------------------------------------------------
// 用户登录验证代码部分
std::vector<loginPool> login_pool_vect;
// 检查用户ID是否存在与容器内,如果存在则返回用户名
bool is_login(std::vector<loginPool> &ptr, int socket_id)
{
for (auto & x : ptr)
{
if (x.SocketID == socket_id)
{
return true;
}
}
return false;
}
// 用户登录验证
bool login(char *username, char *password, int socket_id)
{
if ((strcmp(username, "admin") == 0) && (strcmp(password, "123456") == 0))
{
// 如果在则增加一个socket登录标志
loginPool pool_ptr;
pool_ptr.SocketID = socket_id;
strcpy(pool_ptr.UserName, "admin");
login_pool_vect.push_back(pool_ptr);
return true;
}
else if ((strcmp(username, "admin") == 0) && (strcmp(password, "123456") == 0))
{
// 如果在则增加一个socket登录标志
loginPool pool_ptr;
pool_ptr.SocketID = socket_id;
strcpy(pool_ptr.UserName, "admin");
login_pool_vect.push_back(pool_ptr);
return true;
}
return false;
}
// 根据传入ID从容器内弹出一个节点
bool logout(std::vector<loginPool> &ptr, int socket_id)
{
for (auto it = ptr.begin(); it != ptr.end(); it++)
{
if (it->SocketID == socket_id)
{
// 弹出指定结构体
ptr.erase(it);
return true;
}
}
return false;
}
void ClientPro(void* ptr)
{
// 初始化
auto* pSock = (MySocket*)ptr;
MySocket server_socket = *pSock;
server_socket.Send((const char *)"Welcome to CPP WebSocket Server!", 31);
// 获取客户端信息
char sIp[20];
UINT nPort;
server_socket.GetPeerName(sIp, nPort);
while (true)
{
char szBuffer[4096] = { 0 };
int sid = pSock->GetSocketID();
int ref = server_socket.Receive(szBuffer, sizeof(szBuffer));
if (ref <= 0)
{
logout(login_pool_vect, sid);
ilog->warn("client :{0}:{1} , [disconnect]",sIp,nPort);
// std::cout << "客户: " << sIp << ":" << nPort << " [已断开]" << std::endl;
break;
}
ilog->info("address:{0}:{1},receive command:{2} ",sIp,nPort,szBuffer);
// std::cout << "地址: " << sIp << ":" << nPort << " 接收命令: " << szBuffer << std::endl;
// 用户登录
if (strcmp(szBuffer, "login\n") == 0)
{
char recv_username[32] = { 0 };
char recv_password[32] = { 0 };
// 接收用户名和密码
pSock->Receive(recv_username, 32, 0);
pSock->Receive(recv_password, 32, 0);
ilog->info("account :{0}, password : {1}",recv_username,recv_password);
// 验证登录状态
bool login_flag = login(recv_username, recv_password, sid);
if (login_flag == TRUE)
{
ilog->info("user :{0}, login .",recv_username);
// std::cout << "用户: " << recv_username << " 已登录" << std::endl;
pSock->Send("login", sizeof("login"), 0);
}
else
{
pSock->Send("account or password incorrect", sizeof("account or password incorrect"), 0);
}
}
// 用户登出
else if (strcmp(szBuffer, "logout\n") == 0)
{
// 验证是否登录成功
int login_flag = is_login(login_pool_vect, sid);
if (login_flag == TRUE)
{
ilog->info("user logout.");
// std::cout << "用户已登出" << std::endl;
logout(login_pool_vect, sid);
pSock->Send("user logout.", sizeof("user logout."), 0);
}
else
{
ilog->warn("please login account!");
// std::cout << "请先登录" << std::endl;
pSock->Send("please login account!", sizeof("please login account!"), 0);
}
}
// 遍历本机文件
else if (strcmp(szBuffer, "list\n") == 0)
{
// 验证是否登录成功
int login_flag = is_login(login_pool_vect, sid);
if (login_flag == TRUE)
{
ilog->info("user has login , output current pc file ");
// std::cout << "用户已登录,输出本机文件" << std::endl;
pSock->Send("auth success!", sizeof("auth success!"), 0);
// 循环输出数据包
for (int x = 0; x < 10; x++)
{
char sz[1024] = { 0 };
sprintf(sz, "count -> %d", x);
pSock->Send(sz, sizeof(sz), 0);
}
}
else
{
ilog->warn("please login account!");
// std::cout << "请先登录" << std::endl;
pSock->Send("please login account!", sizeof("please login account!"), 0);
}
}
}
}
int startSocketServer(){
MySocket sock;
if (!sock.Create(8233, SOCK_STREAM, "127.0.0.1"))
{
return -1;
}
// 获取本机信息
char sSevIp[20];
UINT nSevPort;
sock.GetSockName(sSevIp, nSevPort);
ilog->info("server : {0},{1} ,server is running!",sSevIp,nSevPort);
// std::cout << "服务端: " << sSevIp << ":" << nSevPort << " 服务器启动成功" << std::endl;
sock.Listen(5);
// 获取客户端信息
char sIp[20];
UINT nPort;
MySocket ptr;
while(true)
{
// 当有新用户进来自动创建一个线程来维持会话
sock.Accept(ptr, sIp, &nPort);
ilog->info("client : {0} :{1} [login]",sIp,nPort);
// std::cout << "客户: " << sIp << ":" << nPort << " [已登录]" << std::endl;
// 多线程
_beginthread(ClientPro, 0, &ptr);
}
return 0;
}
std::string testR(wsProtocol ws) {
return "asd";
}
std::string httpR(requestHttp httpRq) {
return "httpR";
}
void XmlNodeGetVal(std::stringstream stringstream, tinyxml2::XMLElement *pElement, const char *string, const char *ip) {
ip = pElement->Attribute(string);
}
int main() {
// readXMLConfig("./../config.xml");
startSocketServer();
// startWebSocketServer();
return 0;
} - 2.include/test.h
存放test.cpp中所需头文件与函数定义,变量定义.
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
137
138
139
140
141
class MySocket
{
protected:
SOCKET m_hSocket;
public:
// 获取对端Socket用户IP端口等
BOOL GetPeerName(char* rSocketAddress, UINT& rSocketPort)
{
sockaddr_in name = { AF_INET };
int lenname = sizeof(name);
if (getpeername(m_hSocket, (sockaddr*)&name, &lenname) < 0)
return false;
strcpy(rSocketAddress, inet_ntoa(name.sin_addr));
rSocketPort = htons(name.sin_port);
return true;
}
// 获取本机Socket用户IP端口等
BOOL GetSockName(char* rSocketAddress, UINT& rSocketPort)
{
sockaddr_in name = { AF_INET };
int lenname = sizeof(name);
if (getsockname(m_hSocket, (sockaddr*)&name, &lenname) < 0)
return false;
strcpy(rSocketAddress, inet_ntoa(name.sin_addr));
rSocketPort = htons(name.sin_port);
return true;
}
// 获取当前用户SocketID
BOOL GetSocketID()
{
return m_hSocket;
}
// 创建套接字
BOOL Create(UINT nSocketPort = 0, int nSockType = SOCK_STREAM, LPCTSTR lpszSocketAddress = NULL)
{
// 创建套接字
m_hSocket = socket(AF_INET, nSockType, 0);
if (m_hSocket == INVALID_SOCKET)
return false;
// 设置IP地址和端口
sockaddr_in sa = { AF_INET };
sa.sin_port = htons(nSocketPort);
if (lpszSocketAddress)
sa.sin_addr.s_addr = inet_addr(lpszSocketAddress);
// 绑定套接字和IP地址端口
return !bind(m_hSocket, (sockaddr*)&sa, sizeof(sa));
}
// 接受客户请求
BOOL Accept(MySocket& rConnectedSock, LPSTR szIp = NULL, UINT* nPort = NULL)
{
sockaddr_in sa = { AF_INET };
int nLen = sizeof(sa);
rConnectedSock.m_hSocket = accept(this->m_hSocket, (sockaddr*)&sa, &nLen);
if (rConnectedSock.m_hSocket == INVALID_SOCKET)
return false;
if (szIp)
strcpy(szIp, inet_ntoa(sa.sin_addr));
if (nPort)
*nPort = htons(sa.sin_port);
return true;
}
// 连接服务端
BOOL Connection(LPCSTR lpszHostAddress, UINT nPort)
{
sockaddr_in sa = { AF_INET };
sa.sin_port = htons(nPort);
sa.sin_addr.s_addr = inet_addr(lpszHostAddress);
return !connect(m_hSocket, (sockaddr*)&sa, sizeof(sa));
}
// 侦听
BOOL Listen(int nConnectionBacklog = 5)
{
return !listen(m_hSocket, nConnectionBacklog);
}
// 逐条发送
int Send(const void* lpBuf, int nBufLen, int nFlags = 0)
{
return send(m_hSocket, (LPCSTR)lpBuf, nBufLen, nFlags);
}
// 发送整个缓冲区
int SendTo(const void* lpBuf, int nBufLen, UINT nHostPort, LPCSTR lpszHostAddress = NULL,
int nFlags = 0)
{
sockaddr_in to = { AF_INET };
to.sin_port = htons(nHostPort);
to.sin_addr.s_addr = inet_addr(lpszHostAddress);
return sendto(m_hSocket, (LPCSTR)lpBuf, nBufLen, nFlags, (sockaddr*)&to, sizeof(to));
}
// 逐条接收
int Receive(void* lpBuf, int nBufLen, int nFlags = 0)
{
return recv(m_hSocket, (LPTSTR)lpBuf, nBufLen, nFlags);
}
// 接收整个缓冲区
int ReceiveFrom(void* lpBuf, int nBufLen, char* rSocketAddress, UINT& rSocketPort, int nFlags = 0)
{
sockaddr_in from = { AF_INET };
int lenFrom = sizeof(from);
int n = recvfrom(m_hSocket, (LPSTR)lpBuf, nBufLen, nFlags, (sockaddr*)&from, &lenFrom);
strcpy(rSocketAddress, inet_ntoa(from.sin_addr));
rSocketPort = htons(from.sin_port);
return n;
}
// 关闭套接字
void Close()
{
closesocket(m_hSocket);
m_hSocket = INVALID_SOCKET;
}
MySocket()
{
WSADATA wsaData;
WSAStartup(0x0202, &wsaData);
m_hSocket = INVALID_SOCKET;
}
~MySocket()
{
Close();
}
}; - 3.CMakeLists.txt
配置CMake编译所需环境
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# cmake最小版本
cmake_minimum_required(VERSION 3.26)
# 定义项目名
project(cpp_socket_server)
# cpp版本
set(CMAKE_CXX_STANDARD 17)
# 设置编译参数
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -s")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s")
# 引入win平台socket库
link_libraries(ws2_32)
# 引入所需头文件库
include_directories(
lib/include
lib/include/spdlog
# lib/include/cryptopp
)
# 引入所需编译源文件(*.cpp)
aux_source_directory(
src
SRC_FILES
)
# 设置编译文件
set(main_file
${SRC_FILES}
lib/include/tcpSocket.h
src/tcpSocket/tcpSocket.cpp
src/tcpSocket/MySocket.hpp
lib/include/tinyxml2.h
src/utils/tinyxml2.cpp
lib/include/xmlParser.h
src/utils/xmlParser.cpp
)
# 链接第三方库
if(SPDLOG_BUILD_EXAMPLE_HO)
target_link_libraries(cpp_socket_server PRIVATE spdlog::spdlog_header_only)
endif()
# 添加可执行文件
# Add executable files
add_executable(
${PROJECT_NAME}
${main_file}
)
# 链接第三方库
target_link_libraries(
${PROJECT_NAME}
${LIBRARY}
)
mingw工具
- 配置环境
下载完mingw包后,在系统变量Path中引入以下路径
1
D:\mingw64\bin
- 工具引入
在clion中,可添加mingw工具。点击File->Settings->Build、Execution、Deployment->Toolchains;在其中设置如下路径,也可使用clion默认路径
1
toolset: D:\\mingw64\\bin
如图
- 编写配置
在CMakeLists.txt(示例)中写入需引入配置,如下所示:
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# 设置 CMake 最低版本要求
cmake_minimum_required(VERSION 3.8)
# 定义项目名称和版本
project(MyApp VERSION 1.0.0 LANGUAGES CXX)
# 设置 C++ 标准
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# 定义用户可配置的选项
option(ENABLE_DEBUG "Enable debug output" ON)
if(ENABLE_DEBUG)
add_definitions(-DDEBUG_OUTPUT)
endif()
# 自定义宏:添加 MSVC 常用编译选项
macro(add_msvc_options target)
if(MSVC)
target_compile_options(${target} PRIVATE
/W4 # 设置警告级别为 4
/WX # 将警告视为错误
/MP # 启用多处理器编译
/permissive- # 禁用不严格的语言 conformance
/Zc:__cplusplus # 启用正确的 __cplusplus 宏值
/Zc:inline # 移除未使用的函数
/Gm- # 禁用最小生成(minimal rebuild)
/EHsc # 指定异常处理模型
)
endif()
endmacro()
# 添加源文件
set(SOURCE_FILES src/main.cpp)
# 生成可执行文件
add_executable(MyApp ${SOURCE_FILES})
# 调用自定义宏,为 MyApp 添加 MSVC 常用编译选项
add_msvc_options(MyApp)
# 为特定目标设置头文件目录
target_include_directories(MyApp PRIVATE include)
# 链接静态库
find_library(STATIC_LIB libStatic.lib PATHS "${CMAKE_SOURCE_DIR}/libs/static")
target_link_libraries(MyApp PRIVATE ${STATIC_LIB})
# 链接动态库
find_library(DYNAMIC_LIB libDynamic.dll PATHS "${CMAKE_SOURCE_DIR}/libs/dynamic")
find_library(DYNAMIC_LIB_IMPORT libDynamic.lib PATHS "${CMAKE_SOURCE_DIR}/libs/dynamic")
target_link_libraries(MyApp PRIVATE ${DYNAMIC_LIB_IMPORT})
# 使用 Windows 的 DLL delay-load 机制
set_target_properties(MyApp PROPERTIES LINK_FLAGS "/DELAYLOAD:libDynamic.dll")
# 根据目标架构定制编译选项和链接选项
if(CMAKE_GENERATOR_PLATFORM STREQUAL "Win32")
message("Building for Win32 (x86) architecture")
target_compile_options(MyApp PRIVATE /arch:SSE2)
elseif(CMAKE_GENERATOR_PLATFORM STREQUAL "x64")
message("Building for x64 architecture")
target_compile_options(MyApp PRIVATE /arch:AVX2)
else()
message(WARNING "Unknown architecture")
endif()
# 添加子项目
add_subdirectory(subproject)
# 在构建时生成配置文件
configure_file(config.h.in config.h @ONLY)
# 指定安装规则
install(TARGETS MyApp RUNTIME DESTINATION bin)
install(FILES "${CMAKE_SOURCE_DIR}/libs/dynamic/libDynamic.dll" DESTINATION bin)
CMake编译
- 编译test.exe
点击运行按钮,查看效果
- 目录结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23MyApp/
├─ CMakeLists.txt
├─ src/
│ └─ main.cpp
├─ include/
│ ├─ static_lib/
│ │ └─ StaticLibHeader.h
│ └─ dynamic_lib/
│ └─ DynamicLibHeader.h
├─ libs/
│ ├─ static/
│ │ └─ libStatic.lib
│ └─ dynamic/
│ ├─ libDynamic.dll
│ └─ libDynamic.lib
├─ subproject/
│ ├─ CMakeLists.txt
│ ├─ src/
│ │ └─ subproject_main.cpp
│ └─ include/
│ └─ subproject/
│ └─ SubProjectHeader.h
└─ config.h.in - cmake运行(Visual Studio 2019)
1
2
3
4# 对于 x86 架构:
cmake -G "Visual Studio 16 2019" -A Win32 ..
# 对于 x64 架构:
cmake -G "Visual Studio 16 2019" -A x64 ..
引入第三方库(以spdlog为例)
Very fast, header-only/compiled, C++ logging library.
工具下载
在以下路径下载spdlog:
1 | git clone https://github.com/gabime/spdlog.git |
cmake引入
在项目include 文件夹中复制以下文件夹内容
1 | include/spdlog/ |
在CMakeLists.txt中引入库
1 | include_directories( |
测试运行
编写如下代码
- 1.封装spdlog库
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
class CLog {
public:
static CLog &instance() {
static CLog m_instance;
return m_instance;
}
auto get_logger() const {
return this->logger_;
}
~CLog() {
spdlog::drop_all(); // 释放所有logger
}
CLog() {
this->init();
}
private:
void init() {
std::cout << "Log System Init , start record log : " << std::endl;
this->init_file();
this->init_logger();
}
void init_file() {
this->log_root_path = R"(C:\Users\Administrator\CLionProjects\cpp_socket_server\logs\)";
this->info_file_path = "info.log";
this->error_file_path = "error.log";
this->rotation_h = 5; // 分割时间
this->rotation_m = 59;
}
void init_logger() {
this->info_sink_ = std::make_shared<spdlog::sinks::daily_file_sink_mt>(
this->log_root_path + this->info_file_path, this->rotation_h, this->rotation_m);
this->error_sink_ = std::make_shared<spdlog::sinks::daily_file_sink_mt>(
this->log_root_path + this->error_file_path, this->rotation_h, this->rotation_m);
this->console_sink_ = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
this->info_sink_->set_level(spdlog::level::info); // debug< info< warn< error< critical 日志信息低于设置的级别时, 不予显示
this->error_sink_->set_level(spdlog::level::err);
this->console_sink_->set_level(spdlog::level::debug);
this->sinks_.push_back(this->info_sink_); // info
this->sinks_.push_back(this->error_sink_); // error
this->sinks_.push_back(this->console_sink_); // console
this->logger_ = std::make_shared<spdlog::logger>("log_demo", begin(this->sinks_), end(this->sinks_));
this->logger_->set_pattern("[%l] [%Y-%m-%d %H:%M:%S %e] [Process:%P] - %v");
this->logger_->flush_on(spdlog::level::info); // 设置当触发 info 或更严重的错误时立刻刷新日志到 disk
spdlog::register_logger(this->logger_); // 注册logger
spdlog::flush_every(std::chrono::seconds(10)); // 每隔10秒刷新一次日志
}
private:
std::shared_ptr <spdlog::logger> logger_;
std::shared_ptr <spdlog::sinks::daily_file_sink_mt> info_sink_; // info
std::shared_ptr <spdlog::sinks::daily_file_sink_mt> error_sink_; // error
std::shared_ptr <spdlog::sinks::stdout_color_sink_mt> console_sink_; // console
std::vector <spdlog::sink_ptr> sinks_;
std::string log_root_path;
std::string error_file_path;
std::string info_file_path;
short int rotation_h{};
short int rotation_m{};
}; // CLog - 2.在主cpp中写入测试代码
1
2
3ilog->info("server : {0},{1} ,server is running!",sSevIp,nSevPort);
ilog->warn("client :{0}:{1} , [disconnect]",sIp,nPort);
ilog->error("[VP] load config file error : {0}",doc_dump.LoadFile(path)); - 3.运行主cpp,测试效果
如图: