这是服务器(sendfile)部分:
offset = 0;
for (size_to_send = fsize; size_to_send > 0; ){
rc = sendfile(newsockd, fd, &offset, size_to_send);
if (rc <= 0){
perror("sendfile");
onexit(newsockd, sockd, fd, 3);
}
offset += rc;
size_to_send -= rc;
}
close(fd); /* la chiusura del file va qui altrimenti rischio loop infinito e scrittura all'interno del file */
memset(buffer, 0, sizeof(buffer));
strcpy(buffer, "226 File Successfully transfered\n");
if(send(newsockd, buffer, strlen(buffer), 0) < 0){
perror("Errore durante l'invio 226");
onexit(newsockd, sockd, 0, 2);
}
memset(buffer, 0, sizeof(buffer));
这是客户端(recv文件)部分的一部分:
fsize_tmp = fsize;
sInfo.filebuffer = malloc(fsize);
if(sInfo.filebuffer == NULL){
perror("malloc");
onexit(sockd, 0, fd, 4);
}
while(((uint32_t)total_bytes_read != fsize) && ((nread = read(sockd, sInfo.filebuffer, fsize_tmp)) > 0)){
if(write(fd, sInfo.filebuffer, nread) != nread){
perror("write RETR");
onexit(sockd, 0, 0, 1);
}
total_bytes_read += nread;
fsize_tmp -= nread;
}
close(fd); /* la chiusura del file va qui altrimenti client entra in loop infinito e si scrive all'interno del file */
memset(buffer, 0, sizeof(buffer));
if(recv(sockd, buffer, 34, 0) < 0){
perror("Errore ricezione 226");
onexit(sockd, 0, 0, 1);
}
printf("%s", buffer);
memset(buffer, 0, sizeof(buffer));
memset(dirpath, 0, sizeof(dirpath));
free(sInfo.filebuffer);
问题是字符串“226 File etc etc”写在里面已发送的文件。
我试过做一个小调试,所以我在for循环(服务器发送文件)之后添加了printf
,在while循环(客户端)之后添加了printf
,我注意到了文件已发送但在客户端上不会退出,因为printf
未打印...
为什么我有这种奇怪的行为?br br>
修改
服务器通过以下代码将文件大小发送给客户端:
fd = open(filename, O_RDONLY);
if(fd < 0){
error!!
}
if(fstat(fd, &fileStat) < 0){
perror("Errore fstat");
onexit(newsockd, sockd, fd, 3);
}
fsize = fileStat.st_size;
if(send(newsockd, &fsize, sizeof(fsize), 0) < 0){
perror("Errore durante l'invio della grandezza del file\n");
onexit(newsockd, sockd, fd, 3);
}
客户端使用以下代码从服务器接收fsize:
if(read(sockd, &fsize, sizeof(fsize)) < 0){
perror("Errore durante ricezione grandezza file\n");
onexit(sockd, 0 ,0 ,1);
}
fd = open(sInfo.filename, O_CREAT | O_WRONLY, 0644);
if (fd < 0) {
perror("open");
onexit(sockd, 0 ,0 ,1);
}
fsize_tmp = fsize;
fsize
都声明为uint32_t
...
答案 0 :(得分:16)
试试这段代码:
客户方:
/* Client code */
/* TODO : Modify to meet your need */
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>
#define PORT_NUMBER 5000
#define SERVER_ADDRESS "192.168.1.7"
#define FILENAME "/home/toc/foo.c"
int main(int argc, char **argv)
{
int client_socket;
ssize_t len;
struct sockaddr_in remote_addr;
char buffer[BUFSIZ];
int file_size;
FILE *received_file;
int remain_data = 0;
/* Zeroing remote_addr struct */
memset(&remote_addr, 0, sizeof(remote_addr));
/* Construct remote_addr struct */
remote_addr.sin_family = AF_INET;
inet_pton(AF_INET, SERVER_ADDRESS, &(remote_addr.sin_addr));
remote_addr.sin_port = htons(PORT_NUMBER);
/* Create client socket */
client_socket = socket(AF_INET, SOCK_STREAM, 0);
if (client_socket == -1)
{
fprintf(stderr, "Error creating socket --> %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
/* Connect to the server */
if (connect(client_socket, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr)) == -1)
{
fprintf(stderr, "Error on connect --> %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
/* Receiving file size */
recv(client_socket, buffer, BUFSIZ, 0);
file_size = atoi(buffer);
//fprintf(stdout, "\nFile size : %d\n", file_size);
received_file = fopen(FILENAME, "w");
if (received_file == NULL)
{
fprintf(stderr, "Failed to open file foo --> %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
remain_data = file_size;
while ((remain_data > 0) && ((len = recv(client_socket, buffer, BUFSIZ, 0)) > 0))
{
fwrite(buffer, sizeof(char), len, received_file);
remain_data -= len;
fprintf(stdout, "Receive %d bytes and we hope :- %d bytes\n", len, remain_data);
}
fclose(received_file);
close(client_socket);
return 0;
}
服务器端:
/* Server code */
/* TODO : Modify to meet your need */
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/sendfile.h>
#define PORT_NUMBER 5000
#define SERVER_ADDRESS "192.168.1.7"
#define FILE_TO_SEND "hello.c"
int main(int argc, char **argv)
{
int server_socket;
int peer_socket;
socklen_t sock_len;
ssize_t len;
struct sockaddr_in server_addr;
struct sockaddr_in peer_addr;
int fd;
int sent_bytes = 0;
char file_size[256];
struct stat file_stat;
int offset;
int remain_data;
/* Create server socket */
server_socket = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket == -1)
{
fprintf(stderr, "Error creating socket --> %s", strerror(errno));
exit(EXIT_FAILURE);
}
/* Zeroing server_addr struct */
memset(&server_addr, 0, sizeof(server_addr));
/* Construct server_addr struct */
server_addr.sin_family = AF_INET;
inet_pton(AF_INET, SERVER_ADDRESS, &(server_addr.sin_addr));
server_addr.sin_port = htons(PORT_NUMBER);
/* Bind */
if ((bind(server_socket, (struct sockaddr *)&server_addr, sizeof(struct sockaddr))) == -1)
{
fprintf(stderr, "Error on bind --> %s", strerror(errno));
exit(EXIT_FAILURE);
}
/* Listening to incoming connections */
if ((listen(server_socket, 5)) == -1)
{
fprintf(stderr, "Error on listen --> %s", strerror(errno));
exit(EXIT_FAILURE);
}
fd = open(FILE_TO_SEND, O_RDONLY);
if (fd == -1)
{
fprintf(stderr, "Error opening file --> %s", strerror(errno));
exit(EXIT_FAILURE);
}
/* Get file stats */
if (fstat(fd, &file_stat) < 0)
{
fprintf(stderr, "Error fstat --> %s", strerror(errno));
exit(EXIT_FAILURE);
}
fprintf(stdout, "File Size: \n%d bytes\n", file_stat.st_size);
sock_len = sizeof(struct sockaddr_in);
/* Accepting incoming peers */
peer_socket = accept(server_socket, (struct sockaddr *)&peer_addr, &sock_len);
if (peer_socket == -1)
{
fprintf(stderr, "Error on accept --> %s", strerror(errno));
exit(EXIT_FAILURE);
}
fprintf(stdout, "Accept peer --> %s\n", inet_ntoa(peer_addr.sin_addr));
sprintf(file_size, "%d", file_stat.st_size);
/* Sending file size */
len = send(peer_socket, file_size, sizeof(file_size), 0);
if (len < 0)
{
fprintf(stderr, "Error on sending greetings --> %s", strerror(errno));
exit(EXIT_FAILURE);
}
fprintf(stdout, "Server sent %d bytes for the size\n", len);
offset = 0;
remain_data = file_stat.st_size;
/* Sending file data */
while (((sent_bytes = sendfile(peer_socket, fd, &offset, BUFSIZ)) > 0) && (remain_data > 0))
{
fprintf(stdout, "1. Server sent %d bytes from file's data, offset is now : %d and remaining data = %d\n", sent_bytes, offset, remain_data);
remain_data -= sent_bytes;
fprintf(stdout, "2. Server sent %d bytes from file's data, offset is now : %d and remaining data = %d\n", sent_bytes, offset, remain_data);
}
close(peer_socket);
close(server_socket);
return 0;
}
编辑:添加关于offset
发送文件的手册页说:
如果offset不为NULL,则它指向保存文件的变量 sendfile()将从in_fd开始读取数据的偏移量 当sendfile()返回时,此变量将设置为读取的最后一个字节后的字节偏移量。
答案 1 :(得分:0)
客户端不知道文件何时结束。它只是读取,直到它收到fsize
个字节。
在您当前的实现中,客户端仅适用于精确fsize
字节的文件。
我建议您更改协议并添加包含文件大小的标头。 为什么不使用http协议?
答案 2 :(得分:0)
我认为您应该提供更详细的代码,至少使用已使用的变量声明和初始化。
客户端部分如何获取fsize值?
你还应该像这样检查while条件:
while(((uint32_t)total_bytes_read < fsize) && ( .....
不要使用“!= ”,因为如果(由于未知原因)total_bytes_read变得大于fsize,您将陷入无限循环,直到套接字连接关闭(并且read返回错误)。
我也认为(但不确定)您应该在客户端部分使用 recv 而不是读取。
答案 3 :(得分:0)
您应始终确定要作为协议的一部分发送的文件的大小,例如,您可以将文件大小发送为前4个(或更多,取决于您希望处理的文件大小)字节,先前到实际的流。
如果您希望使用常量文件,您的实现应该可以正常工作,在这种情况下,请为fsize和total_bytes_read添加打印。
答案 4 :(得分:0)
您错误地使用了sendfile
API。由于您在第三个参数中传入非NULL值,sendfile
将为您更新偏移量。但是因为你的发送循环也在更新偏移量,所以在sendfile
无法在一次调用中发送整个文件的情况下,你将跳过一些字节。
来自man page:
如果
offset
不为NULL,则它指向一个变量,该变量保存文件偏移量sendfile()
将从in_fd
开始读取数据。当sendfile()
返回时,此变量将被设置为读取的最后一个字节后的字节的偏移量。
您应该从发送循环中删除此行:
offset += rc;
修改 在您的更新中,您使用(这在另一个更新中得到修复。)无论如何,我写了一个test program using the code you provided (with my suggested fix),它似乎工作得很好超过2KB文件和3MB文件。fpl
从您的文件中获取fstat
信息,但在您的sendfile
代码中,使用fd
。我会确保这些是你期望它们的。