问题
This is the server (sendfile) part:
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));
and this is the part of the client (recv file) part:
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);
The problem is that the string "226 File etc etc" is written inside the file that has been sent.
I've tried to do a small debug and so i've added a printf
after the for loop (server sendfile) and a printf
after the while loop (client) and i've noticed that the file is sent but on the client it doesn't exit from the while because the printf
isn't printed...
Why i got this strange behaviour??
br>
EDIT:
The server send the file size to the client whit this code:
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);
}
the client receives the fsize from the server with this code:
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;
both fsize
are declared as uint32_t
...
回答1:
Try this code:
Client side:
/* 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 side:
/* 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;
}
EDIT : Adding explanation from the man about the offset
The man page of send file said:
If offset is not NULL, then it points to a variable holding the file offset from which sendfile() will start reading data from in_fd.
When sendfile() returns, this variable will be set to the offset of the byte following the last byte that was read.
回答2:
The client doesn't know when the file ends. It just reads on until it has received fsize
bytes.
In your current implementaton the client will only work for files that are exactly fsize
bytes.
I suggest that you change your protocol and add a header that contains the file size. Why not use the http protocol?
回答3:
I think you should provide a more detailed code with at least the used variables declarations and initialisation.
How does the client part get the fsize value ?
You should also check the while condition like this :
while(((uint32_t)total_bytes_read < fsize) && ( .....
Don't use "!=", because if (for an unknown reason) total_bytes_read become greater than fsize, you'll be stucked in an infinite loop until the socket connection is closed (and read returns an error).
I also think (but not sure) that you should use recv instead of read in your client part.
回答4:
You should always determine the size of file you're sending as part of the protocol, for example, you can send the file size as the first 4 (or more, depending on file sizes you expect to handle) bytes, prior to the actual stream.
If you wish to work with constant size files, your implementation should work, in this case please add prints for fsize and total_bytes_read.
回答5:
You are using the sendfile
API incorrectly. Since you are passing in a non-NULL value in the third parameter, sendfile
will update the offset for you. But because your send loop is also updating the offset, you will be skipping over some bytes in the case that sendfile
was not able to send the whole file in a single call.
From the man page:
If
offset
is not NULL, then it points to a variable holding the file offset from whichsendfile()
will start reading data fromin_fd
. Whensendfile()
returns, this variable will be set to the offset of the byte following the last byte that was read.
You should remove this line from your send loop:
offset += rc;
Edit: In your update, you use (This was fixed in yet another update.) In any case, I have written a test program using the code you provided (with my suggested fix), and it seems to work fine with a less than 2KB file and a 3MB file.fpl
to get the fstat
information from your file, but in your sendfile
code, you use fd
. I would make sure these are what you expect them to be.
来源:https://stackoverflow.com/questions/11952898/c-send-and-receive-file