-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathServer.cpp
More file actions
288 lines (237 loc) · 7.97 KB
/
Copy pathServer.cpp
File metadata and controls
288 lines (237 loc) · 7.97 KB
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
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
#include <iostream>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <vector>
#include <thread>
#include <mutex>
#include <algorithm>
#include <string>
#include <atomic>
#include <Windows.h>
#ifndef NOMINMAX
#define NOMINMAX
#endif
#pragma comment(lib, "ws2_32.lib")
using namespace std;
//版本号
string vertion = "1.3.1";
// 客户端信息结构体
struct ClientInfo {
SOCKET socket;
string name;
};
vector<ClientInfo> clients;
mutex clientsMutex;
atomic<bool> serverRunning(true);
atomic<bool> shutdownRequested(false);
SOCKET serverSocket = INVALID_SOCKET;
// Ctrl+C信号处理函数
BOOL WINAPI ConsoleHandler(DWORD signal) {
if (signal == CTRL_C_EVENT || signal == CTRL_CLOSE_EVENT) {
cout << "\n收到关闭信号。正在关闭服务器..." << endl;
serverRunning = false;
shutdownRequested = true;
if (serverSocket != INVALID_SOCKET) {
closesocket(serverSocket);
serverSocket = INVALID_SOCKET;
}
return TRUE;
}
return FALSE;
}
// 向所有客户端广播消息
void BroadcastMessage(const string& message, SOCKET senderSocket = INVALID_SOCKET) {
lock_guard<mutex> lock(clientsMutex);
for (const auto& client : clients) {
if (client.socket != senderSocket && client.socket != INVALID_SOCKET) {
send(client.socket, message.c_str(), message.length(), 0);
}
}
}
// 处理单个客户端连接
void HandleClient(SOCKET clientSocket) {
char buffer[1024];
int bytesReceived;
string clientName = "Unknown";
// 重要:设置socket为阻塞模式,以便接收数据
u_long mode = 0; // 0表示阻塞
ioctlsocket(clientSocket, FIONBIO, &mode);
// 设置接收超时
int timeout = 20000;
setsockopt(clientSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));
// 接收客户端名称
bytesReceived = recv(clientSocket, buffer, sizeof(buffer) - 1, 0);
if (bytesReceived > 0) {
buffer[bytesReceived] = '\0';
clientName = string(buffer);
string welcomeMsg = "欢迎" + clientName + "进入聊天室!";
cout << welcomeMsg << endl;
// 添加到客户端列表
{
lock_guard<mutex> lock(clientsMutex);
clients.push_back({ clientSocket, clientName });
}
// 广播欢迎消息给所有客户端(包括新客户端)
for (const auto& client : clients) {
if (client.socket != INVALID_SOCKET) {
send(client.socket, welcomeMsg.c_str(), welcomeMsg.length(), 0);
}
}
}
else {
cerr << "从客户端接受用户名失败" << endl;
closesocket(clientSocket);
return;
}
// 接收和转发消息
while (serverRunning && !shutdownRequested) {
bytesReceived = recv(clientSocket, buffer, sizeof(buffer) - 1, 0);
if (bytesReceived > 0) {
buffer[bytesReceived] = '\0';
string message = clientName + ": " + string(buffer);
cout << message << endl;
BroadcastMessage(message, clientSocket);
}
else if (bytesReceived == 0) {
cout << clientName << " 离开了客户端 " << endl;
break;
}
else {
int error = WSAGetLastError();
if (error == WSAETIMEDOUT) {
// 超时是正常的,继续等待
continue;
}
else if (error != 0) {
cerr << "接受用户名失败" << clientName << ": " << error << endl;
break;
}
}
}
// 清理客户端连接
if (clientSocket != INVALID_SOCKET) {
closesocket(clientSocket);
}
// 从客户端列表中移除
{
lock_guard<mutex> lock(clientsMutex);
auto it = find_if(clients.begin(), clients.end(),
[clientSocket](const ClientInfo& c) { return c.socket == clientSocket; });
if (it != clients.end()) {
clients.erase(it);
}
}
// 通知其他客户端
if (!shutdownRequested) {
string leaveMsg = clientName + " 离开了客户端 ";
cout << leaveMsg << endl;
BroadcastMessage(leaveMsg);
}
}
// 服务器主函数
int main() {
//修改控制台编码为UTF-8
SetConsoleOutputCP(CP_UTF8);
SetConsoleCP(CP_UTF8);
cout << "===网络聊天室 服务器===\n"<<"当前版本号 : "<<vertion<<endl<< endl;
// 设置控制台控制信号处理
if (!SetConsoleCtrlHandler(ConsoleHandler, TRUE)) {
cerr << "设置控制台控制处理器出错" << endl;
return 1;
}
// 初始化Winsock
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
cerr << "WSAStartup 运行失败: " << WSAGetLastError() << endl;
return 1;
}
cout << "1. Winsock 初始化成功..." << endl;
// 创建socket
serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (serverSocket == INVALID_SOCKET) {
cerr << "创建 socket 失败 : " << WSAGetLastError() << endl;
WSACleanup();
return 1;
}
cout << "2. 服务器 socket 创建成功..." << endl;
// 设置socket选项,允许地址重用
int reuse = 1;
setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&reuse, sizeof(reuse));
// 绑定地址
sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(8888);
serverAddr.sin_addr.s_addr = INADDR_ANY;
if (bind(serverSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
cerr << "绑定失败: " << WSAGetLastError() << endl;
closesocket(serverSocket);
WSACleanup();
return 1;
}
cout << "3. 成功连接至端口号 : 8888" << endl;
// 监听连接
if (listen(serverSocket, SOMAXCONN) == SOCKET_ERROR) {
cerr << "监听失败: " << WSAGetLastError() << endl;
closesocket(serverSocket);
WSACleanup();
return 1;
}
cout << "4. 正在监听链接..." << endl;
cout << "服务器正在运行中,按下ctrl+c停止运行...\n" << endl;
vector<thread> clientThreads;
// 接受客户端连接
while (serverRunning) {
sockaddr_in clientAddr;
int clientAddrSize = sizeof(clientAddr);
SOCKET clientSocket = accept(serverSocket, (sockaddr*)&clientAddr, &clientAddrSize);
if (clientSocket == INVALID_SOCKET) {
int error = WSAGetLastError();
if (shutdownRequested) {
break;
}
if (error != WSAEWOULDBLOCK) {
cerr << "接收失败: " << error << endl;
}
this_thread::sleep_for(chrono::milliseconds(100));
continue;
}
char clientIP[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &clientAddr.sin_addr, clientIP, INET_ADDRSTRLEN);
cout << "新的客户端连接 : " << clientIP << ":" << ntohs(clientAddr.sin_port) << endl;
// 创建线程处理客户端
clientThreads.emplace_back(HandleClient, clientSocket);
clientThreads.back().detach();
}
// 清理
cout << "关闭服务器..." << endl;
// 关闭所有客户端连接
{
lock_guard<mutex> lock(clientsMutex);
for (auto& client : clients) {
if (client.socket != INVALID_SOCKET) {
closesocket(client.socket);
client.socket = INVALID_SOCKET;
}
}
clients.clear();
}
// 等待所有线程结束
for (auto& t : clientThreads) {
if (t.joinable()) {
t.join();
}
}
// 关闭服务器socket
if (serverSocket != INVALID_SOCKET) {
closesocket(serverSocket);
serverSocket = INVALID_SOCKET;
}
// 清理Winsock
WSACleanup();
// 移除控制台控制信号处理
SetConsoleCtrlHandler(ConsoleHandler, FALSE);
cout << "服务器关闭成功!" << endl;
cout << "按下回车键离开..." << endl;
cin.get();
return 0;
}