gpt4 book ai didi

c - 使用套接字下载(获取)文件时观察不稳定的行为

转载 作者:行者123 更新时间:2023-11-30 17:05:47 25 4
gpt4 key购买 nike

我已经使用套接字实现了 mget 命令。但我得到的输出有一些随机行为。有时我能够在客户端上下载整个文件,有时我能够部分下载文件,有时服务器代码会出现段错误,有时客户端会进入无限循环。

服务器代码:

      /* A simple server in the internet domain using TCP
The port number is passed as an argument */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

int main(int argc, char *argv[])
{
int sockfd, newsockfd, portno;
socklen_t clilen;
// char buffer[256];
struct sockaddr_in serv_addr, cli_addr;
int n;
if (argc < 2) {
fprintf(stderr,"ERROR, no port provided\n");
exit(1);
}
sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sockfd < 0)
error("ERROR opening socket");
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = atoi(argv[1]);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr,
sizeof(serv_addr)) < 0)
error("ERROR on binding");
listen(sockfd,5);
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd,
(struct sockaddr *) &cli_addr,
&clilen);
if (newsockfd < 0)
error("ERROR on accept");
/*bzero(buffer,256);
n = read(newsockfd,buffer,255);
if (n < 0) error("ERROR reading from socket");
printf("Here is the message: %s\n",buffer);
n = write(newsockfd,"I got your message",18);
if (n < 0) error("ERROR writing to socket");*/

char command[512];
bzero(command, 512);

char buffer[4096];
char output[5000];
char f_size[20];

int send_bytes;
int written_bytes;
int total_sent_bytes = 0;

long int file_size;

n = recv(newsockfd, command, 512, 0); //try with MSG_WAITALL
if(n < 0)error("Error receiving command");
puts(command); //only file name

FILE * fp;
fp = fopen(command, "rb");
if(fp == NULL)error("Can not open requested file");

FILE * test_file_fp;
test_file_fp = fopen("test_file.txt", "wb");
if(test_file_fp == NULL)error("Can not open test file");

fseek(fp,0, SEEK_END);
file_size = ftell(fp);
rewind(fp);

sprintf(f_size,"%ld", file_size);

send(newsockfd, f_size, strlen(f_size), 0);

while(!feof(fp)){
bzero(buffer, 4096);

n = fread(buffer, sizeof(char), 4095, fp);
sprintf(output, "read bytes using fread = %d", n);
puts(output);

send_bytes = send(newsockfd, buffer, strlen(buffer) + 1, MSG_MORE);
total_sent_bytes += send_bytes;
sprintf(output, "sent bytes using send = %d", send_bytes);
puts(output);

written_bytes = fwrite(buffer, sizeof(char), strlen(buffer), test_file_fp);
sprintf(output, "written bytes using fwrite = %d", written_bytes);
puts(output);

//bzero(command, 512);

//recv(newsockfd, buffer, 512, 0);

puts("\n");
}

sprintf(output, "total sent bytes using send = %d\n", total_sent_bytes);
puts(output);

send(newsockfd, NULL, 1, 0);

fclose(test_file_fp);
fclose(fp);
close(newsockfd);
close(sockfd);
return 0;
}

客户端代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

void error(const char *msg)
{
perror(msg);
exit(0);
}

int main(int argc, char *argv[])
{
int sockfd, portno, n;
struct sockaddr_in serv_addr;
struct hostent *server;
int i;
// char buffer[5000];
if (argc < 3) {
fprintf(stderr,"usage %s hostname port\n", argv[0]);
exit(0);
}
portno = atoi(argv[2]);
sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sockfd < 0)
error("ERROR opening socket");
server = gethostbyname(argv[1]);
if (server == NULL) {
fprintf(stderr,"ERROR, no such host\n");
exit(0);
}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
bcopy((char *)server->h_addr,
(char *)&serv_addr.sin_addr.s_addr,
server->h_length);
serv_addr.sin_port = htons(portno);
if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
error("ERROR connecting");


char command[512];
char output[5096];

int send_command_bytes;
int reveived_bytes;
char buffer[4096];

long int total_bytes;
long int total_received_bytes;

int written_bytes;

sprintf(output, "IP Protocol Number = %d", IPPROTO_TCP);
puts(output);

