修改Ncat源码实现新连接对应独立文件输出以替代标准输出
Hey there, let's break down why your current code isn't working and how to fix it properly.
The Root Problem
Your f variable is declared inside the read_socket function—that means every time this function gets called (whether it's for a new client connection, or the same client sending more data), f gets reset to 0. On top of that, you're incrementing f every time you read a chunk of data, which would split a single client's data across multiple files instead of keeping it all together. That's exactly the opposite of what you want!
The Solution: Bind a Unique ID to Each Client Connection
We need to assign a persistent, unique ID to each client when they first connect, then use that ID for all data from that connection. Ncat already uses a struct fdinfo to track client socket details, so we can extend that to store our file ID.
Step 1: Extend the fdinfo Structure
First, find where struct fdinfo is defined (usually in ncat.h or a related header) and add a field to store the connection's unique file ID:
struct fdinfo { // ... keep all existing fields ... int file_id; // New: Stores the unique ID for this client's log file };
Step 2: Add a Global Counter for Unique IDs
Define a global (or module-level) variable to track the next available file ID. Since Ncat typically uses a single-threaded select loop, we don't need heavy thread safety here—but if you're building a multi-threaded version, add a mutex to protect this counter.
static int next_file_id = 0; // For multi-threaded builds: // static pthread_mutex_t file_id_mutex = PTHREAD_MUTEX_INITIALIZER;
Step 3: Assign the ID When a New Client Connects
Find the code that handles new client connections (where accept() is called, often in a function like handle_new_connection). When you create a new fdinfo for the client, assign it the next unique ID:
// When initializing a new fdinfo for a connected client struct fdinfo *new_fdn = create_fdinfo(new_client_fd); // Lock if multi-threaded: pthread_mutex_lock(&file_id_mutex); new_fdn->file_id = next_file_id++; // Unlock if multi-threaded: pthread_mutex_unlock(&file_id_mutex);
Step 4: Rewrite read_socket to Use the Persistent ID
Now update your read_socket function to use the ID stored in the client's fdinfo instead of the local f variable. We'll also optimize by generating the filename once per connection instead of every data chunk:
int read_socket(int recv_fd) { char buf[DEFAULT_TCP_BUF_LEN]; struct fdinfo *fdn; int nbytes, pending; char filename[20]; fdn = get_fdinfo(&client_fdlist, recv_fd); ncat_assert(fdn != NULL); // Generate filename once for this connection snprintf(filename, sizeof(filename), "filename_%i", fdn->file_id); nbytes = 0; do { int n, s; n = ncat_recv(fdn, buf, 512, &pending); if (n <= 0) { // Keep your existing cleanup logic here unchanged if (o.debug) logdebug("Closing fd %d.\n", recv_fd); #ifdef HAVE_OPENSSL if (o.ssl && fdn->ssl) { if (nbytes == 0) SSL_shutdown(fdn->ssl); SSL_free(fdn->ssl); } #endif close(recv_fd); checked_fd_clr(recv_fd, &master_readfds); rm_fd(&client_fdlist, recv_fd); checked_fd_clr(recv_fd, &master_broadcastfds); rm_fd(&broadcast_fdlist, recv_fd); conn_inc--; if (get_conn_count() == 0) checked_fd_clr(STDIN_FILENO, &master_readfds); return n; } else { FILE *fp = fopen(filename, "a"); if (fp == NULL) { fprintf(stderr, "Failed to open log file: %s\n", filename); return 0; } s = fwrite(buf, 1, n, fp); fclose(fp); nbytes += n; } } while (pending); return nbytes; }
Bonus Optimization: Avoid Reopening Files
Opening and closing the file every time you read a chunk is inefficient. Instead, open the file when the client connects and store the file pointer in fdinfo, then close it when the connection ends:
- Add a
FILE *log_fpfield tostruct fdinfo - Open the file during connection setup:
snprintf(filename, sizeof(filename), "filename_%i", new_fdn->file_id); new_fdn->log_fp = fopen(filename, "w"); if (new_fdn->log_fp == NULL) { // Handle error (e.g., reject the connection or log to stderr) } - Use the open file in
read_socket:s = fwrite(buf, 1, n, fdn->log_fp); fflush(fdn->log_fp); // Ensure data writes immediately instead of buffering - Close the file in the cleanup section of
read_socket:if (fdn->log_fp != NULL) { fclose(fdn->log_fp); fdn->log_fp = NULL; }
内容的提问来源于stack exchange,提问作者Jason Smith

