我正在开发一个项目,需要将字符串转换为二进制格式并对二进制数据实施循环冗余校验(crc)。数据需要通过客户端-服务器通信进行传输。我需要提供两个文件 city.txt 和 Country.txt,这两个文件的数据需要首先转换为二进制并在其上实现 crc,然后将这些文件发送到服务器,服务器删除 crc 并将其再次更改回字符串,将其发送给客户。所有这些功能对于我通过网络发送的第一个文件(city.txt)运行良好,但对于第二个文件则不起作用。
客户端代码是
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#define SIZE 1024
// Function to run the CRC service and generate the CRC file
void runCRCService(const char *inputFile, const char *outputFile) {
pid_t pid = fork();
if (pid < 0) {
perror("[-]Error in fork");
exit(1);
} else if (pid == 0) {
FILE *fp = fopen(outputFile, "w");
if (!fp) {
perror("[-]Error in opening output file");
exit(1);
}
dup2(fileno(fp), STDOUT_FILENO);
fclose(fp);
execlp("./crcservice_v2", "./crcservice_v2", inputFile, NULL);
perror("[-]Error in execlp for CRC service");
exit(1);
} else {
wait(NULL);
}
}
// Function to receive a file from the server
void write_file(int sockfd, const char *filename) {
FILE *fp = fopen(filename, "w");
if (fp == NULL) {
perror("[-]Error in opening file");
exit(1);
}
char buffer[SIZE] = {0};
int n;
while (1) {
n = recv(sockfd, buffer, SIZE, 0);
if (n <= 0) break;
if (strncmp(buffer, "FILE_END", 8) == 0) break;
fwrite(buffer, 1, n, fp);
memset(buffer, 0, SIZE);
}
fclose(fp);
}
void send_file(FILE *fp, int sockfd) {
char data[SIZE] = {0};
int bytes_read, bytes_sent, total_sent = 0;
while ((bytes_read = fread(data, 1, SIZE, fp)) > 0) {
total_sent = 0;
while (total_sent < bytes_read) {
bytes_sent = send(sockfd, data + total_sent, bytes_read - total_sent, 0);
if (bytes_sent == -1) {
perror("[-]Error in sending file");
exit(1);
}
total_sent += bytes_sent;
}
bzero(data, SIZE);
}
// Send "FILE_END" delimiter
send(sockfd, "FILE_END", 8, 0);
}
int main() {
char *ip = "127.0.0.1";
int port = 8080;
int sockfd, e;
struct sockaddr_in server_addr;
FILE *fp;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("[-]Error in socket");
exit(1);
}
printf("[+]Server socket created successfully.\n");
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
server_addr.sin_addr.s_addr = inet_addr(ip);
e = connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));
if (e == -1) {
perror("[-]Error in connecting to server");
exit(1);
}
printf("[+]Connected to Server.\n");
// Generate CRC for city.txt and store it in citycrc.txt
printf("[+]Generating CRC for city.txt\n");
runCRCService("city.txt", "citycrc.txt");
sleep(1);
// Generate CRC for country.txt and store it in countrycrc.txt
printf("[+]Generating CRC for country.txt\n");
runCRCService("country.txt", "countrycrc.txt");
// Send citycrc.txt to the server
fp = fopen("citycrc.txt", "r");
if (fp == NULL) {
perror("[-]Error in reading citycrc.txt");
exit(1);
}
printf("[+]Sending citycrc.txt to the server\n");
send_file(fp, sockfd);
fclose(fp);
printf("[+]citycrc.txt sent successfully.\n");
sleep(1);
// Send countrycrc.txt to the server
fp = fopen("countrycrc.txt", "r");
if (fp == NULL) {
perror("[-]Error in reading countrycrc.txt");
exit(1);
}
printf("[+]Sending countrycrc.txt to the server\n");
send_file(fp, sockfd);
fclose(fp);
printf("[+]countrycrc.txt sent successfully.\n");
sleep(1);
// Receive crcascii.txt from the server
printf("[+]Receiving crcasciicity.txt from the server\n");
write_file(sockfd, "crcasciicity.txt");
printf("[+]crcasciicity.txt received successfully.\n");
sleep(1);
printf("[+]Receiving crcasciicountry.txt from the server\n");
write_file(sockfd, "crcasciicountry.txt");
printf("[+]crcasciicountry.txt received successfully.\n");
// Close the connection
close(sockfd);
printf("[+]Connection closed.\n");
return 0;
}
服务器端代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#define SIZE 1024
// Function to receive a file from the client
void write_file(int sockfd, const char *filename) {
FILE *fp = fopen(filename, "w");
if (fp == NULL) {
perror("[-]Error in opening file");
exit(1);
}
char buffer[SIZE];
int n;
while (1) {
n = recv(sockfd, buffer, SIZE, 0);
if (n <= 0) break;
// Check for "FILE_END" delimiter
if (strncmp(buffer, "FILE_END", 8) == 0) break;
fwrite(buffer, 1, n, fp);
memset(buffer, 0, SIZE);
}
fclose(fp);
fsync(fileno(fp));
}
void send_file(int sockfd, const char *filename) {
FILE *fp = fopen(filename, "r");
if (fp == NULL) {
perror("[-]Error in opening file");
exit(1);
}
char data[SIZE] = {0};
int bytes_read;
while ((bytes_read = fread(data, 1, SIZE, fp)) > 0) {
if (send(sockfd, data, bytes_read, 0) == -1) {
perror("[-]Error in sending file");
exit(1);
}
memset(data, 0, SIZE);
}
// Send "FILE_END" delimiter
send(sockfd, "FILE_END", 8, 0);
fclose(fp);
}
// Function to extract, flip MSB, convert to ASCII, and save to ASCII file
void extract_flip_and_save_ascii(const char *inputFile, const char *outputFile) {
FILE *fp = fopen(inputFile, "r");
if (fp == NULL) {
perror("[-]Error in opening input file");
return;
}
char buffer[SIZE * 8] = {0};
int bitCount = 0;
char ch;
while ((ch = fgetc(fp)) != EOF) {
if (ch == '0' || ch == '1') {
buffer[bitCount++] = ch;
}
}
buffer[bitCount] = '\0';
fclose(fp);
int totalBits = strlen(buffer);
if (totalBits <= 54) {
printf("[-]Error: Not enough bits to extract data part.\n");
return;
}
int dataStart = 24;
int dataEnd = totalBits - 31;
FILE *output_fp = fopen(outputFile, "w");
if (output_fp == NULL) {
perror("[-]Error in opening output file");
return;
}
for (int i = dataStart; i < dataEnd; i += 8) {
if (i + 7 < dataEnd) {
buffer[i] = (buffer[i] == '0') ? '1' : '0';
int asciiValue = 0;
for (int j = 0; j < 8; j++) {
asciiValue = (asciiValue << 1) | (buffer[i + j] - '0');
}
fputc(asciiValue, output_fp);
}
}
fclose(output_fp);
}
int main() {
char *ip = "127.0.0.1";
int port = 8080;
int sockfd, new_sock;
struct sockaddr_in server_addr, new_addr;
socklen_t addr_size;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("[-]Error in socket");
exit(1);
}
printf("[+]Server socket created successfully.\n");
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
server_addr.sin_addr.s_addr = inet_addr(ip);
if (bind(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
perror("[-]Error in bind");
exit(1);
}
printf("[+]Binding successful.\n");
if (listen(sockfd, 10) == 0) {
printf("[+]Listening...\n");
} else {
perror("[-]Error in listening");
exit(1);
}
addr_size = sizeof(new_addr);
new_sock = accept(sockfd, (struct sockaddr*)&new_addr, &addr_size);
if (new_sock < 0) {
perror("[-]Error in accepting connection");
exit(1);
}
printf("[+]Connection accepted.\n");
// Receive citycrc.txt from the client
write_file(new_sock, "citycrc.txt");
printf("[+]citycrc.txt received successfully.\n");
// Receive countrycrc.txt from the client
write_file(new_sock, "countrycrc.txt");
printf("[+]countrycrc.txt received successfully.\n");
// Generate crcasciicity.txt from citycrc.txt
extract_flip_and_save_ascii("citycrc.txt", "crcasciicity.txt");
printf("[+]crcasciicity.txt generated successfully.\n");
// Send crcasciicity.txt to the client
printf("[+]Sending crcasciicity.txt to the client.\n");
send_file(new_sock, "crcasciicity.txt");
printf("[+]crcasciicity.txt sent successfully.\n");
sleep(1);
// Generate crcasciicountry.txt from countrycrc.txt
extract_flip_and_save_ascii("countrycrc.txt", "crcasciicountry.txt");
printf("[+]crcasciicountry.txt generated successfully.\n");
// Send crcasciicountry.txt to the client
printf("[+]Sending crcasciicountry.txt to the client.\n");
send_file(new_sock, "crcasciicountry.txt");
printf("[+]crcasciicountry.txt sent successfully.\n");
// Close the connection
close(new_sock);
close(sockfd);
printf("[+]Connection closed.\n");
return 0;
}
city.txt 包含克林顿、格林维尔等数据......
county.txt 有加拿大、巴西等数据...
citycrc.txt 保存 city.txt 文件的二进制表示形式,如“10”格式。
countrycrc.txt 保存country.txt 文件的二进制表示形式,如“10”格式。
cityasciicrc.txt 以 citycrc.txt 作为输入,对其进行 extract_flip_save_ascii() 操作并返回 Clinton、Greenvile。 Countryasciicrc.txt 将countrycrc.txt 作为输入,对其进行extract_flip_save_ascii() 操作并返回加拿大、巴西。
上述代码的问题是当我运行服务器可执行文件时它首先执行
Server socket created successfully
Binding successful.
Listening ...
客户端可执行文件:
[+]Server socket created successfully.
[+]Connected to Server.
[+]Generating CRC for city.txt
[+]Generating CRC for country.txt
[+]Sending citycrc.txt to the server
[+]citycrc.txt sent successfully.
[+]Sending countrycrc.txt to the server
[+]countrycrc.txt sent successfully.
[+]Receiving crcasciicity.txt from the server
然后停止执行。在这种情况下,如果我从工作目录打开 citycrc.txt 和 Countrycrc.txt,我可以看到正确生成的二进制结果。那么他们的crcservice_v2就没有问题了。我等待了 5/6 分钟代码完全执行,然后我按 ctrl+c 终止了客户端的代码。
现在,如果我检查countrycrc.txt,我发现它是空的。我还发现有时(当我重复运行处理后)会生成 crcasciicountry.txt 但是一个空文件。有时crcasciicountry.txt没有生成,有时crcasciicity.txt实际上同时具有city.txt和country.txt的内容。如果我收到 crcasciicountry.txt 的空文件,则 crcasciicity 的输出是正确的,没有合并两个文件的内容。
我使用的是Ubuntu 20.04,一台电脑,两个终端,实现客户端服务器通信。
谢谢你。任何帮助将不胜感激。
您似乎对
read()
的行为有错误的看法。 您的代码假设它将在传输的每个单独文件的数据末尾观察到零字节读取,但事实并非如此。 TCP是一种流协议。 它没有内置的消息或其他细分意义,除非您认为单个字节是这样的。 套接字上的文件结尾与传输的数据无关——它与套接字是否仍然打开有关,以便附加数据可能仍然传输。
为了能够将两个文件识别为单独的单元,您需要在原始字节流之上分层某种应用程序协议。 这可能就像在每个文件的数据前面放置一个字节计数一样简单,这样接收器就知道它已经通过读取了那么多(附加)字节而到达了该文件的末尾。 当然,更复杂的方案也是可能的。