FILE * fp;

fp = fopen("received_file.txt", "wb");
if(fp == NULL)error("Could not open file for receiving data");

memcpy(command, "send_me.txt", 11);

send_command_bytes = send(sockfd, command, strlen(command), 0);
puts(command);

recv(sockfd, buffer, 4096, 0);
total_bytes = atoi(buffer);

sprintf(output, "Total bytes to be received = %ld", total_bytes);
puts(output);

total_received_bytes = 0;

while(totala_received_bytes < total_bytes){
bzero(buffer, 4096);
reveived_bytes = recv(sockfd, buffer, 4095, 0); //try with MSG_WAITALL, MSG_DONTWAIT
total_received_bytes += reveived_bytes;
sprintf(output, "Number of bytes received = %d", reveived_bytes);
puts(output);

written_bytes = fwrite(buffer, sizeof(char), strlen(buffer), fp);
sprintf(output, "Total written bytes = %d", written_bytes);
puts(output);

//send(sockfd, "1", 2, 0);

sprintf(output, "total Number of bytes received so far = %ld", total_received_bytes);
puts(output);
puts("\n");
}
fclose(fp);
close(sockfd);
return 0;
}

我从客户端发送需要下载的文件名“send_me.txt”,并且服务器正在名为命令的缓冲区中读取该文件名。该文件正在服务器中打开,然后读取,然后发送到客户端。我已经尝试了很多可能性来解决这个问题,但问题仍然存在。

最佳答案

您的代码在处理套接字通信时存在两个常见问题

<强>1。缺少 NUL 终止
C 中的字符串以 NUL 结尾,因此您需要将 NUL 作为消息的一部分发送,或者需要将 NUL 添加到接收端。

<强>2。关于消息大小的假设
在套接字编程中,一个常见的误解是每个 recv 都会匹配相应的 send。例如,如果我发送 10个字节,然后是50个字节,最后是20个字节,那么预期需要调用recv三次,并且将返回10字节、50字节、20字节。这是完全、完全、完全错误的。事实上,第一个 recv 将返回 1 字节到 80 字节之间的任何位置。也就是说,它可以返回来自第一次发送的数据的任何部分,直到来自所有发送的所有数据。

代码示例
在服务器代码中,您执行此操作

 sprintf(f_size,"%ld", file_size);
send(newsockfd, f_size, strlen(f_size), 0);

如果file_size是123,那么sprintf将写入123\0f_size,即三位数字和一个 NUL 终止符。 strlen 将返回 3,因此代码仅发送三位数字,但不发送 NUL 终止符。

在客户端,你有这个

recv(sockfd, buffer, 4096, 0);
total_bytes = atoi(buffer);

由于服务器不发送 NUL 字节并且客户端不添加 NUL 字节,因此 atoi 将看到 recv 放入 中的任何内容>缓冲区,后面是垃圾。如果幸运的话,第一个垃圾字符不会是数字,并且 atoi 将正常工作。如果你运气不好,第一个垃圾字符将是一个数字。

除了缺少NUL之外,recv可能只获得1个字节,在这种情况下文件大小为1(假设第一个垃圾字符不是数字)。或者,recv 可能会获取 100 个字节(3 个字节的长度加上文件中的一些字节)。如果文件恰好以数字开头,则大小将会错误。

修复代码
在服务器端,将文件大小作为包含 NUL 的固定长度消息发送,例如

#define MSGLEN 24
char buffer[MSGLEN];
snprintf( buffer, MSGLEN, "%*ld", MSGLEN-1, file_size );
send( sockfd, buffer, MSGLEN, 0 );

在客户端,循环读取,直到获得该大小的所有字节,但不再有

#define MSGLEN 24
char buffer[MSGLEN];
int count = 0;
int index;
for ( index = 0; index < MSGLEN; index += count )
{
count = recv( sockfd, &buffer[index], MSGLEN - index, 0 );
if ( count <= 0 )
break;
}
if ( index != MSGLEN || buffer[MSGLEN-1] != '\0' )
{
printf( "bad file size\n" );
exit( 1 );
}
total_bytes = atoi( buffer );

最后一点
while(!feof(fp))-is-always-wrong

关于c - 使用套接字下载(获取)文件时观察不稳定的行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35092225/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com