react-native-tcp-windows 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/ReactNativeTcpWindows.js.map +1 -1
- package/lib/commonjs/index.js +37 -37
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/ReactNativeTcpWindows.js.map +1 -1
- package/lib/module/index.js +37 -37
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/commonjs/ReactNativeTcpWindows.d.ts.map +1 -0
- package/lib/typescript/commonjs/index.d.ts.map +1 -0
- package/lib/typescript/module/ReactNativeTcpWindows.d.ts.map +1 -0
- package/lib/typescript/module/index.d.ts.map +1 -0
- package/package.json +205 -204
- package/src/ReactNativeTcpWindows.ts +14 -14
- package/src/index.tsx +124 -124
- package/windows/ExperimentalFeatures.props +33 -33
- package/windows/ReactNativeTcpWindows/ReactNativeTcpWindows.cpp +206 -206
- package/windows/ReactNativeTcpWindows/ReactNativeTcpWindows.def +3 -3
- package/windows/ReactNativeTcpWindows/ReactNativeTcpWindows.h +83 -83
- package/windows/ReactNativeTcpWindows/ReactNativeTcpWindows.vcxproj +145 -145
- package/windows/ReactNativeTcpWindows/ReactNativeTcpWindows.vcxproj.filters +43 -43
- package/windows/ReactNativeTcpWindows/ReactPackageProvider.cpp +20 -20
- package/windows/ReactNativeTcpWindows/ReactPackageProvider.h +24 -24
- package/windows/ReactNativeTcpWindows/ReactPackageProvider.idl +9 -9
- package/windows/ReactNativeTcpWindows/TcpSocket.cpp +564 -564
- package/windows/ReactNativeTcpWindows/codegen/NativeReactNativeTcpWindowsSpec.g.h +71 -71
- package/windows/ReactNativeTcpWindows/packages.lock.json +59 -59
- package/windows/ReactNativeTcpWindows/pch.cpp +1 -1
- package/windows/ReactNativeTcpWindows/pch.h +30 -30
- package/windows/ReactNativeTcpWindows/resource.h +5 -5
- package/windows/ReactNativeTcpWindows/targetver.h +8 -8
- package/windows/ReactNativeTcpWindows.sln +43 -43
- package/lib/typescript/commonjs/src/ReactNativeTcpWindows.d.ts.map +0 -1
- package/lib/typescript/commonjs/src/__tests__/index.test.d.ts +0 -1
- package/lib/typescript/commonjs/src/__tests__/index.test.d.ts.map +0 -1
- package/lib/typescript/commonjs/src/index.d.ts.map +0 -1
- package/lib/typescript/module/src/ReactNativeTcpWindows.d.ts.map +0 -1
- package/lib/typescript/module/src/__tests__/index.test.d.ts +0 -1
- package/lib/typescript/module/src/__tests__/index.test.d.ts.map +0 -1
- package/lib/typescript/module/src/index.d.ts.map +0 -1
- /package/lib/typescript/commonjs/{src/ReactNativeTcpWindows.d.ts → ReactNativeTcpWindows.d.ts} +0 -0
- /package/lib/typescript/commonjs/{src/index.d.ts → index.d.ts} +0 -0
- /package/lib/typescript/module/{src/ReactNativeTcpWindows.d.ts → ReactNativeTcpWindows.d.ts} +0 -0
- /package/lib/typescript/module/{src/index.d.ts → index.d.ts} +0 -0
@@ -1,565 +1,565 @@
|
|
1
|
-
#include "pch.h"
|
2
|
-
#include "TcpSocket.h"
|
3
|
-
#include <sstream>
|
4
|
-
|
5
|
-
bool TcpSocket::s_wsaInitialized = false;
|
6
|
-
int TcpSocket::s_wsaRefCount = 0;
|
7
|
-
std::mutex TcpSocket::s_wsaMutex;
|
8
|
-
|
9
|
-
TcpSocket::TcpSocket(const std::string &address, int port, ConnectionType type)
|
10
|
-
: m_address(address), m_port(port), m_type(type), m_socket(INVALID_SOCKET), m_serverSocket(INVALID_SOCKET), m_isRunning(false), m_isConnected(false), m_isServerRunning(false)
|
11
|
-
{
|
12
|
-
|
13
|
-
std::lock_guard<std::mutex> lock(s_wsaMutex);
|
14
|
-
if (!s_wsaInitialized)
|
15
|
-
{
|
16
|
-
WSADATA wsaData;
|
17
|
-
if (WSAStartup(MAKEWORD(2, 2), &wsaData) == 0)
|
18
|
-
{
|
19
|
-
s_wsaInitialized = true;
|
20
|
-
}
|
21
|
-
}
|
22
|
-
s_wsaRefCount++;
|
23
|
-
}
|
24
|
-
|
25
|
-
TcpSocket::~TcpSocket()
|
26
|
-
{
|
27
|
-
close();
|
28
|
-
|
29
|
-
std::lock_guard<std::mutex> lock(s_wsaMutex);
|
30
|
-
s_wsaRefCount--;
|
31
|
-
if (s_wsaRefCount == 0 && s_wsaInitialized)
|
32
|
-
{
|
33
|
-
WSACleanup();
|
34
|
-
s_wsaInitialized = false;
|
35
|
-
}
|
36
|
-
}
|
37
|
-
|
38
|
-
bool TcpSocket::connect()
|
39
|
-
{
|
40
|
-
if (m_type != ConnectionType::Client)
|
41
|
-
{
|
42
|
-
return false;
|
43
|
-
}
|
44
|
-
|
45
|
-
if (m_isConnected)
|
46
|
-
{
|
47
|
-
close();
|
48
|
-
}
|
49
|
-
|
50
|
-
m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
51
|
-
if (m_socket == INVALID_SOCKET)
|
52
|
-
{
|
53
|
-
if (m_connectionStatusCallback)
|
54
|
-
{
|
55
|
-
m_connectionStatusCallback(false, "Failed to create socket");
|
56
|
-
}
|
57
|
-
return false;
|
58
|
-
}
|
59
|
-
|
60
|
-
sockaddr_in serverAddr = {};
|
61
|
-
serverAddr.sin_family = AF_INET;
|
62
|
-
serverAddr.sin_port = htons(m_port);
|
63
|
-
|
64
|
-
if (inet_pton(AF_INET, m_address.c_str(), &serverAddr.sin_addr) <= 0)
|
65
|
-
{
|
66
|
-
closesocket(m_socket);
|
67
|
-
m_socket = INVALID_SOCKET;
|
68
|
-
if (m_connectionStatusCallback)
|
69
|
-
{
|
70
|
-
m_connectionStatusCallback(false, "Invalid address format");
|
71
|
-
}
|
72
|
-
return false;
|
73
|
-
}
|
74
|
-
|
75
|
-
if (::connect(m_socket, (sockaddr *)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)
|
76
|
-
{
|
77
|
-
int error = WSAGetLastError();
|
78
|
-
closesocket(m_socket);
|
79
|
-
m_socket = INVALID_SOCKET;
|
80
|
-
if (m_connectionStatusCallback)
|
81
|
-
{
|
82
|
-
m_connectionStatusCallback(false, "Connection failed. Error: " + std::to_string(error));
|
83
|
-
}
|
84
|
-
return false;
|
85
|
-
}
|
86
|
-
|
87
|
-
m_isConnected = true;
|
88
|
-
m_isRunning = true;
|
89
|
-
startReading();
|
90
|
-
|
91
|
-
if (m_connectionStatusCallback)
|
92
|
-
{
|
93
|
-
m_connectionStatusCallback(true, "Connected successfully");
|
94
|
-
}
|
95
|
-
|
96
|
-
return true;
|
97
|
-
}
|
98
|
-
|
99
|
-
bool TcpSocket::startServer()
|
100
|
-
{
|
101
|
-
if (m_type != ConnectionType::Server)
|
102
|
-
{
|
103
|
-
return false;
|
104
|
-
}
|
105
|
-
|
106
|
-
if (m_isServerRunning)
|
107
|
-
{
|
108
|
-
close();
|
109
|
-
}
|
110
|
-
|
111
|
-
m_serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
112
|
-
if (m_serverSocket == INVALID_SOCKET)
|
113
|
-
{
|
114
|
-
if (m_connectionStatusCallback)
|
115
|
-
{
|
116
|
-
m_connectionStatusCallback(false, "Failed to create server socket");
|
117
|
-
}
|
118
|
-
return false;
|
119
|
-
}
|
120
|
-
|
121
|
-
// Allow socket reuse
|
122
|
-
BOOL reuseAddr = TRUE;
|
123
|
-
setsockopt(m_serverSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&reuseAddr, sizeof(reuseAddr));
|
124
|
-
|
125
|
-
sockaddr_in serverAddr = {};
|
126
|
-
serverAddr.sin_family = AF_INET;
|
127
|
-
serverAddr.sin_port = htons(m_port);
|
128
|
-
serverAddr.sin_addr.s_addr = INADDR_ANY;
|
129
|
-
|
130
|
-
if (bind(m_serverSocket, (sockaddr *)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)
|
131
|
-
{
|
132
|
-
int error = WSAGetLastError();
|
133
|
-
closesocket(m_serverSocket);
|
134
|
-
m_serverSocket = INVALID_SOCKET;
|
135
|
-
if (m_connectionStatusCallback)
|
136
|
-
{
|
137
|
-
m_connectionStatusCallback(false, "Bind failed. Error: " + std::to_string(error));
|
138
|
-
}
|
139
|
-
return false;
|
140
|
-
}
|
141
|
-
|
142
|
-
if (listen(m_serverSocket, SOMAXCONN) == SOCKET_ERROR)
|
143
|
-
{
|
144
|
-
int error = WSAGetLastError();
|
145
|
-
closesocket(m_serverSocket);
|
146
|
-
m_serverSocket = INVALID_SOCKET;
|
147
|
-
if (m_connectionStatusCallback)
|
148
|
-
{
|
149
|
-
m_connectionStatusCallback(false, "Listen failed. Error: " + std::to_string(error));
|
150
|
-
}
|
151
|
-
return false;
|
152
|
-
}
|
153
|
-
|
154
|
-
m_isServerRunning = true;
|
155
|
-
m_isRunning = true;
|
156
|
-
|
157
|
-
// Start accepting connections
|
158
|
-
m_acceptThread = std::thread(&TcpSocket::serverAcceptThread, this);
|
159
|
-
|
160
|
-
if (m_connectionStatusCallback)
|
161
|
-
{
|
162
|
-
m_connectionStatusCallback(true, "Server started on port " + std::to_string(m_port));
|
163
|
-
}
|
164
|
-
|
165
|
-
return true;
|
166
|
-
}
|
167
|
-
|
168
|
-
void TcpSocket::close()
|
169
|
-
{
|
170
|
-
// STEP 1: Shutdown sockets FIRST to wake up any blocking operations
|
171
|
-
if (m_socket != INVALID_SOCKET) {
|
172
|
-
shutdown(m_socket, SD_BOTH); // This wakes up recv() calls
|
173
|
-
}
|
174
|
-
|
175
|
-
if (m_serverSocket != INVALID_SOCKET) {
|
176
|
-
shutdown(m_serverSocket, SD_BOTH); // This wakes up accept() calls
|
177
|
-
}
|
178
|
-
|
179
|
-
// Shutdown all client connections
|
180
|
-
{
|
181
|
-
std::lock_guard<std::mutex> lock(m_clientsMutex);
|
182
|
-
for (SOCKET clientSocket : m_clientSockets) {
|
183
|
-
if (clientSocket != INVALID_SOCKET) {
|
184
|
-
shutdown(clientSocket, SD_BOTH);
|
185
|
-
}
|
186
|
-
}
|
187
|
-
}
|
188
|
-
|
189
|
-
// STEP 2: Now stopReading should complete quickly since sockets are shutdown
|
190
|
-
stopReading();
|
191
|
-
|
192
|
-
// STEP 3: Close the sockets
|
193
|
-
if (m_socket != INVALID_SOCKET)
|
194
|
-
{
|
195
|
-
closesocket(m_socket);
|
196
|
-
m_socket = INVALID_SOCKET;
|
197
|
-
}
|
198
|
-
|
199
|
-
if (m_serverSocket != INVALID_SOCKET)
|
200
|
-
{
|
201
|
-
closesocket(m_serverSocket);
|
202
|
-
m_serverSocket = INVALID_SOCKET;
|
203
|
-
}
|
204
|
-
|
205
|
-
// Close all client connections
|
206
|
-
{
|
207
|
-
std::lock_guard<std::mutex> lock(m_clientsMutex);
|
208
|
-
for (SOCKET clientSocket : m_clientSockets)
|
209
|
-
{
|
210
|
-
closesocket(clientSocket);
|
211
|
-
}
|
212
|
-
m_clientSockets.clear();
|
213
|
-
}
|
214
|
-
|
215
|
-
m_isConnected = false;
|
216
|
-
m_isServerRunning = false;
|
217
|
-
}
|
218
|
-
|
219
|
-
bool TcpSocket::write(const std::vector<uint8_t> &data)
|
220
|
-
{
|
221
|
-
if (data.empty())
|
222
|
-
{
|
223
|
-
return false;
|
224
|
-
}
|
225
|
-
|
226
|
-
if (m_type == ConnectionType::Client && m_isConnected)
|
227
|
-
{
|
228
|
-
int result = send(m_socket, (char *)data.data(), static_cast<int>(data.size()), 0);
|
229
|
-
return result != SOCKET_ERROR;
|
230
|
-
}
|
231
|
-
else if (m_type == ConnectionType::Server && m_isServerRunning)
|
232
|
-
{
|
233
|
-
// Send to all connected clients
|
234
|
-
std::lock_guard<std::mutex> lock(m_clientsMutex);
|
235
|
-
bool success = true;
|
236
|
-
for (SOCKET clientSocket : m_clientSockets)
|
237
|
-
{
|
238
|
-
int result = send(clientSocket, (char *)data.data(), static_cast<int>(data.size()), 0);
|
239
|
-
if (result == SOCKET_ERROR)
|
240
|
-
{
|
241
|
-
success = false;
|
242
|
-
}
|
243
|
-
}
|
244
|
-
return success;
|
245
|
-
}
|
246
|
-
|
247
|
-
return false;
|
248
|
-
}
|
249
|
-
|
250
|
-
void TcpSocket::setDataReceivedCallback(DataReceivedCallback callback)
|
251
|
-
{
|
252
|
-
m_dataCallback = std::move(callback);
|
253
|
-
}
|
254
|
-
|
255
|
-
void TcpSocket::setClientConnectedCallback(ClientConnectedCallback callback)
|
256
|
-
{
|
257
|
-
m_clientConnectedCallback = std::move(callback);
|
258
|
-
}
|
259
|
-
|
260
|
-
void TcpSocket::setClientDisconnectedCallback(ClientDisconnectedCallback callback)
|
261
|
-
{
|
262
|
-
m_clientDisconnectedCallback = std::move(callback);
|
263
|
-
}
|
264
|
-
|
265
|
-
void TcpSocket::setConnectionStatusCallback(ConnectionStatusCallback callback)
|
266
|
-
{
|
267
|
-
m_connectionStatusCallback = std::move(callback);
|
268
|
-
}
|
269
|
-
|
270
|
-
void TcpSocket::startReading()
|
271
|
-
{
|
272
|
-
if (m_type == ConnectionType::Client)
|
273
|
-
{
|
274
|
-
m_readThread = std::thread(&TcpSocket::clientReadThread, this);
|
275
|
-
}
|
276
|
-
}
|
277
|
-
|
278
|
-
// CHANGE 1: Update clientReadThread() - Add socket timeout
|
279
|
-
void TcpSocket::clientReadThread()
|
280
|
-
{
|
281
|
-
std::vector<uint8_t> buffer(1024);
|
282
|
-
|
283
|
-
// Set socket timeout to prevent infinite blocking
|
284
|
-
DWORD timeout = 1000; // 1 second
|
285
|
-
setsockopt(m_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout));
|
286
|
-
|
287
|
-
while (m_isRunning && m_isConnected)
|
288
|
-
{
|
289
|
-
int bytesReceived = recv(m_socket, (char *)buffer.data(), buffer.size(), 0);
|
290
|
-
|
291
|
-
if (bytesReceived > 0)
|
292
|
-
{
|
293
|
-
if (m_dataCallback)
|
294
|
-
{
|
295
|
-
OutputDebugStringA(("TCP Client received " + std::to_string(bytesReceived) + " bytes\n").c_str());
|
296
|
-
m_dataCallback(std::vector<uint8_t>(buffer.begin(), buffer.begin() + bytesReceived));
|
297
|
-
}
|
298
|
-
}
|
299
|
-
else if (bytesReceived == 0)
|
300
|
-
{
|
301
|
-
// Connection closed by peer
|
302
|
-
m_isConnected = false;
|
303
|
-
if (m_connectionStatusCallback)
|
304
|
-
{
|
305
|
-
m_connectionStatusCallback(false, "Connection closed by peer");
|
306
|
-
}
|
307
|
-
break;
|
308
|
-
}
|
309
|
-
else
|
310
|
-
{
|
311
|
-
// Error occurred
|
312
|
-
int error = WSAGetLastError();
|
313
|
-
if (error == WSAETIMEDOUT)
|
314
|
-
{
|
315
|
-
// Timeout - continue loop to check m_isRunning
|
316
|
-
continue;
|
317
|
-
}
|
318
|
-
else if (error == WSAECONNRESET || error == WSAESHUTDOWN || error == WSAENOTCONN)
|
319
|
-
{
|
320
|
-
// Connection closed/shutdown - exit gracefully
|
321
|
-
m_isConnected = false;
|
322
|
-
OutputDebugStringA("Connection closed during recv\n");
|
323
|
-
break;
|
324
|
-
}
|
325
|
-
else if (error != WSAEWOULDBLOCK)
|
326
|
-
{
|
327
|
-
m_isConnected = false;
|
328
|
-
if (m_connectionStatusCallback)
|
329
|
-
{
|
330
|
-
m_connectionStatusCallback(false, "Receive error: " + std::to_string(error));
|
331
|
-
}
|
332
|
-
break;
|
333
|
-
}
|
334
|
-
}
|
335
|
-
}
|
336
|
-
OutputDebugStringA("Client read thread exiting\n");
|
337
|
-
}
|
338
|
-
|
339
|
-
// CHANGE 2: Update serverAcceptThread() - Add socket timeout
|
340
|
-
void TcpSocket::serverAcceptThread()
|
341
|
-
{
|
342
|
-
// Set socket timeout for accept() to prevent infinite blocking
|
343
|
-
DWORD timeout = 1000; // 1 second
|
344
|
-
setsockopt(m_serverSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout));
|
345
|
-
|
346
|
-
while (m_isRunning && m_isServerRunning)
|
347
|
-
{
|
348
|
-
sockaddr_in clientAddr = {};
|
349
|
-
int clientAddrLen = sizeof(clientAddr);
|
350
|
-
|
351
|
-
SOCKET clientSocket = accept(m_serverSocket, (sockaddr *)&clientAddr, &clientAddrLen);
|
352
|
-
|
353
|
-
if (clientSocket != INVALID_SOCKET)
|
354
|
-
{
|
355
|
-
std::string clientAddress = getSocketAddress(clientSocket);
|
356
|
-
|
357
|
-
{
|
358
|
-
std::lock_guard<std::mutex> lock(m_clientsMutex);
|
359
|
-
m_clientSockets.push_back(clientSocket);
|
360
|
-
m_clientThreads.emplace_back(&TcpSocket::serverClientReadThread, this, clientSocket, clientAddress);
|
361
|
-
}
|
362
|
-
|
363
|
-
if (m_clientConnectedCallback)
|
364
|
-
{
|
365
|
-
m_clientConnectedCallback(clientAddress);
|
366
|
-
}
|
367
|
-
|
368
|
-
OutputDebugStringA(("Client connected from: " + clientAddress + "\n").c_str());
|
369
|
-
}
|
370
|
-
else if (m_isRunning && m_isServerRunning)
|
371
|
-
{
|
372
|
-
int error = WSAGetLastError();
|
373
|
-
if (error == WSAETIMEDOUT)
|
374
|
-
{
|
375
|
-
// Timeout - continue loop to check m_isRunning
|
376
|
-
continue;
|
377
|
-
}
|
378
|
-
else if (error == WSAECONNRESET || error == WSAESHUTDOWN || error == WSAENOTCONN)
|
379
|
-
{
|
380
|
-
// Server socket closed/shutdown - exit gracefully
|
381
|
-
OutputDebugStringA("Server socket closed during accept\n");
|
382
|
-
break;
|
383
|
-
}
|
384
|
-
else if (error != WSAEINTR)
|
385
|
-
{
|
386
|
-
OutputDebugStringA(("Accept error: " + std::to_string(error) + "\n").c_str());
|
387
|
-
}
|
388
|
-
}
|
389
|
-
}
|
390
|
-
OutputDebugStringA("Server accept thread exiting\n");
|
391
|
-
}
|
392
|
-
|
393
|
-
// CHANGE 3: Update serverClientReadThread() - Add socket timeout
|
394
|
-
void TcpSocket::serverClientReadThread(SOCKET clientSocket, std::string clientAddress)
|
395
|
-
{
|
396
|
-
std::vector<uint8_t> buffer(1024);
|
397
|
-
|
398
|
-
// Set socket timeout to prevent infinite blocking
|
399
|
-
DWORD timeout = 1000; // 1 second
|
400
|
-
setsockopt(clientSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout));
|
401
|
-
|
402
|
-
while (m_isRunning && m_isServerRunning)
|
403
|
-
{
|
404
|
-
int bytesReceived = recv(clientSocket, (char *)buffer.data(), buffer.size(), 0);
|
405
|
-
|
406
|
-
if (bytesReceived > 0)
|
407
|
-
{
|
408
|
-
if (m_dataCallback)
|
409
|
-
{
|
410
|
-
OutputDebugStringA(("TCP Server received " + std::to_string(bytesReceived) + " bytes from " + clientAddress + "\n").c_str());
|
411
|
-
m_dataCallback(std::vector<uint8_t>(buffer.begin(), buffer.begin() + bytesReceived));
|
412
|
-
}
|
413
|
-
}
|
414
|
-
else if (bytesReceived == 0)
|
415
|
-
{
|
416
|
-
// Client disconnected
|
417
|
-
OutputDebugStringA(("Client disconnected normally: " + clientAddress + "\n").c_str());
|
418
|
-
break;
|
419
|
-
}
|
420
|
-
else
|
421
|
-
{
|
422
|
-
// Error occurred
|
423
|
-
int error = WSAGetLastError();
|
424
|
-
if (error == WSAETIMEDOUT)
|
425
|
-
{
|
426
|
-
// Timeout - continue loop to check m_isRunning
|
427
|
-
continue;
|
428
|
-
}
|
429
|
-
else if (error == WSAECONNRESET || error == WSAESHUTDOWN || error == WSAENOTCONN)
|
430
|
-
{
|
431
|
-
// Client connection closed/shutdown - exit gracefully
|
432
|
-
OutputDebugStringA(("Client connection closed during recv: " + clientAddress + "\n").c_str());
|
433
|
-
break;
|
434
|
-
}
|
435
|
-
else if (error != WSAEWOULDBLOCK)
|
436
|
-
{
|
437
|
-
OutputDebugStringA(("Client receive error: " + std::to_string(error) + " for " + clientAddress + "\n").c_str());
|
438
|
-
break;
|
439
|
-
}
|
440
|
-
}
|
441
|
-
}
|
442
|
-
|
443
|
-
// Remove client from list and close socket
|
444
|
-
{
|
445
|
-
std::lock_guard<std::mutex> lock(m_clientsMutex);
|
446
|
-
auto it = std::find(m_clientSockets.begin(), m_clientSockets.end(), clientSocket);
|
447
|
-
if (it != m_clientSockets.end())
|
448
|
-
{
|
449
|
-
m_clientSockets.erase(it);
|
450
|
-
}
|
451
|
-
}
|
452
|
-
|
453
|
-
closesocket(clientSocket);
|
454
|
-
|
455
|
-
if (m_clientDisconnectedCallback)
|
456
|
-
{
|
457
|
-
m_clientDisconnectedCallback(clientAddress);
|
458
|
-
}
|
459
|
-
|
460
|
-
OutputDebugStringA(("Client thread exiting: " + clientAddress + "\n").c_str());
|
461
|
-
}
|
462
|
-
|
463
|
-
// CHANGE 4: Simplify stopReading() - Remove async timeout logic (it's causing issues)
|
464
|
-
void TcpSocket::stopReading()
|
465
|
-
{
|
466
|
-
OutputDebugStringA("stopReading() called\n");
|
467
|
-
|
468
|
-
// 1. Set the stop flag
|
469
|
-
m_isRunning = false;
|
470
|
-
OutputDebugStringA("Set m_isRunning = false\n");
|
471
|
-
|
472
|
-
// 2. WAKE UP blocked threads by shutting down sockets
|
473
|
-
if (m_socket != INVALID_SOCKET)
|
474
|
-
{
|
475
|
-
OutputDebugStringA("Shutting down main socket...\n");
|
476
|
-
shutdown(m_socket, SD_BOTH);
|
477
|
-
}
|
478
|
-
|
479
|
-
if (m_serverSocket != INVALID_SOCKET)
|
480
|
-
{
|
481
|
-
OutputDebugStringA("Shutting down server socket...\n");
|
482
|
-
shutdown(m_serverSocket, SD_BOTH);
|
483
|
-
}
|
484
|
-
|
485
|
-
// Wake up client sockets
|
486
|
-
{
|
487
|
-
std::lock_guard<std::mutex> lock(m_clientsMutex);
|
488
|
-
OutputDebugStringA(("Shutting down " + std::to_string(m_clientSockets.size()) + " client sockets...\n").c_str());
|
489
|
-
for (SOCKET clientSocket : m_clientSockets)
|
490
|
-
{
|
491
|
-
shutdown(clientSocket, SD_BOTH);
|
492
|
-
}
|
493
|
-
}
|
494
|
-
|
495
|
-
// 3. Join threads with simple timeout
|
496
|
-
OutputDebugStringA("Joining read thread...\n");
|
497
|
-
if (m_readThread.joinable())
|
498
|
-
{
|
499
|
-
try
|
500
|
-
{
|
501
|
-
m_readThread.join();
|
502
|
-
OutputDebugStringA("Read thread joined successfully\n");
|
503
|
-
}
|
504
|
-
catch (...)
|
505
|
-
{
|
506
|
-
OutputDebugStringA("Read thread join failed - detaching\n");
|
507
|
-
m_readThread.detach();
|
508
|
-
}
|
509
|
-
}
|
510
|
-
|
511
|
-
OutputDebugStringA("Joining accept thread...\n");
|
512
|
-
if (m_acceptThread.joinable())
|
513
|
-
{
|
514
|
-
try
|
515
|
-
{
|
516
|
-
m_acceptThread.join();
|
517
|
-
OutputDebugStringA("Accept thread joined successfully\n");
|
518
|
-
}
|
519
|
-
catch (...)
|
520
|
-
{
|
521
|
-
OutputDebugStringA("Accept thread join failed - detaching\n");
|
522
|
-
m_acceptThread.detach();
|
523
|
-
}
|
524
|
-
}
|
525
|
-
|
526
|
-
// Join client threads
|
527
|
-
OutputDebugStringA(("Joining " + std::to_string(m_clientThreads.size()) + " client threads...\n").c_str());
|
528
|
-
for (size_t i = 0; i < m_clientThreads.size(); ++i)
|
529
|
-
{
|
530
|
-
auto &thread = m_clientThreads[i];
|
531
|
-
if (thread.joinable())
|
532
|
-
{
|
533
|
-
try
|
534
|
-
{
|
535
|
-
thread.join();
|
536
|
-
OutputDebugStringA(("Client thread " + std::to_string(i) + " joined successfully\n").c_str());
|
537
|
-
}
|
538
|
-
catch (...)
|
539
|
-
{
|
540
|
-
OutputDebugStringA(("Client thread " + std::to_string(i) + " join failed - detaching\n").c_str());
|
541
|
-
thread.detach();
|
542
|
-
}
|
543
|
-
}
|
544
|
-
}
|
545
|
-
m_clientThreads.clear();
|
546
|
-
|
547
|
-
OutputDebugStringA("stopReading() completed successfully\n");
|
548
|
-
}
|
549
|
-
|
550
|
-
std::string TcpSocket::getSocketAddress(SOCKET socket)
|
551
|
-
{
|
552
|
-
sockaddr_in addr = {};
|
553
|
-
int addrLen = sizeof(addr);
|
554
|
-
|
555
|
-
if (getpeername(socket, (sockaddr *)&addr, &addrLen) == 0)
|
556
|
-
{
|
557
|
-
char ipStr[INET_ADDRSTRLEN];
|
558
|
-
if (inet_ntop(AF_INET, &addr.sin_addr, ipStr, INET_ADDRSTRLEN))
|
559
|
-
{
|
560
|
-
return std::string(ipStr) + ":" + std::to_string(ntohs(addr.sin_port));
|
561
|
-
}
|
562
|
-
}
|
563
|
-
|
564
|
-
return "unknown";
|
1
|
+
#include "pch.h"
|
2
|
+
#include "TcpSocket.h"
|
3
|
+
#include <sstream>
|
4
|
+
|
5
|
+
bool TcpSocket::s_wsaInitialized = false;
|
6
|
+
int TcpSocket::s_wsaRefCount = 0;
|
7
|
+
std::mutex TcpSocket::s_wsaMutex;
|
8
|
+
|
9
|
+
TcpSocket::TcpSocket(const std::string &address, int port, ConnectionType type)
|
10
|
+
: m_address(address), m_port(port), m_type(type), m_socket(INVALID_SOCKET), m_serverSocket(INVALID_SOCKET), m_isRunning(false), m_isConnected(false), m_isServerRunning(false)
|
11
|
+
{
|
12
|
+
|
13
|
+
std::lock_guard<std::mutex> lock(s_wsaMutex);
|
14
|
+
if (!s_wsaInitialized)
|
15
|
+
{
|
16
|
+
WSADATA wsaData;
|
17
|
+
if (WSAStartup(MAKEWORD(2, 2), &wsaData) == 0)
|
18
|
+
{
|
19
|
+
s_wsaInitialized = true;
|
20
|
+
}
|
21
|
+
}
|
22
|
+
s_wsaRefCount++;
|
23
|
+
}
|
24
|
+
|
25
|
+
TcpSocket::~TcpSocket()
|
26
|
+
{
|
27
|
+
close();
|
28
|
+
|
29
|
+
std::lock_guard<std::mutex> lock(s_wsaMutex);
|
30
|
+
s_wsaRefCount--;
|
31
|
+
if (s_wsaRefCount == 0 && s_wsaInitialized)
|
32
|
+
{
|
33
|
+
WSACleanup();
|
34
|
+
s_wsaInitialized = false;
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
bool TcpSocket::connect()
|
39
|
+
{
|
40
|
+
if (m_type != ConnectionType::Client)
|
41
|
+
{
|
42
|
+
return false;
|
43
|
+
}
|
44
|
+
|
45
|
+
if (m_isConnected)
|
46
|
+
{
|
47
|
+
close();
|
48
|
+
}
|
49
|
+
|
50
|
+
m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
51
|
+
if (m_socket == INVALID_SOCKET)
|
52
|
+
{
|
53
|
+
if (m_connectionStatusCallback)
|
54
|
+
{
|
55
|
+
m_connectionStatusCallback(false, "Failed to create socket");
|
56
|
+
}
|
57
|
+
return false;
|
58
|
+
}
|
59
|
+
|
60
|
+
sockaddr_in serverAddr = {};
|
61
|
+
serverAddr.sin_family = AF_INET;
|
62
|
+
serverAddr.sin_port = htons(m_port);
|
63
|
+
|
64
|
+
if (inet_pton(AF_INET, m_address.c_str(), &serverAddr.sin_addr) <= 0)
|
65
|
+
{
|
66
|
+
closesocket(m_socket);
|
67
|
+
m_socket = INVALID_SOCKET;
|
68
|
+
if (m_connectionStatusCallback)
|
69
|
+
{
|
70
|
+
m_connectionStatusCallback(false, "Invalid address format");
|
71
|
+
}
|
72
|
+
return false;
|
73
|
+
}
|
74
|
+
|
75
|
+
if (::connect(m_socket, (sockaddr *)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)
|
76
|
+
{
|
77
|
+
int error = WSAGetLastError();
|
78
|
+
closesocket(m_socket);
|
79
|
+
m_socket = INVALID_SOCKET;
|
80
|
+
if (m_connectionStatusCallback)
|
81
|
+
{
|
82
|
+
m_connectionStatusCallback(false, "Connection failed. Error: " + std::to_string(error));
|
83
|
+
}
|
84
|
+
return false;
|
85
|
+
}
|
86
|
+
|
87
|
+
m_isConnected = true;
|
88
|
+
m_isRunning = true;
|
89
|
+
startReading();
|
90
|
+
|
91
|
+
if (m_connectionStatusCallback)
|
92
|
+
{
|
93
|
+
m_connectionStatusCallback(true, "Connected successfully");
|
94
|
+
}
|
95
|
+
|
96
|
+
return true;
|
97
|
+
}
|
98
|
+
|
99
|
+
bool TcpSocket::startServer()
|
100
|
+
{
|
101
|
+
if (m_type != ConnectionType::Server)
|
102
|
+
{
|
103
|
+
return false;
|
104
|
+
}
|
105
|
+
|
106
|
+
if (m_isServerRunning)
|
107
|
+
{
|
108
|
+
close();
|
109
|
+
}
|
110
|
+
|
111
|
+
m_serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
112
|
+
if (m_serverSocket == INVALID_SOCKET)
|
113
|
+
{
|
114
|
+
if (m_connectionStatusCallback)
|
115
|
+
{
|
116
|
+
m_connectionStatusCallback(false, "Failed to create server socket");
|
117
|
+
}
|
118
|
+
return false;
|
119
|
+
}
|
120
|
+
|
121
|
+
// Allow socket reuse
|
122
|
+
BOOL reuseAddr = TRUE;
|
123
|
+
setsockopt(m_serverSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&reuseAddr, sizeof(reuseAddr));
|
124
|
+
|
125
|
+
sockaddr_in serverAddr = {};
|
126
|
+
serverAddr.sin_family = AF_INET;
|
127
|
+
serverAddr.sin_port = htons(m_port);
|
128
|
+
serverAddr.sin_addr.s_addr = INADDR_ANY;
|
129
|
+
|
130
|
+
if (bind(m_serverSocket, (sockaddr *)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)
|
131
|
+
{
|
132
|
+
int error = WSAGetLastError();
|
133
|
+
closesocket(m_serverSocket);
|
134
|
+
m_serverSocket = INVALID_SOCKET;
|
135
|
+
if (m_connectionStatusCallback)
|
136
|
+
{
|
137
|
+
m_connectionStatusCallback(false, "Bind failed. Error: " + std::to_string(error));
|
138
|
+
}
|
139
|
+
return false;
|
140
|
+
}
|
141
|
+
|
142
|
+
if (listen(m_serverSocket, SOMAXCONN) == SOCKET_ERROR)
|
143
|
+
{
|
144
|
+
int error = WSAGetLastError();
|
145
|
+
closesocket(m_serverSocket);
|
146
|
+
m_serverSocket = INVALID_SOCKET;
|
147
|
+
if (m_connectionStatusCallback)
|
148
|
+
{
|
149
|
+
m_connectionStatusCallback(false, "Listen failed. Error: " + std::to_string(error));
|
150
|
+
}
|
151
|
+
return false;
|
152
|
+
}
|
153
|
+
|
154
|
+
m_isServerRunning = true;
|
155
|
+
m_isRunning = true;
|
156
|
+
|
157
|
+
// Start accepting connections
|
158
|
+
m_acceptThread = std::thread(&TcpSocket::serverAcceptThread, this);
|
159
|
+
|
160
|
+
if (m_connectionStatusCallback)
|
161
|
+
{
|
162
|
+
m_connectionStatusCallback(true, "Server started on port " + std::to_string(m_port));
|
163
|
+
}
|
164
|
+
|
165
|
+
return true;
|
166
|
+
}
|
167
|
+
|
168
|
+
void TcpSocket::close()
|
169
|
+
{
|
170
|
+
// STEP 1: Shutdown sockets FIRST to wake up any blocking operations
|
171
|
+
if (m_socket != INVALID_SOCKET) {
|
172
|
+
shutdown(m_socket, SD_BOTH); // This wakes up recv() calls
|
173
|
+
}
|
174
|
+
|
175
|
+
if (m_serverSocket != INVALID_SOCKET) {
|
176
|
+
shutdown(m_serverSocket, SD_BOTH); // This wakes up accept() calls
|
177
|
+
}
|
178
|
+
|
179
|
+
// Shutdown all client connections
|
180
|
+
{
|
181
|
+
std::lock_guard<std::mutex> lock(m_clientsMutex);
|
182
|
+
for (SOCKET clientSocket : m_clientSockets) {
|
183
|
+
if (clientSocket != INVALID_SOCKET) {
|
184
|
+
shutdown(clientSocket, SD_BOTH);
|
185
|
+
}
|
186
|
+
}
|
187
|
+
}
|
188
|
+
|
189
|
+
// STEP 2: Now stopReading should complete quickly since sockets are shutdown
|
190
|
+
stopReading();
|
191
|
+
|
192
|
+
// STEP 3: Close the sockets
|
193
|
+
if (m_socket != INVALID_SOCKET)
|
194
|
+
{
|
195
|
+
closesocket(m_socket);
|
196
|
+
m_socket = INVALID_SOCKET;
|
197
|
+
}
|
198
|
+
|
199
|
+
if (m_serverSocket != INVALID_SOCKET)
|
200
|
+
{
|
201
|
+
closesocket(m_serverSocket);
|
202
|
+
m_serverSocket = INVALID_SOCKET;
|
203
|
+
}
|
204
|
+
|
205
|
+
// Close all client connections
|
206
|
+
{
|
207
|
+
std::lock_guard<std::mutex> lock(m_clientsMutex);
|
208
|
+
for (SOCKET clientSocket : m_clientSockets)
|
209
|
+
{
|
210
|
+
closesocket(clientSocket);
|
211
|
+
}
|
212
|
+
m_clientSockets.clear();
|
213
|
+
}
|
214
|
+
|
215
|
+
m_isConnected = false;
|
216
|
+
m_isServerRunning = false;
|
217
|
+
}
|
218
|
+
|
219
|
+
bool TcpSocket::write(const std::vector<uint8_t> &data)
|
220
|
+
{
|
221
|
+
if (data.empty())
|
222
|
+
{
|
223
|
+
return false;
|
224
|
+
}
|
225
|
+
|
226
|
+
if (m_type == ConnectionType::Client && m_isConnected)
|
227
|
+
{
|
228
|
+
int result = send(m_socket, (char *)data.data(), static_cast<int>(data.size()), 0);
|
229
|
+
return result != SOCKET_ERROR;
|
230
|
+
}
|
231
|
+
else if (m_type == ConnectionType::Server && m_isServerRunning)
|
232
|
+
{
|
233
|
+
// Send to all connected clients
|
234
|
+
std::lock_guard<std::mutex> lock(m_clientsMutex);
|
235
|
+
bool success = true;
|
236
|
+
for (SOCKET clientSocket : m_clientSockets)
|
237
|
+
{
|
238
|
+
int result = send(clientSocket, (char *)data.data(), static_cast<int>(data.size()), 0);
|
239
|
+
if (result == SOCKET_ERROR)
|
240
|
+
{
|
241
|
+
success = false;
|
242
|
+
}
|
243
|
+
}
|
244
|
+
return success;
|
245
|
+
}
|
246
|
+
|
247
|
+
return false;
|
248
|
+
}
|
249
|
+
|
250
|
+
void TcpSocket::setDataReceivedCallback(DataReceivedCallback callback)
|
251
|
+
{
|
252
|
+
m_dataCallback = std::move(callback);
|
253
|
+
}
|
254
|
+
|
255
|
+
void TcpSocket::setClientConnectedCallback(ClientConnectedCallback callback)
|
256
|
+
{
|
257
|
+
m_clientConnectedCallback = std::move(callback);
|
258
|
+
}
|
259
|
+
|
260
|
+
void TcpSocket::setClientDisconnectedCallback(ClientDisconnectedCallback callback)
|
261
|
+
{
|
262
|
+
m_clientDisconnectedCallback = std::move(callback);
|
263
|
+
}
|
264
|
+
|
265
|
+
void TcpSocket::setConnectionStatusCallback(ConnectionStatusCallback callback)
|
266
|
+
{
|
267
|
+
m_connectionStatusCallback = std::move(callback);
|
268
|
+
}
|
269
|
+
|
270
|
+
void TcpSocket::startReading()
|
271
|
+
{
|
272
|
+
if (m_type == ConnectionType::Client)
|
273
|
+
{
|
274
|
+
m_readThread = std::thread(&TcpSocket::clientReadThread, this);
|
275
|
+
}
|
276
|
+
}
|
277
|
+
|
278
|
+
// CHANGE 1: Update clientReadThread() - Add socket timeout
|
279
|
+
void TcpSocket::clientReadThread()
|
280
|
+
{
|
281
|
+
std::vector<uint8_t> buffer(1024);
|
282
|
+
|
283
|
+
// Set socket timeout to prevent infinite blocking
|
284
|
+
DWORD timeout = 1000; // 1 second
|
285
|
+
setsockopt(m_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout));
|
286
|
+
|
287
|
+
while (m_isRunning && m_isConnected)
|
288
|
+
{
|
289
|
+
int bytesReceived = recv(m_socket, (char *)buffer.data(), buffer.size(), 0);
|
290
|
+
|
291
|
+
if (bytesReceived > 0)
|
292
|
+
{
|
293
|
+
if (m_dataCallback)
|
294
|
+
{
|
295
|
+
OutputDebugStringA(("TCP Client received " + std::to_string(bytesReceived) + " bytes\n").c_str());
|
296
|
+
m_dataCallback(std::vector<uint8_t>(buffer.begin(), buffer.begin() + bytesReceived));
|
297
|
+
}
|
298
|
+
}
|
299
|
+
else if (bytesReceived == 0)
|
300
|
+
{
|
301
|
+
// Connection closed by peer
|
302
|
+
m_isConnected = false;
|
303
|
+
if (m_connectionStatusCallback)
|
304
|
+
{
|
305
|
+
m_connectionStatusCallback(false, "Connection closed by peer");
|
306
|
+
}
|
307
|
+
break;
|
308
|
+
}
|
309
|
+
else
|
310
|
+
{
|
311
|
+
// Error occurred
|
312
|
+
int error = WSAGetLastError();
|
313
|
+
if (error == WSAETIMEDOUT)
|
314
|
+
{
|
315
|
+
// Timeout - continue loop to check m_isRunning
|
316
|
+
continue;
|
317
|
+
}
|
318
|
+
else if (error == WSAECONNRESET || error == WSAESHUTDOWN || error == WSAENOTCONN)
|
319
|
+
{
|
320
|
+
// Connection closed/shutdown - exit gracefully
|
321
|
+
m_isConnected = false;
|
322
|
+
OutputDebugStringA("Connection closed during recv\n");
|
323
|
+
break;
|
324
|
+
}
|
325
|
+
else if (error != WSAEWOULDBLOCK)
|
326
|
+
{
|
327
|
+
m_isConnected = false;
|
328
|
+
if (m_connectionStatusCallback)
|
329
|
+
{
|
330
|
+
m_connectionStatusCallback(false, "Receive error: " + std::to_string(error));
|
331
|
+
}
|
332
|
+
break;
|
333
|
+
}
|
334
|
+
}
|
335
|
+
}
|
336
|
+
OutputDebugStringA("Client read thread exiting\n");
|
337
|
+
}
|
338
|
+
|
339
|
+
// CHANGE 2: Update serverAcceptThread() - Add socket timeout
|
340
|
+
void TcpSocket::serverAcceptThread()
|
341
|
+
{
|
342
|
+
// Set socket timeout for accept() to prevent infinite blocking
|
343
|
+
DWORD timeout = 1000; // 1 second
|
344
|
+
setsockopt(m_serverSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout));
|
345
|
+
|
346
|
+
while (m_isRunning && m_isServerRunning)
|
347
|
+
{
|
348
|
+
sockaddr_in clientAddr = {};
|
349
|
+
int clientAddrLen = sizeof(clientAddr);
|
350
|
+
|
351
|
+
SOCKET clientSocket = accept(m_serverSocket, (sockaddr *)&clientAddr, &clientAddrLen);
|
352
|
+
|
353
|
+
if (clientSocket != INVALID_SOCKET)
|
354
|
+
{
|
355
|
+
std::string clientAddress = getSocketAddress(clientSocket);
|
356
|
+
|
357
|
+
{
|
358
|
+
std::lock_guard<std::mutex> lock(m_clientsMutex);
|
359
|
+
m_clientSockets.push_back(clientSocket);
|
360
|
+
m_clientThreads.emplace_back(&TcpSocket::serverClientReadThread, this, clientSocket, clientAddress);
|
361
|
+
}
|
362
|
+
|
363
|
+
if (m_clientConnectedCallback)
|
364
|
+
{
|
365
|
+
m_clientConnectedCallback(clientAddress);
|
366
|
+
}
|
367
|
+
|
368
|
+
OutputDebugStringA(("Client connected from: " + clientAddress + "\n").c_str());
|
369
|
+
}
|
370
|
+
else if (m_isRunning && m_isServerRunning)
|
371
|
+
{
|
372
|
+
int error = WSAGetLastError();
|
373
|
+
if (error == WSAETIMEDOUT)
|
374
|
+
{
|
375
|
+
// Timeout - continue loop to check m_isRunning
|
376
|
+
continue;
|
377
|
+
}
|
378
|
+
else if (error == WSAECONNRESET || error == WSAESHUTDOWN || error == WSAENOTCONN)
|
379
|
+
{
|
380
|
+
// Server socket closed/shutdown - exit gracefully
|
381
|
+
OutputDebugStringA("Server socket closed during accept\n");
|
382
|
+
break;
|
383
|
+
}
|
384
|
+
else if (error != WSAEINTR)
|
385
|
+
{
|
386
|
+
OutputDebugStringA(("Accept error: " + std::to_string(error) + "\n").c_str());
|
387
|
+
}
|
388
|
+
}
|
389
|
+
}
|
390
|
+
OutputDebugStringA("Server accept thread exiting\n");
|
391
|
+
}
|
392
|
+
|
393
|
+
// CHANGE 3: Update serverClientReadThread() - Add socket timeout
|
394
|
+
void TcpSocket::serverClientReadThread(SOCKET clientSocket, std::string clientAddress)
|
395
|
+
{
|
396
|
+
std::vector<uint8_t> buffer(1024);
|
397
|
+
|
398
|
+
// Set socket timeout to prevent infinite blocking
|
399
|
+
DWORD timeout = 1000; // 1 second
|
400
|
+
setsockopt(clientSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout));
|
401
|
+
|
402
|
+
while (m_isRunning && m_isServerRunning)
|
403
|
+
{
|
404
|
+
int bytesReceived = recv(clientSocket, (char *)buffer.data(), buffer.size(), 0);
|
405
|
+
|
406
|
+
if (bytesReceived > 0)
|
407
|
+
{
|
408
|
+
if (m_dataCallback)
|
409
|
+
{
|
410
|
+
OutputDebugStringA(("TCP Server received " + std::to_string(bytesReceived) + " bytes from " + clientAddress + "\n").c_str());
|
411
|
+
m_dataCallback(std::vector<uint8_t>(buffer.begin(), buffer.begin() + bytesReceived));
|
412
|
+
}
|
413
|
+
}
|
414
|
+
else if (bytesReceived == 0)
|
415
|
+
{
|
416
|
+
// Client disconnected
|
417
|
+
OutputDebugStringA(("Client disconnected normally: " + clientAddress + "\n").c_str());
|
418
|
+
break;
|
419
|
+
}
|
420
|
+
else
|
421
|
+
{
|
422
|
+
// Error occurred
|
423
|
+
int error = WSAGetLastError();
|
424
|
+
if (error == WSAETIMEDOUT)
|
425
|
+
{
|
426
|
+
// Timeout - continue loop to check m_isRunning
|
427
|
+
continue;
|
428
|
+
}
|
429
|
+
else if (error == WSAECONNRESET || error == WSAESHUTDOWN || error == WSAENOTCONN)
|
430
|
+
{
|
431
|
+
// Client connection closed/shutdown - exit gracefully
|
432
|
+
OutputDebugStringA(("Client connection closed during recv: " + clientAddress + "\n").c_str());
|
433
|
+
break;
|
434
|
+
}
|
435
|
+
else if (error != WSAEWOULDBLOCK)
|
436
|
+
{
|
437
|
+
OutputDebugStringA(("Client receive error: " + std::to_string(error) + " for " + clientAddress + "\n").c_str());
|
438
|
+
break;
|
439
|
+
}
|
440
|
+
}
|
441
|
+
}
|
442
|
+
|
443
|
+
// Remove client from list and close socket
|
444
|
+
{
|
445
|
+
std::lock_guard<std::mutex> lock(m_clientsMutex);
|
446
|
+
auto it = std::find(m_clientSockets.begin(), m_clientSockets.end(), clientSocket);
|
447
|
+
if (it != m_clientSockets.end())
|
448
|
+
{
|
449
|
+
m_clientSockets.erase(it);
|
450
|
+
}
|
451
|
+
}
|
452
|
+
|
453
|
+
closesocket(clientSocket);
|
454
|
+
|
455
|
+
if (m_clientDisconnectedCallback)
|
456
|
+
{
|
457
|
+
m_clientDisconnectedCallback(clientAddress);
|
458
|
+
}
|
459
|
+
|
460
|
+
OutputDebugStringA(("Client thread exiting: " + clientAddress + "\n").c_str());
|
461
|
+
}
|
462
|
+
|
463
|
+
// CHANGE 4: Simplify stopReading() - Remove async timeout logic (it's causing issues)
|
464
|
+
void TcpSocket::stopReading()
|
465
|
+
{
|
466
|
+
OutputDebugStringA("stopReading() called\n");
|
467
|
+
|
468
|
+
// 1. Set the stop flag
|
469
|
+
m_isRunning = false;
|
470
|
+
OutputDebugStringA("Set m_isRunning = false\n");
|
471
|
+
|
472
|
+
// 2. WAKE UP blocked threads by shutting down sockets
|
473
|
+
if (m_socket != INVALID_SOCKET)
|
474
|
+
{
|
475
|
+
OutputDebugStringA("Shutting down main socket...\n");
|
476
|
+
shutdown(m_socket, SD_BOTH);
|
477
|
+
}
|
478
|
+
|
479
|
+
if (m_serverSocket != INVALID_SOCKET)
|
480
|
+
{
|
481
|
+
OutputDebugStringA("Shutting down server socket...\n");
|
482
|
+
shutdown(m_serverSocket, SD_BOTH);
|
483
|
+
}
|
484
|
+
|
485
|
+
// Wake up client sockets
|
486
|
+
{
|
487
|
+
std::lock_guard<std::mutex> lock(m_clientsMutex);
|
488
|
+
OutputDebugStringA(("Shutting down " + std::to_string(m_clientSockets.size()) + " client sockets...\n").c_str());
|
489
|
+
for (SOCKET clientSocket : m_clientSockets)
|
490
|
+
{
|
491
|
+
shutdown(clientSocket, SD_BOTH);
|
492
|
+
}
|
493
|
+
}
|
494
|
+
|
495
|
+
// 3. Join threads with simple timeout
|
496
|
+
OutputDebugStringA("Joining read thread...\n");
|
497
|
+
if (m_readThread.joinable())
|
498
|
+
{
|
499
|
+
try
|
500
|
+
{
|
501
|
+
m_readThread.join();
|
502
|
+
OutputDebugStringA("Read thread joined successfully\n");
|
503
|
+
}
|
504
|
+
catch (...)
|
505
|
+
{
|
506
|
+
OutputDebugStringA("Read thread join failed - detaching\n");
|
507
|
+
m_readThread.detach();
|
508
|
+
}
|
509
|
+
}
|
510
|
+
|
511
|
+
OutputDebugStringA("Joining accept thread...\n");
|
512
|
+
if (m_acceptThread.joinable())
|
513
|
+
{
|
514
|
+
try
|
515
|
+
{
|
516
|
+
m_acceptThread.join();
|
517
|
+
OutputDebugStringA("Accept thread joined successfully\n");
|
518
|
+
}
|
519
|
+
catch (...)
|
520
|
+
{
|
521
|
+
OutputDebugStringA("Accept thread join failed - detaching\n");
|
522
|
+
m_acceptThread.detach();
|
523
|
+
}
|
524
|
+
}
|
525
|
+
|
526
|
+
// Join client threads
|
527
|
+
OutputDebugStringA(("Joining " + std::to_string(m_clientThreads.size()) + " client threads...\n").c_str());
|
528
|
+
for (size_t i = 0; i < m_clientThreads.size(); ++i)
|
529
|
+
{
|
530
|
+
auto &thread = m_clientThreads[i];
|
531
|
+
if (thread.joinable())
|
532
|
+
{
|
533
|
+
try
|
534
|
+
{
|
535
|
+
thread.join();
|
536
|
+
OutputDebugStringA(("Client thread " + std::to_string(i) + " joined successfully\n").c_str());
|
537
|
+
}
|
538
|
+
catch (...)
|
539
|
+
{
|
540
|
+
OutputDebugStringA(("Client thread " + std::to_string(i) + " join failed - detaching\n").c_str());
|
541
|
+
thread.detach();
|
542
|
+
}
|
543
|
+
}
|
544
|
+
}
|
545
|
+
m_clientThreads.clear();
|
546
|
+
|
547
|
+
OutputDebugStringA("stopReading() completed successfully\n");
|
548
|
+
}
|
549
|
+
|
550
|
+
std::string TcpSocket::getSocketAddress(SOCKET socket)
|
551
|
+
{
|
552
|
+
sockaddr_in addr = {};
|
553
|
+
int addrLen = sizeof(addr);
|
554
|
+
|
555
|
+
if (getpeername(socket, (sockaddr *)&addr, &addrLen) == 0)
|
556
|
+
{
|
557
|
+
char ipStr[INET_ADDRSTRLEN];
|
558
|
+
if (inet_ntop(AF_INET, &addr.sin_addr, ipStr, INET_ADDRSTRLEN))
|
559
|
+
{
|
560
|
+
return std::string(ipStr) + ":" + std::to_string(ntohs(addr.sin_port));
|
561
|
+
}
|
562
|
+
}
|
563
|
+
|
564
|
+
return "unknown";
|
565
565
|
}
|