react-native-tcp-windows 0.2.0
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/LICENSE +20 -0
- package/README.md +145 -0
- package/lib/commonjs/ReactNativeTcpWindows.js +9 -0
- package/lib/commonjs/ReactNativeTcpWindows.js.map +1 -0
- package/lib/commonjs/index.js +124 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/module/ReactNativeTcpWindows.js +5 -0
- package/lib/module/ReactNativeTcpWindows.js.map +1 -0
- package/lib/module/index.js +104 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/typescript/commonjs/package.json +1 -0
- package/lib/typescript/commonjs/src/ReactNativeTcpWindows.d.ts +13 -0
- package/lib/typescript/commonjs/src/ReactNativeTcpWindows.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/__tests__/index.test.d.ts +1 -0
- package/lib/typescript/commonjs/src/__tests__/index.test.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/index.d.ts +80 -0
- package/lib/typescript/commonjs/src/index.d.ts.map +1 -0
- package/lib/typescript/module/package.json +1 -0
- package/lib/typescript/module/src/ReactNativeTcpWindows.d.ts +13 -0
- package/lib/typescript/module/src/ReactNativeTcpWindows.d.ts.map +1 -0
- package/lib/typescript/module/src/__tests__/index.test.d.ts +1 -0
- package/lib/typescript/module/src/__tests__/index.test.d.ts.map +1 -0
- package/lib/typescript/module/src/index.d.ts +80 -0
- package/lib/typescript/module/src/index.d.ts.map +1 -0
- package/package.json +204 -0
- package/react-native.config.js +12 -0
- package/src/ReactNativeTcpWindows.ts +14 -0
- package/src/__tests__/index.test.tsx +1 -0
- package/src/index.tsx +124 -0
- package/windows/ExperimentalFeatures.props +33 -0
- package/windows/ReactNativeTcpWindows/ReactNativeTcpWindows.cpp +206 -0
- package/windows/ReactNativeTcpWindows/ReactNativeTcpWindows.def +3 -0
- package/windows/ReactNativeTcpWindows/ReactNativeTcpWindows.h +83 -0
- package/windows/ReactNativeTcpWindows/ReactNativeTcpWindows.rc +0 -0
- package/windows/ReactNativeTcpWindows/ReactNativeTcpWindows.vcxproj +146 -0
- package/windows/ReactNativeTcpWindows/ReactNativeTcpWindows.vcxproj.filters +44 -0
- package/windows/ReactNativeTcpWindows/ReactPackageProvider.cpp +20 -0
- package/windows/ReactNativeTcpWindows/ReactPackageProvider.h +24 -0
- package/windows/ReactNativeTcpWindows/ReactPackageProvider.idl +9 -0
- package/windows/ReactNativeTcpWindows/TcpSocket.cpp +565 -0
- package/windows/ReactNativeTcpWindows/TcpSocket.h +69 -0
- package/windows/ReactNativeTcpWindows/codegen/.clang-format +2 -0
- package/windows/ReactNativeTcpWindows/codegen/NativeReactNativeTcpWindowsSpec.g.h +71 -0
- package/windows/ReactNativeTcpWindows/packages.lock.json +60 -0
- package/windows/ReactNativeTcpWindows/pch.cpp +1 -0
- package/windows/ReactNativeTcpWindows/pch.h +30 -0
- package/windows/ReactNativeTcpWindows/resource.h +5 -0
- package/windows/ReactNativeTcpWindows/targetver.h +8 -0
- package/windows/ReactNativeTcpWindows.sln +43 -0
@@ -0,0 +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";
|
565
|
+
}
|
@@ -0,0 +1,69 @@
|
|
1
|
+
#pragma once
|
2
|
+
#include "pch.h"
|
3
|
+
#include <string>
|
4
|
+
#include <WinSock2.h>
|
5
|
+
#include <WS2tcpip.h>
|
6
|
+
#include <functional>
|
7
|
+
#include <thread>
|
8
|
+
#include <atomic>
|
9
|
+
#include <mutex>
|
10
|
+
|
11
|
+
#pragma comment(lib, "ws2_32.lib")
|
12
|
+
|
13
|
+
class TcpSocket {
|
14
|
+
public:
|
15
|
+
enum class ConnectionType { Client = 0, Server = 1 };
|
16
|
+
|
17
|
+
using DataReceivedCallback = std::function<void(const std::vector<uint8_t>&)>;
|
18
|
+
using ClientConnectedCallback = std::function<void(const std::string&)>;
|
19
|
+
using ClientDisconnectedCallback = std::function<void(const std::string&)>;
|
20
|
+
using ConnectionStatusCallback = std::function<void(bool, const std::string&)>;
|
21
|
+
|
22
|
+
TcpSocket(const std::string& address, int port, ConnectionType type);
|
23
|
+
//TcpSocket::TcpSocket(const std::string&, int, TcpSocket::ConnectionType)
|
24
|
+
|
25
|
+
~TcpSocket();
|
26
|
+
|
27
|
+
bool connect();
|
28
|
+
bool startServer();
|
29
|
+
void close();
|
30
|
+
bool write(const std::vector<uint8_t>& data);
|
31
|
+
void setDataReceivedCallback(DataReceivedCallback callback);
|
32
|
+
void setClientConnectedCallback(ClientConnectedCallback callback);
|
33
|
+
void setClientDisconnectedCallback(ClientDisconnectedCallback callback);
|
34
|
+
void setConnectionStatusCallback(ConnectionStatusCallback callback);
|
35
|
+
bool isConnected() const { return m_isConnected; }
|
36
|
+
bool isServerRunning() const { return m_isServerRunning; }
|
37
|
+
|
38
|
+
private:
|
39
|
+
void startReading();
|
40
|
+
void clientReadThread();
|
41
|
+
void serverAcceptThread();
|
42
|
+
void serverClientReadThread(SOCKET clientSocket, std::string clientAddress);
|
43
|
+
void stopReading();
|
44
|
+
void cleanup();
|
45
|
+
std::string getSocketAddress(SOCKET socket);
|
46
|
+
|
47
|
+
std::string m_address;
|
48
|
+
int m_port;
|
49
|
+
ConnectionType m_type;
|
50
|
+
SOCKET m_socket;
|
51
|
+
SOCKET m_serverSocket;
|
52
|
+
std::atomic<bool> m_isRunning;
|
53
|
+
std::atomic<bool> m_isConnected;
|
54
|
+
std::atomic<bool> m_isServerRunning;
|
55
|
+
std::thread m_readThread;
|
56
|
+
std::thread m_acceptThread;
|
57
|
+
std::vector<std::thread> m_clientThreads;
|
58
|
+
std::vector<SOCKET> m_clientSockets;
|
59
|
+
std::mutex m_clientsMutex;
|
60
|
+
|
61
|
+
DataReceivedCallback m_dataCallback;
|
62
|
+
ClientConnectedCallback m_clientConnectedCallback;
|
63
|
+
ClientDisconnectedCallback m_clientDisconnectedCallback;
|
64
|
+
ConnectionStatusCallback m_connectionStatusCallback;
|
65
|
+
|
66
|
+
static bool s_wsaInitialized;
|
67
|
+
static int s_wsaRefCount;
|
68
|
+
static std::mutex s_wsaMutex;
|
69
|
+
};
|
@@ -0,0 +1,71 @@
|
|
1
|
+
|
2
|
+
/*
|
3
|
+
* This file is auto-generated from a NativeModule spec file in js.
|
4
|
+
*
|
5
|
+
* This is a C++ Spec class that should be used with MakeTurboModuleProvider to register native modules
|
6
|
+
* in a way that also verifies at compile time that the native module matches the interface required
|
7
|
+
* by the TurboModule JS spec.
|
8
|
+
*/
|
9
|
+
#pragma once
|
10
|
+
// clang-format off
|
11
|
+
|
12
|
+
#include <NativeModules.h>
|
13
|
+
#include <tuple>
|
14
|
+
|
15
|
+
namespace ReactNativeTcpWindowsCodegen {
|
16
|
+
|
17
|
+
|
18
|
+
struct ReactNativeTcpWindowsSpec : winrt::Microsoft::ReactNative::TurboModuleSpec {
|
19
|
+
static constexpr auto methods = std::tuple{
|
20
|
+
Method<void(std::string, double, Promise<std::string>) noexcept>{0, L"connectToServer"},
|
21
|
+
Method<void(double, Promise<std::string>) noexcept>{1, L"startServer"},
|
22
|
+
Method<void(Promise<std::string>) noexcept>{2, L"closeConnection"},
|
23
|
+
Method<void(std::vector<double>, Promise<bool>) noexcept>{3, L"write"},
|
24
|
+
Method<void(Promise<bool>) noexcept>{4, L"getConnectionStatus"},
|
25
|
+
Method<void(std::string) noexcept>{5, L"addListener"},
|
26
|
+
Method<void(double) noexcept>{6, L"removeListeners"},
|
27
|
+
};
|
28
|
+
|
29
|
+
template <class TModule>
|
30
|
+
static constexpr void ValidateModule() noexcept {
|
31
|
+
constexpr auto methodCheckResults = CheckMethods<TModule, ReactNativeTcpWindowsSpec>();
|
32
|
+
|
33
|
+
REACT_SHOW_METHOD_SPEC_ERRORS(
|
34
|
+
0,
|
35
|
+
"connectToServer",
|
36
|
+
" REACT_METHOD(connectToServer) void connectToServer(std::string address, double port, ::React::ReactPromise<std::string> &&result) noexcept { /* implementation */ }\n"
|
37
|
+
" REACT_METHOD(connectToServer) static void connectToServer(std::string address, double port, ::React::ReactPromise<std::string> &&result) noexcept { /* implementation */ }\n");
|
38
|
+
REACT_SHOW_METHOD_SPEC_ERRORS(
|
39
|
+
1,
|
40
|
+
"startServer",
|
41
|
+
" REACT_METHOD(startServer) void startServer(double port, ::React::ReactPromise<std::string> &&result) noexcept { /* implementation */ }\n"
|
42
|
+
" REACT_METHOD(startServer) static void startServer(double port, ::React::ReactPromise<std::string> &&result) noexcept { /* implementation */ }\n");
|
43
|
+
REACT_SHOW_METHOD_SPEC_ERRORS(
|
44
|
+
2,
|
45
|
+
"closeConnection",
|
46
|
+
" REACT_METHOD(closeConnection) void closeConnection(::React::ReactPromise<std::string> &&result) noexcept { /* implementation */ }\n"
|
47
|
+
" REACT_METHOD(closeConnection) static void closeConnection(::React::ReactPromise<std::string> &&result) noexcept { /* implementation */ }\n");
|
48
|
+
REACT_SHOW_METHOD_SPEC_ERRORS(
|
49
|
+
3,
|
50
|
+
"write",
|
51
|
+
" REACT_METHOD(write) void write(std::vector<double> const & data, ::React::ReactPromise<bool> &&result) noexcept { /* implementation */ }\n"
|
52
|
+
" REACT_METHOD(write) static void write(std::vector<double> const & data, ::React::ReactPromise<bool> &&result) noexcept { /* implementation */ }\n");
|
53
|
+
REACT_SHOW_METHOD_SPEC_ERRORS(
|
54
|
+
4,
|
55
|
+
"getConnectionStatus",
|
56
|
+
" REACT_METHOD(getConnectionStatus) void getConnectionStatus(::React::ReactPromise<bool> &&result) noexcept { /* implementation */ }\n"
|
57
|
+
" REACT_METHOD(getConnectionStatus) static void getConnectionStatus(::React::ReactPromise<bool> &&result) noexcept { /* implementation */ }\n");
|
58
|
+
REACT_SHOW_METHOD_SPEC_ERRORS(
|
59
|
+
5,
|
60
|
+
"addListener",
|
61
|
+
" REACT_METHOD(addListener) void addListener(std::string eventType) noexcept { /* implementation */ }\n"
|
62
|
+
" REACT_METHOD(addListener) static void addListener(std::string eventType) noexcept { /* implementation */ }\n");
|
63
|
+
REACT_SHOW_METHOD_SPEC_ERRORS(
|
64
|
+
6,
|
65
|
+
"removeListeners",
|
66
|
+
" REACT_METHOD(removeListeners) void removeListeners(double count) noexcept { /* implementation */ }\n"
|
67
|
+
" REACT_METHOD(removeListeners) static void removeListeners(double count) noexcept { /* implementation */ }\n");
|
68
|
+
}
|
69
|
+
};
|
70
|
+
|
71
|
+
} // namespace ReactNativeTcpWindowsCodegen
|