-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathClient.cpp
More file actions
334 lines (271 loc) · 9.26 KB
/
Copy pathClient.cpp
File metadata and controls
334 lines (271 loc) · 9.26 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
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
#include <iostream>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <thread>
#include <string>
#include <atomic>
#include <Windows.h>
#include <cstddef> // 用于 std::string::npos,v1.3修改
#ifndef NOMINMAX
#define NOMINMAX
#endif
#pragma comment(lib, "ws2_32.lib")
using namespace std;
atomic<bool> clientRunning(true);
atomic<bool> shutdownRequested(false);
SOCKET clientSocket = INVALID_SOCKET;
//获取ip地址函数(可选自动或手动输入)
string getServerIP() {
int choice;
string serverIP;//重要返回值
cout << "\n=== 选择连接方式 ===" << endl;
cout << "1. 自动输入本地回环ip (127.0.0.1)" << endl;
cout << "2. 手动输入服务器IP (用于跨设备连接)" << endl;
cout << "请选择 (1 或 2): ";
cin >> choice;
cin.ignore(); // 清除输入缓冲区中的换行符
if (choice == 1) {
serverIP = "127.0.0.1";
cout << "已选择本地自动连接模式,客户端服务器IP: " << serverIP << endl;
}
else if (choice == 2) {
//v1.3修改,需要输入端口号
cout << "请输入服务器地址 (格式: IP:端口, 例如 60.215.128.117:64403): ";
getline(cin, serverIP);
// 简单的IP格式验证
if (serverIP.empty()) {
cerr << "错误: IP地址不能为空,将使用默认地址 127.0.0.1" << endl;
serverIP = "127.0.0.1";
}
}
else {
cerr << "无效的选择,将使用默认地址 127.0.0.1" << endl;
serverIP = "127.0.0.1";
}
return serverIP;
}
//v1.3新增
// 分离IP和端口,默认端口为8888
pair<string, short> parseAddress(const string& input) {
string ip = input;
short port = 8888; // 默认端口
size_t colonPos = input.find(':');
if (colonPos != string::npos) {
ip = input.substr(0, colonPos);
try {
port = static_cast<short>(stoi(input.substr(colonPos + 1)));
}
catch (...) {
cerr << "警告: 端口解析失败,使用默认端口 8888" << endl;
}
}
return make_pair(ip, port);
}
// Ctrl+C信号处理函数
BOOL WINAPI ConsoleHandler(DWORD signal) {
if (signal == CTRL_C_EVENT || signal == CTRL_CLOSE_EVENT) {
cout << "\nShutdown signal received. Closing client..." << endl;
clientRunning = false;
shutdownRequested = true;
if (clientSocket != INVALID_SOCKET) {
closesocket(clientSocket);
clientSocket = INVALID_SOCKET;
}
return TRUE;
}
return FALSE;
}
// 接收消息线程函数
void ReceiveMessages() {
char buffer[1024];
int bytesReceived;
while (clientRunning && !shutdownRequested) {
bytesReceived = recv(clientSocket, buffer, sizeof(buffer) - 1, 0);
if (bytesReceived > 0) {
buffer[bytesReceived] = '\0';
cout << buffer << endl;
cout << "> " << flush;
}
else if (bytesReceived == 0) {
if (!shutdownRequested) {
cout << "\n服务器断开连接" << endl;
}
clientRunning = false;
break;
}
else {
int error = WSAGetLastError();
if (error != WSAEWOULDBLOCK && error != 0) {
if (!shutdownRequested) {
cout << "\n失去连接 Error: " << error << endl;
}
clientRunning = false;
break;
}
}
this_thread::sleep_for(chrono::milliseconds(100));
}
}
// 客户端主函数
int main() {
//修改控制台编码为UTF-8
SetConsoleOutputCP(CP_UTF8);
SetConsoleCP(CP_UTF8);
cout << "=== 网络聊天室客户端 ===" << 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
clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (clientSocket == INVALID_SOCKET) {
cerr << "创建socket失败: " << WSAGetLastError() << endl;
WSACleanup();
return 1;
}
cout << "2. 客户端 socket 创建成功..." << endl;
// 设置连接超时
int timeout = 10000;
setsockopt(clientSocket, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(timeout));
setsockopt(clientSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));
// 设置服务器地址
sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET;
//v1.3删除
//serverAddr.sin_port = htons(8888);
/* V1.1版本原代码:
string serverIP;
cout << "输入ip端口号,回车键确认 (默认为: 127.0.0.1): ";
getline(cin, serverIP);
if (serverIP.empty()) {
serverIP = "127.0.0.1";
}
*/
//V1.2版本新增代码,调用getServerIP函数
//string serverIP = getServerIP();
//
//v1.3版本新增代码
// 改为:
string input = getServerIP(); // 获取用户输入
// 使用 pair 来接收 parseAddress 的返回结果
pair<string, short> addressPair = parseAddress(input);
string serverIP = addressPair.first;
short serverPort = addressPair.second;
//设置端口和ip
serverAddr.sin_port = htons(serverPort); // 使用解析出的端口
//
// 后续的连接提示信息也需要更新:
cout << "正在连接服务器 " << serverIP << ":" << serverPort << "..." << endl;
// 将IP地址字符串转换为网络字节序
if (inet_pton(AF_INET, serverIP.c_str(), &serverAddr.sin_addr) != 1) {
cerr << "无效的ip地址格式" << endl;
WSACleanup();
cout << "按回车键退出" << endl;
cin.get();
return 1;
}
// 连接服务器
cout << "已成功连接至服务器 " << serverIP << ":8888..." << endl;
if (connect(clientSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
int error = WSAGetLastError();
cerr << "连接失败 with error " << error << ": ";
switch (error) {
case WSAETIMEDOUT:
cerr << "连接超时 检查服务器是否正在运行." << endl;
break;
case WSAECONNREFUSED:
cerr << "被拒绝连接. 服务器可能未在端口 8888 上监听." << endl;
break;
default:
cerr << "未知错误." << endl;
break;
}
WSACleanup();
cout << "按回车键退出" << endl;
cin.get();
return 1;
}
// 重置超时设置,使用默认值
timeout = 0;
setsockopt(clientSocket, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(timeout));
setsockopt(clientSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));
// 输入用户名
string userName;
cout << "输入用户名(回车键确认): ";
getline(cin, userName);
if (userName.empty()) {
userName = "游客";
}
// 发送用户名到服务器
int userNameLength = static_cast<int>(userName.length());
if (send(clientSocket, userName.c_str(), userNameLength, 0) == SOCKET_ERROR) {
int error = WSAGetLastError();
cerr << "发送用户名失败... " << error << endl;
closesocket(clientSocket);
WSACleanup();
cout << "按下回车键离开" << endl;
cin.get();
return 1;
}
cout << "\n=== 您已进入聊天室 ===" << endl;
cout << "输入消息并按回车键发送" << endl;
cout << "输入 'exit' 离开聊天室\n" << endl;
// 创建接收消息线程
thread receiveThread(ReceiveMessages);
// 发送消息
string message;
while (clientRunning && !shutdownRequested) {
cout << "> ";
if (!getline(cin, message)) {
break;
}
if (message == "exit") {
clientRunning = false;
break;
}
if (!message.empty()) {
int messageLength = static_cast<int>(message.length());
if (send(clientSocket, message.c_str(), messageLength, 0) == SOCKET_ERROR) {
int error = WSAGetLastError();
if (!shutdownRequested) {
cerr << "发送消息失败: " << error << endl;
}
clientRunning = false;
break;
}
}
}
// 断开连接和清理
cout << "\n正在断开连接..." << endl;
shutdownRequested = true;
// 关闭socket发送端
shutdown(clientSocket, SD_SEND);
// 等待接收线程结束
if (receiveThread.joinable()) {
receiveThread.join();
}
// 关闭socket
if (clientSocket != INVALID_SOCKET) {
closesocket(clientSocket);
clientSocket = INVALID_SOCKET;
}
// 清理Winsock
WSACleanup();
// 移除控制台控制信号处理
SetConsoleCtrlHandler(ConsoleHandler, FALSE);
cout << "客户端关闭成功" << endl;
// 等待用户查看关闭消息
cout << "按下回车键离开..." << endl;
cin.clear();
cin.ignore((numeric_limits<streamsize>::max)(), '\n');
cin.get();
return 0;
}