librats 0.7.2 → 0.9.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/README.md +9 -4
- package/lib/index.d.ts +9 -22
- package/native-src/CMakeLists.txt +132 -50
- package/native-src/cmake/ratsConfig.cmake.in +8 -0
- package/native-src/src/bt_network.cpp +296 -183
- package/native-src/src/bt_network.h +25 -5
- package/native-src/src/crypto/sha256.c +1 -1
- package/native-src/src/dht.cpp +297 -47
- package/native-src/src/dht.h +70 -6
- package/native-src/src/file_transfer.cpp +1185 -1578
- package/native-src/src/file_transfer.h +240 -521
- package/native-src/src/io_poller.cpp +917 -0
- package/native-src/src/io_poller.h +138 -0
- package/native-src/src/krpc.cpp +161 -96
- package/native-src/src/krpc.h +18 -4
- package/native-src/src/librats.cpp +812 -1236
- package/native-src/src/librats.h +207 -208
- package/native-src/src/librats_bittorrent.cpp +1 -5
- package/native-src/src/librats_c.cpp +22 -39
- package/native-src/src/librats_discovery.cpp +377 -0
- package/native-src/src/librats_encryption.cpp +130 -283
- package/native-src/src/librats_file_transfer.cpp +27 -109
- package/native-src/src/librats_gossipsub.cpp +1 -5
- package/native-src/src/librats_ice.cpp +5 -1
- package/native-src/src/librats_log_macros.h +36 -0
- package/native-src/src/librats_logging.cpp +1 -7
- package/native-src/src/librats_mdns.cpp +1 -11
- package/native-src/src/librats_persistence.cpp +1 -11
- package/native-src/src/librats_reconnection.cpp +2 -13
- package/native-src/src/librats_statistic.cpp +105 -0
- package/native-src/src/socket.cpp +15 -3
- package/package.json +1 -1
- package/scripts/build-librats.js +3 -0
- package/scripts/prepare-package.js +10 -0
- package/src/librats_node.cpp +53 -64
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
#include <winsock2.h>
|
|
10
10
|
#include <ws2tcpip.h>
|
|
11
11
|
#else
|
|
12
|
-
#include <sys/select.h>
|
|
13
12
|
#include <netinet/tcp.h>
|
|
14
13
|
#include <fcntl.h>
|
|
15
14
|
#include <errno.h>
|
|
@@ -38,6 +37,21 @@ BtNetworkManager::~BtNetworkManager() {
|
|
|
38
37
|
stop();
|
|
39
38
|
}
|
|
40
39
|
|
|
40
|
+
//=============================================================================
|
|
41
|
+
// TCP_NODELAY helper
|
|
42
|
+
//=============================================================================
|
|
43
|
+
|
|
44
|
+
bool BtNetworkManager::set_tcp_nodelay(socket_t sock) {
|
|
45
|
+
int flag = 1;
|
|
46
|
+
int result = setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
|
|
47
|
+
reinterpret_cast<const char*>(&flag), sizeof(flag));
|
|
48
|
+
if (result < 0) {
|
|
49
|
+
LOG_NET_WARN("Failed to set TCP_NODELAY on socket " + std::to_string(static_cast<int>(sock)));
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
|
|
41
55
|
//=============================================================================
|
|
42
56
|
// Lifecycle
|
|
43
57
|
//=============================================================================
|
|
@@ -51,6 +65,14 @@ bool BtNetworkManager::start() {
|
|
|
51
65
|
return false;
|
|
52
66
|
}
|
|
53
67
|
|
|
68
|
+
// Create the I/O poller
|
|
69
|
+
poller_ = IOPoller::create();
|
|
70
|
+
if (!poller_) {
|
|
71
|
+
LOG_NET_ERROR("Failed to create I/O poller");
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
LOG_NET_INFO("Using I/O backend: " + std::string(poller_->name()));
|
|
75
|
+
|
|
54
76
|
// Create listen socket if incoming enabled
|
|
55
77
|
if (config_.enable_incoming) {
|
|
56
78
|
listen_socket_ = create_tcp_server(config_.listen_port, 50);
|
|
@@ -74,6 +96,10 @@ bool BtNetworkManager::start() {
|
|
|
74
96
|
actual_listen_port_ = config_.listen_port;
|
|
75
97
|
}
|
|
76
98
|
|
|
99
|
+
// Register listen socket with poller (interested in incoming connections)
|
|
100
|
+
poller_->add(listen_socket_, PollIn);
|
|
101
|
+
poller_state_[listen_socket_] = PollIn;
|
|
102
|
+
|
|
77
103
|
LOG_NET_INFO("Listening on port " + std::to_string(actual_listen_port_));
|
|
78
104
|
}
|
|
79
105
|
|
|
@@ -109,12 +135,14 @@ void BtNetworkManager::stop() {
|
|
|
109
135
|
event.connection = ctx.connection;
|
|
110
136
|
disconnected_events.push_back(std::move(event));
|
|
111
137
|
}
|
|
138
|
+
poller_->remove(socket);
|
|
112
139
|
close_socket(socket, true);
|
|
113
140
|
}
|
|
114
141
|
connections_.clear();
|
|
115
142
|
|
|
116
143
|
// Close connecting sockets
|
|
117
144
|
for (auto& [socket, pending] : connecting_) {
|
|
145
|
+
poller_->remove(socket);
|
|
118
146
|
close_socket(socket, true);
|
|
119
147
|
}
|
|
120
148
|
connecting_.clear();
|
|
@@ -126,10 +154,14 @@ void BtNetworkManager::stop() {
|
|
|
126
154
|
|
|
127
155
|
// Close listen socket
|
|
128
156
|
if (is_valid_socket(listen_socket_)) {
|
|
157
|
+
poller_->remove(listen_socket_);
|
|
129
158
|
close_socket(listen_socket_);
|
|
130
159
|
listen_socket_ = INVALID_SOCKET_VALUE;
|
|
131
160
|
}
|
|
132
161
|
|
|
162
|
+
poller_state_.clear();
|
|
163
|
+
poller_.reset();
|
|
164
|
+
|
|
133
165
|
LOG_NET_INFO("Network manager stopped");
|
|
134
166
|
}
|
|
135
167
|
|
|
@@ -285,8 +317,22 @@ bool BtNetworkManager::send_to_peer(socket_t socket, const std::vector<uint8_t>&
|
|
|
285
317
|
return false;
|
|
286
318
|
}
|
|
287
319
|
|
|
320
|
+
bool was_empty = send_buf.empty();
|
|
321
|
+
|
|
288
322
|
// Append directly to connection's send buffer (single source of truth)
|
|
289
323
|
send_buf.append(data.data(), data.size());
|
|
324
|
+
|
|
325
|
+
// If buffer was empty, we need to start watching for writability.
|
|
326
|
+
// Update poller to add PollOut interest.
|
|
327
|
+
if (was_empty && poller_) {
|
|
328
|
+
uint32_t desired = PollIn | PollOut;
|
|
329
|
+
auto ps = poller_state_.find(socket);
|
|
330
|
+
if (ps != poller_state_.end() && ps->second != desired) {
|
|
331
|
+
poller_->modify(socket, desired);
|
|
332
|
+
ps->second = desired;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
290
336
|
return true;
|
|
291
337
|
}
|
|
292
338
|
|
|
@@ -300,6 +346,45 @@ size_t BtNetworkManager::pending_connect_count() const {
|
|
|
300
346
|
return pending_connects_.size() + connecting_.size();
|
|
301
347
|
}
|
|
302
348
|
|
|
349
|
+
//=============================================================================
|
|
350
|
+
// Poller State Sync
|
|
351
|
+
//=============================================================================
|
|
352
|
+
|
|
353
|
+
void BtNetworkManager::sync_poller() {
|
|
354
|
+
// Called under mutex. Ensures poller registrations match current state.
|
|
355
|
+
// This is the primary sync point for connections whose send buffer
|
|
356
|
+
// state may have changed (e.g., after flush_send_buffer drains it).
|
|
357
|
+
|
|
358
|
+
for (auto& [socket, ctx] : connections_) {
|
|
359
|
+
// Determine desired events
|
|
360
|
+
uint32_t desired = PollIn; // Always interested in reading
|
|
361
|
+
if (ctx.connection && !ctx.connection->send_buffer().empty()) {
|
|
362
|
+
desired |= PollOut; // Also interested in writing
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
auto ps = poller_state_.find(socket);
|
|
366
|
+
if (ps == poller_state_.end()) {
|
|
367
|
+
// Not yet registered (shouldn't happen normally, but be safe)
|
|
368
|
+
poller_->add(socket, desired);
|
|
369
|
+
poller_state_[socket] = desired;
|
|
370
|
+
} else if (ps->second != desired) {
|
|
371
|
+
poller_->modify(socket, desired);
|
|
372
|
+
ps->second = desired;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Connecting sockets: interested in write (connect complete) + error
|
|
377
|
+
for (auto& [socket, pending] : connecting_) {
|
|
378
|
+
auto ps = poller_state_.find(socket);
|
|
379
|
+
uint32_t desired = PollOut;
|
|
380
|
+
if (ps == poller_state_.end()) {
|
|
381
|
+
poller_->add(socket, desired);
|
|
382
|
+
poller_state_[socket] = desired;
|
|
383
|
+
}
|
|
384
|
+
// Connecting sockets don't change their interest, no modify needed
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
303
388
|
//=============================================================================
|
|
304
389
|
// I/O Loop
|
|
305
390
|
//=============================================================================
|
|
@@ -312,89 +397,37 @@ void BtNetworkManager::io_loop() {
|
|
|
312
397
|
std::vector<DataEvent> data_events;
|
|
313
398
|
std::vector<DisconnectedEvent> disconnected_events;
|
|
314
399
|
|
|
400
|
+
// Poll results buffer
|
|
401
|
+
static constexpr int MAX_POLL_EVENTS = 256;
|
|
402
|
+
PollResult poll_results[MAX_POLL_EVENTS];
|
|
403
|
+
|
|
315
404
|
while (running_) {
|
|
316
405
|
// Clear event vectors for this iteration
|
|
317
406
|
connected_events.clear();
|
|
318
407
|
data_events.clear();
|
|
319
408
|
disconnected_events.clear();
|
|
320
409
|
|
|
321
|
-
// Process pending connect queue
|
|
410
|
+
// Process pending connect queue (adds new sockets to poller)
|
|
322
411
|
process_pending_connects();
|
|
323
412
|
|
|
324
|
-
//
|
|
325
|
-
fd_set read_fds, write_fds, error_fds;
|
|
326
|
-
FD_ZERO(&read_fds);
|
|
327
|
-
FD_ZERO(&write_fds);
|
|
328
|
-
FD_ZERO(&error_fds);
|
|
329
|
-
|
|
330
|
-
socket_t max_fd = 0;
|
|
331
|
-
|
|
413
|
+
// Sync poller state (update write interest based on send buffer state)
|
|
332
414
|
{
|
|
333
415
|
std::lock_guard<std::mutex> lock(mutex_);
|
|
334
|
-
|
|
335
|
-
// Add listen socket
|
|
336
|
-
if (is_valid_socket(listen_socket_)) {
|
|
337
|
-
FD_SET(listen_socket_, &read_fds);
|
|
338
|
-
if (listen_socket_ > max_fd) max_fd = listen_socket_;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
// Add connected sockets
|
|
342
|
-
for (auto& [socket, ctx] : connections_) {
|
|
343
|
-
FD_SET(socket, &read_fds);
|
|
344
|
-
FD_SET(socket, &error_fds);
|
|
345
|
-
|
|
346
|
-
// Only monitor for write if connection has data to send
|
|
347
|
-
if (ctx.connection && !ctx.connection->send_buffer().empty()) {
|
|
348
|
-
FD_SET(socket, &write_fds);
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
if (socket > max_fd) max_fd = socket;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
// Add connecting sockets (monitor for write = connect complete)
|
|
355
|
-
for (auto& [socket, pending] : connecting_) {
|
|
356
|
-
FD_SET(socket, &write_fds);
|
|
357
|
-
FD_SET(socket, &error_fds);
|
|
358
|
-
if (socket > max_fd) max_fd = socket;
|
|
359
|
-
}
|
|
416
|
+
sync_poller();
|
|
360
417
|
}
|
|
361
418
|
|
|
362
|
-
//
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
std::this_thread::sleep_for(
|
|
366
|
-
std::chrono::milliseconds(config_.select_timeout_ms));
|
|
367
|
-
continue;
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
// Select with timeout
|
|
371
|
-
struct timeval timeout;
|
|
372
|
-
timeout.tv_sec = 0;
|
|
373
|
-
timeout.tv_usec = config_.select_timeout_ms * 1000;
|
|
419
|
+
// Wait for I/O events (NO mutex held — this is the blocking call)
|
|
420
|
+
int num_events = poller_->wait(poll_results, MAX_POLL_EVENTS,
|
|
421
|
+
config_.poll_timeout_ms);
|
|
374
422
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
if (result < 0) {
|
|
379
|
-
#ifdef _WIN32
|
|
380
|
-
int err = WSAGetLastError();
|
|
381
|
-
if (err != WSAEINTR) {
|
|
382
|
-
LOG_NET_ERROR("select() failed: " + std::to_string(err));
|
|
383
|
-
// Avoid spinning on persistent errors
|
|
384
|
-
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
|
385
|
-
}
|
|
386
|
-
#else
|
|
387
|
-
if (errno != EINTR) {
|
|
388
|
-
LOG_NET_ERROR("select() failed: " + std::string(strerror(errno)));
|
|
389
|
-
// Avoid spinning on persistent errors
|
|
390
|
-
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
|
391
|
-
}
|
|
392
|
-
#endif
|
|
423
|
+
if (num_events < 0) {
|
|
424
|
+
// Error (EINTR already filtered by poller implementations)
|
|
425
|
+
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
|
393
426
|
continue;
|
|
394
427
|
}
|
|
395
428
|
|
|
396
|
-
if (
|
|
397
|
-
// Timeout
|
|
429
|
+
if (num_events == 0) {
|
|
430
|
+
// Timeout — check for connection timeouts
|
|
398
431
|
auto now = std::chrono::steady_clock::now();
|
|
399
432
|
|
|
400
433
|
std::lock_guard<std::mutex> lock(mutex_);
|
|
@@ -410,6 +443,8 @@ void BtNetworkManager::io_loop() {
|
|
|
410
443
|
|
|
411
444
|
for (socket_t s : timed_out) {
|
|
412
445
|
LOG_NET_DEBUG("Connection timed out to " + connecting_[s].ip);
|
|
446
|
+
poller_->remove(s);
|
|
447
|
+
poller_state_.erase(s);
|
|
413
448
|
close_socket(s);
|
|
414
449
|
connecting_.erase(s);
|
|
415
450
|
}
|
|
@@ -417,103 +452,149 @@ void BtNetworkManager::io_loop() {
|
|
|
417
452
|
continue;
|
|
418
453
|
}
|
|
419
454
|
|
|
420
|
-
LOG_NET_DEBUG("
|
|
455
|
+
LOG_NET_DEBUG("I/O poll returned " + std::to_string(num_events) + " events");
|
|
421
456
|
|
|
422
|
-
//
|
|
423
|
-
if (is_valid_socket(listen_socket_) && FD_ISSET(listen_socket_, &read_fds)) {
|
|
424
|
-
accept_incoming();
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
// Handle connecting sockets
|
|
457
|
+
// Process all events under mutex
|
|
428
458
|
{
|
|
429
459
|
std::lock_guard<std::mutex> lock(mutex_);
|
|
430
460
|
|
|
431
|
-
std::vector<socket_t>
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
461
|
+
std::vector<socket_t> to_close;
|
|
462
|
+
|
|
463
|
+
for (int i = 0; i < num_events; ++i) {
|
|
464
|
+
socket_t fd = poll_results[i].fd;
|
|
465
|
+
uint32_t events = poll_results[i].events;
|
|
466
|
+
|
|
467
|
+
//--------------------------------------------------------------
|
|
468
|
+
// Listen socket — accept incoming
|
|
469
|
+
//--------------------------------------------------------------
|
|
470
|
+
if (fd == listen_socket_) {
|
|
471
|
+
if (events & PollIn) {
|
|
472
|
+
// NOTE: accept_incoming acquires mutex internally,
|
|
473
|
+
// but we are already holding it. We need to call
|
|
474
|
+
// the internal version without re-locking.
|
|
475
|
+
accept_incoming();
|
|
476
|
+
}
|
|
477
|
+
continue;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
//--------------------------------------------------------------
|
|
481
|
+
// Connecting socket — check connect completion
|
|
482
|
+
//--------------------------------------------------------------
|
|
483
|
+
auto connecting_it = connecting_.find(fd);
|
|
484
|
+
if (connecting_it != connecting_.end()) {
|
|
485
|
+
auto& pending = connecting_it->second;
|
|
441
486
|
|
|
442
|
-
if (
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
ctx.incoming = false;
|
|
459
|
-
ctx.connected_at = std::chrono::steady_clock::now();
|
|
460
|
-
ctx.last_activity = ctx.connected_at;
|
|
461
|
-
|
|
462
|
-
// Send handshake via connection method (sets handshake_sent_ flag)
|
|
463
|
-
LOG_NET_DEBUG("Sending handshake (" +
|
|
464
|
-
std::to_string(BT_HANDSHAKE_SIZE) + " bytes) to " +
|
|
465
|
-
pending.ip);
|
|
466
|
-
conn->start_handshake();
|
|
487
|
+
if (events & PollErr) {
|
|
488
|
+
// Connection failed
|
|
489
|
+
LOG_NET_DEBUG("Connection failed (poll error) to " + pending.ip);
|
|
490
|
+
poller_->remove(fd);
|
|
491
|
+
poller_state_.erase(fd);
|
|
492
|
+
close_socket(fd);
|
|
493
|
+
connecting_.erase(connecting_it);
|
|
494
|
+
continue;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
if (events & PollOut) {
|
|
498
|
+
// Check if connection succeeded
|
|
499
|
+
int sock_error = 0;
|
|
500
|
+
socklen_t len = sizeof(sock_error);
|
|
501
|
+
getsockopt(fd, SOL_SOCKET, SO_ERROR,
|
|
502
|
+
reinterpret_cast<char*>(&sock_error), &len);
|
|
467
503
|
|
|
468
|
-
|
|
504
|
+
if (sock_error == 0) {
|
|
505
|
+
LOG_NET_INFO("Connected to peer " + pending.ip + ":" +
|
|
506
|
+
std::to_string(pending.port));
|
|
507
|
+
|
|
508
|
+
// Set TCP_NODELAY for low latency
|
|
509
|
+
set_tcp_nodelay(fd);
|
|
510
|
+
|
|
511
|
+
// Create connection object
|
|
512
|
+
auto conn = std::make_shared<BtPeerConnection>(
|
|
513
|
+
pending.info_hash, pending.peer_id, pending.num_pieces);
|
|
514
|
+
conn->set_address(pending.ip, pending.port);
|
|
515
|
+
conn->set_socket(static_cast<int>(fd));
|
|
516
|
+
|
|
517
|
+
// Create socket context
|
|
518
|
+
SocketContext ctx;
|
|
519
|
+
ctx.socket = fd;
|
|
520
|
+
ctx.info_hash = pending.info_hash;
|
|
521
|
+
ctx.connection = conn;
|
|
522
|
+
ctx.state = NetConnectionState::Handshaking;
|
|
523
|
+
ctx.incoming = false;
|
|
524
|
+
ctx.connected_at = std::chrono::steady_clock::now();
|
|
525
|
+
ctx.last_activity = ctx.connected_at;
|
|
526
|
+
|
|
527
|
+
// Send handshake
|
|
528
|
+
LOG_NET_DEBUG("Sending handshake (" +
|
|
529
|
+
std::to_string(BT_HANDSHAKE_SIZE) + " bytes) to " +
|
|
530
|
+
pending.ip);
|
|
531
|
+
conn->start_handshake();
|
|
532
|
+
|
|
533
|
+
connections_[fd] = std::move(ctx);
|
|
534
|
+
|
|
535
|
+
// Update poller: now interested in read (+ write if handshake queued)
|
|
536
|
+
uint32_t desired = PollIn;
|
|
537
|
+
if (!conn->send_buffer().empty()) {
|
|
538
|
+
desired |= PollOut;
|
|
539
|
+
}
|
|
540
|
+
poller_->modify(fd, desired);
|
|
541
|
+
poller_state_[fd] = desired;
|
|
542
|
+
|
|
543
|
+
LOG_NET_DEBUG("Handshake queued to " + pending.ip);
|
|
544
|
+
} else {
|
|
545
|
+
LOG_NET_DEBUG("Connection failed to " + pending.ip + ": " +
|
|
546
|
+
std::to_string(sock_error));
|
|
547
|
+
poller_->remove(fd);
|
|
548
|
+
poller_state_.erase(fd);
|
|
549
|
+
close_socket(fd);
|
|
550
|
+
}
|
|
469
551
|
|
|
470
|
-
|
|
471
|
-
} else {
|
|
472
|
-
LOG_NET_DEBUG("Connection failed to " + pending.ip + ": " +
|
|
473
|
-
std::to_string(sock_error));
|
|
474
|
-
close_socket(socket);
|
|
552
|
+
connecting_.erase(connecting_it);
|
|
475
553
|
}
|
|
476
|
-
|
|
554
|
+
continue;
|
|
477
555
|
}
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
std::vector<socket_t> to_close;
|
|
492
|
-
|
|
493
|
-
for (auto& [socket, ctx] : connections_) {
|
|
556
|
+
|
|
557
|
+
//--------------------------------------------------------------
|
|
558
|
+
// Active connection — handle read/write/error
|
|
559
|
+
//--------------------------------------------------------------
|
|
560
|
+
auto conn_it = connections_.find(fd);
|
|
561
|
+
if (conn_it == connections_.end()) {
|
|
562
|
+
// Unknown fd — remove from poller
|
|
563
|
+
poller_->remove(fd);
|
|
564
|
+
poller_state_.erase(fd);
|
|
565
|
+
continue;
|
|
566
|
+
}
|
|
567
|
+
|
|
494
568
|
bool should_close = false;
|
|
495
569
|
|
|
496
|
-
if (
|
|
497
|
-
if (
|
|
498
|
-
LOG_NET_DEBUG("Error on socket for " +
|
|
570
|
+
if (events & PollErr) {
|
|
571
|
+
if (conn_it->second.connection) {
|
|
572
|
+
LOG_NET_DEBUG("Error on socket for " + conn_it->second.connection->ip());
|
|
499
573
|
}
|
|
500
574
|
should_close = true;
|
|
501
575
|
}
|
|
502
576
|
|
|
503
|
-
if (!should_close &&
|
|
504
|
-
|
|
577
|
+
if (!should_close && (events & PollHup)) {
|
|
578
|
+
if (conn_it->second.connection) {
|
|
579
|
+
LOG_NET_DEBUG("Peer hung up: " + conn_it->second.connection->ip());
|
|
580
|
+
}
|
|
581
|
+
should_close = true;
|
|
505
582
|
}
|
|
506
583
|
|
|
507
|
-
if (!should_close &&
|
|
508
|
-
should_close =
|
|
584
|
+
if (!should_close && (events & PollIn)) {
|
|
585
|
+
should_close = handle_readable(fd, connected_events, data_events);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
if (!should_close && (events & PollOut)) {
|
|
589
|
+
should_close = handle_writable(fd);
|
|
509
590
|
}
|
|
510
591
|
|
|
511
592
|
if (should_close) {
|
|
512
|
-
to_close.push_back(
|
|
593
|
+
to_close.push_back(fd);
|
|
513
594
|
}
|
|
514
595
|
}
|
|
515
596
|
|
|
516
|
-
// Close connections AFTER
|
|
597
|
+
// Close connections AFTER processing all events to avoid iterator invalidation
|
|
517
598
|
for (socket_t s : to_close) {
|
|
518
599
|
close_connection_internal(s, disconnected_events);
|
|
519
600
|
}
|
|
@@ -565,6 +646,10 @@ void BtNetworkManager::process_pending_connects() {
|
|
|
565
646
|
pending.socket = sock;
|
|
566
647
|
pending.start_time = std::chrono::steady_clock::now();
|
|
567
648
|
|
|
649
|
+
// Register with poller: interested in write (connect complete)
|
|
650
|
+
poller_->add(sock, PollOut);
|
|
651
|
+
poller_state_[sock] = PollOut;
|
|
652
|
+
|
|
568
653
|
connecting_[sock] = std::move(pending);
|
|
569
654
|
}
|
|
570
655
|
|
|
@@ -589,6 +674,9 @@ socket_t BtNetworkManager::create_connect_socket(const std::string& ip, uint16_t
|
|
|
589
674
|
return INVALID_SOCKET_VALUE;
|
|
590
675
|
}
|
|
591
676
|
|
|
677
|
+
// Set TCP_NODELAY immediately (before connect)
|
|
678
|
+
set_tcp_nodelay(sock);
|
|
679
|
+
|
|
592
680
|
// Resolve and connect
|
|
593
681
|
std::string resolved = network_utils::resolve_hostname(ip);
|
|
594
682
|
if (resolved.empty()) {
|
|
@@ -632,19 +720,18 @@ socket_t BtNetworkManager::create_connect_socket(const std::string& ip, uint16_t
|
|
|
632
720
|
}
|
|
633
721
|
|
|
634
722
|
void BtNetworkManager::accept_incoming() {
|
|
723
|
+
// NOTE: called with mutex_ already held from io_loop
|
|
724
|
+
|
|
635
725
|
socket_t client = accept_client(listen_socket_);
|
|
636
726
|
if (!is_valid_socket(client)) {
|
|
637
727
|
return;
|
|
638
728
|
}
|
|
639
729
|
|
|
640
730
|
// Check connection limit
|
|
641
|
-
{
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
close_socket(client);
|
|
646
|
-
return;
|
|
647
|
-
}
|
|
731
|
+
if (connections_.size() >= config_.max_connections) {
|
|
732
|
+
LOG_NET_DEBUG("Connection limit reached, rejecting incoming");
|
|
733
|
+
close_socket(client);
|
|
734
|
+
return;
|
|
648
735
|
}
|
|
649
736
|
|
|
650
737
|
// Set non-blocking
|
|
@@ -653,6 +740,9 @@ void BtNetworkManager::accept_incoming() {
|
|
|
653
740
|
return;
|
|
654
741
|
}
|
|
655
742
|
|
|
743
|
+
// Set TCP_NODELAY for low latency
|
|
744
|
+
set_tcp_nodelay(client);
|
|
745
|
+
|
|
656
746
|
// Get peer address
|
|
657
747
|
std::string peer_addr = get_peer_address(client);
|
|
658
748
|
std::string ip;
|
|
@@ -666,8 +756,6 @@ void BtNetworkManager::accept_incoming() {
|
|
|
666
756
|
|
|
667
757
|
LOG_NET_INFO("Accepted incoming connection from " + peer_addr);
|
|
668
758
|
|
|
669
|
-
std::lock_guard<std::mutex> lock(mutex_);
|
|
670
|
-
|
|
671
759
|
// Create connection object immediately (info_hash unknown until handshake)
|
|
672
760
|
auto conn = std::make_shared<BtPeerConnection>(config_.peer_id);
|
|
673
761
|
conn->set_address(ip, port);
|
|
@@ -687,6 +775,10 @@ void BtNetworkManager::accept_incoming() {
|
|
|
687
775
|
ctx.connection = conn;
|
|
688
776
|
|
|
689
777
|
connections_[client] = std::move(ctx);
|
|
778
|
+
|
|
779
|
+
// Register with poller (interested in reading handshake)
|
|
780
|
+
poller_->add(client, PollIn);
|
|
781
|
+
poller_state_[client] = PollIn;
|
|
690
782
|
}
|
|
691
783
|
|
|
692
784
|
void BtNetworkManager::on_incoming_info_hash(BtPeerConnection* conn, const BtInfoHash& info_hash) {
|
|
@@ -735,48 +827,55 @@ bool BtNetworkManager::handle_readable(socket_t socket,
|
|
|
735
827
|
return true; // Should close
|
|
736
828
|
}
|
|
737
829
|
|
|
738
|
-
//
|
|
830
|
+
// Drain all available data from the kernel buffer (loop until EWOULDBLOCK)
|
|
739
831
|
auto& recv_buf = ctx.connection->recv_buffer();
|
|
832
|
+
bool got_data = false;
|
|
740
833
|
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
834
|
+
while (true) {
|
|
835
|
+
const size_t recv_size = 16384;
|
|
836
|
+
recv_buf.ensure_space(recv_size);
|
|
837
|
+
|
|
838
|
+
int bytes = recv(socket, reinterpret_cast<char*>(recv_buf.write_ptr()),
|
|
839
|
+
static_cast<int>(recv_buf.write_space()), 0);
|
|
840
|
+
|
|
841
|
+
if (bytes > 0) {
|
|
842
|
+
recv_buf.received(bytes);
|
|
843
|
+
got_data = true;
|
|
844
|
+
|
|
845
|
+
LOG_NET_DEBUG("handle_readable: recv " + std::to_string(bytes) +
|
|
846
|
+
" bytes from " + ctx.connection->ip());
|
|
847
|
+
continue; // Try to read more
|
|
848
|
+
}
|
|
849
|
+
|
|
750
850
|
if (bytes == 0) {
|
|
751
|
-
|
|
752
|
-
|
|
851
|
+
// Peer closed connection gracefully
|
|
852
|
+
LOG_NET_DEBUG("Connection closed by peer: " + ctx.connection->ip());
|
|
853
|
+
return true; // Should close
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
// bytes < 0: error
|
|
753
857
|
#ifdef _WIN32
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
}
|
|
858
|
+
int err = WSAGetLastError();
|
|
859
|
+
if (err == WSAEWOULDBLOCK) {
|
|
860
|
+
break; // No more data available, exit recv loop
|
|
861
|
+
}
|
|
862
|
+
LOG_NET_DEBUG("Receive error: " + std::to_string(err));
|
|
760
863
|
#else
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
} else {
|
|
764
|
-
return false; // Would block, try again later
|
|
765
|
-
}
|
|
766
|
-
#endif
|
|
864
|
+
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
|
865
|
+
break; // No more data available, exit recv loop
|
|
767
866
|
}
|
|
768
|
-
|
|
769
|
-
|
|
867
|
+
LOG_NET_DEBUG("Receive error: " + std::string(strerror(errno)));
|
|
868
|
+
#endif
|
|
869
|
+
return true; // Should close on real errors
|
|
770
870
|
}
|
|
771
871
|
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
872
|
+
if (!got_data) {
|
|
873
|
+
return false; // Nothing received, no processing needed
|
|
874
|
+
}
|
|
775
875
|
|
|
776
|
-
|
|
777
|
-
" bytes from " + ctx.connection->ip());
|
|
876
|
+
ctx.last_activity = std::chrono::steady_clock::now();
|
|
778
877
|
|
|
779
|
-
// Process
|
|
878
|
+
// Process all received data (handshake and messages)
|
|
780
879
|
return handle_peer_data(ctx, connected_events, data_events);
|
|
781
880
|
}
|
|
782
881
|
|
|
@@ -855,6 +954,16 @@ bool BtNetworkManager::flush_send_buffer(SocketContext& ctx) {
|
|
|
855
954
|
if (sent > 0) {
|
|
856
955
|
send_buf.pop_front(static_cast<size_t>(sent));
|
|
857
956
|
ctx.last_activity = std::chrono::steady_clock::now();
|
|
957
|
+
|
|
958
|
+
// If send buffer is now empty, remove PollOut interest to avoid busy-looping
|
|
959
|
+
if (send_buf.empty()) {
|
|
960
|
+
uint32_t desired = PollIn;
|
|
961
|
+
auto ps = poller_state_.find(ctx.socket);
|
|
962
|
+
if (ps != poller_state_.end() && ps->second != desired) {
|
|
963
|
+
poller_->modify(ctx.socket, desired);
|
|
964
|
+
ps->second = desired;
|
|
965
|
+
}
|
|
966
|
+
}
|
|
858
967
|
} else if (sent < 0) {
|
|
859
968
|
#ifdef _WIN32
|
|
860
969
|
int err = WSAGetLastError();
|
|
@@ -887,6 +996,10 @@ void BtNetworkManager::close_connection_internal(socket_t socket,
|
|
|
887
996
|
disconnected_events.push_back(std::move(event));
|
|
888
997
|
}
|
|
889
998
|
|
|
999
|
+
// Remove from poller before closing socket
|
|
1000
|
+
poller_->remove(socket);
|
|
1001
|
+
poller_state_.erase(socket);
|
|
1002
|
+
|
|
890
1003
|
close_socket(socket, true);
|
|
891
1004
|
connections_.erase(it);
|
|
892
1005
|
}
|