I try to send a file into the socket using the system call sendfile()
, but get the error: ERROR sendfile: [EINVAL Invalid argument]. But the call read()
works perfectly with the same arguments. I also tried to give to sendfile()
two files description in arguments - got the same result.
Do you have any suggestions why this might be?
Some additional info: OS: Ubuntu 22.04.1 LTS on VirtualBox: 6.1.38 r153438 (Qt5.6.2); Linux kernel: 5.15.0-52-generic; glibc: 2.35.
There are examples of the two SIMPLEST programs that I used:
sendfile()
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/sendfile.h>
#include "gen_hdr.h"
int main() {
/*** Get file size and open files ***/
int fd;
size_t size;
struct stat statBuf;
if (stat("test_file.txt", &statBuf) == -1) {
errExit("statBuf");
}
size = statBuf.st_size;
fd = open("test_file.txt", O_RDONLY);
if (-1 == fd) {
errExit("open");
}
int fdNew, openFlags;
mode_t filePerms;
openFlags = O_CREAT | O_WRONLY | O_EXCL;
filePerms = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP |
S_IROTH | S_IWOTH; /* rw-rw-rw- */
fdNew = open("clone_test_file.txt", openFlags, filePerms);
/*** END ***/
/*** SENDFILE ***/
off_t offset = 0;
ssize_t sent = 0;
for (; size > 0; ) {
errno = 0;
sent = sendfile(fdNew, fd, &offset, size);
printf("%s\n", strerror(errno));
printf("%ld\n", sent);
if (sent <= 0) { // Error or end of file
if (sent != 0) {
errExit("sendfile"); // Was an error, report it
}
break; // End of file
}
size -= sent; // Decrease the send length by the amount actually sent
}
/*** END ***/
if (close(fd) == -1) {
errExit("close");
}
if (close(fdNew) == -1) {
errExit("close");
}
return 0;
}
send()
(section SENDFILE) instead of sendfile()
- all works well.Server:
#include <ctype.h>
#include <netinet/in.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/sendfile.h>
#include "gen_hdr.h"
#define PORT_NUM 55550
#define BACKLOG 5
int main() {
/*** Get file size and open it ***/
int fd;
size_t size_to_send;
struct stat statBuf;
if (stat("test_file.txt", &statBuf) == -1) {
errExit("statBuf");
}
size_to_send = statBuf.st_size;
fd = open("test_file.txt", O_RDONLY);
if (-1 == fd) {
errExit("open");
}
/*** END ***/
/*** Server Setup ***/
struct sockaddr_in svaddr, claddr;
//struct sockaddr_storage claddr;
socklen_t len;
int sockfd, connfd;
char claddrStr[INET_ADDRSTRLEN];
memset(&svaddr, 0, sizeof(struct sockaddr_in));
svaddr.sin_family = AF_INET;
svaddr.sin_port = (in_port_t)htons(PORT_NUM);
svaddr.sin_addr.s_addr = htonl(INADDR_ANY);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == sockfd) {
errExit("socket");
}
if (bind(sockfd, (struct sockaddr *)&svaddr, sizeof(struct sockaddr_in)) == -1) {
errExit("bind");
}
if (listen(sockfd, BACKLOG) == -1) {
errExit("listen");
}
printf("Server is waiting for connection...\n");
len = sizeof(struct sockaddr_in);
connfd = accept(sockfd, (struct sockaddr*)&claddr, &len);
if (-1 == connfd) {
errExit("accept");
}
if (inet_ntop(AF_INET, &claddr.sin_addr, claddrStr, INET_ADDRSTRLEN) == NULL) {
printf("Coudn't convert client address to string\n");
}
else {
printf("Connection from [%s, %u]\n", claddrStr, ntohs(claddr.sin_port));
}
/*** END ***/
/*** SENDFILE ***/
off_t offset = 0;
ssize_t sent = 0;
for (; size_to_send > 0; ) {
errno = 0;
sent = sendfile(connfd, fd, &offset, size_to_send);
printf("%s\n", strerror(errno));
printf("%ld\n", sent);
if (sent <= 0) { // Error or end of file
if (sent != 0) {
errExit("sendfile"); // Was an error, report it
}
break; // End of file
}
size_to_send -= sent; // Decrease the send length by the amount actually sent
}
/*** END ***/
/*** SEND FILE ***/
// char* buf = (char*)malloc(size_to_send);
// if (NULL == buf) {
// errExit("malloc");
// }
// if (read(fd, buf, size_to_send) == -1) {
// errExit("read");
// }
// if (send(connfd, buf, size_to_send, 0) == -1) {
// errExit("send");
// }
// free(buf);
/*** END ***/
printf("File sent...\n");
if (close(fd) == -1) {
errExit("close");
}
if (close(connfd) == -1) {
printf("ERROR on closing connection\n");
}
if (close(sockfd) == -1) {
printf("ERROR on closing socket\n");
}
return 0;
}
Client:
#include <netinet/in.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "gen_hdr.h"
#define PORT_NUM 55550
#define BUF_SIZE 10
int main(int argc, char* argv[]) {
/*** Get file size and allocate memory ***/
char* buf;
size_t size_to_recv;
struct stat statBuf;
if (stat("test_file.txt", &statBuf) == -1) {
errExit("statBuf");
}
size_to_recv = statBuf.st_size;
buf = (char*)malloc(size_to_recv);
if (NULL == buf) {
errExit("malloc");
}
/*** END ***/
/*** Server Setup ***/
struct sockaddr_in svaddr;
socklen_t len;
int sockfd;
ssize_t numBytes;
char claddrStr[INET_ADDRSTRLEN];
memset(&svaddr, 0, sizeof(struct sockaddr_in));
svaddr.sin_family = AF_INET;
svaddr.sin_port = (in_port_t)htons(PORT_NUM);
svaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == sockfd) {
errExit("socket");
}
if (connect(sockfd, (struct sockaddr *)&svaddr, sizeof(struct sockaddr_in)) == -1) {
errExit("connect");
}
/*** END ***/
printf("Getting file...\n");
/*** RECEIVE FILE ***/
ssize_t numRead; /* Bytes that we read from the last call of read() */
size_t totRead; /* Number of bytes that we have read at the moment */
char* recvBuf = buf;
for (totRead = 0; totRead < size_to_recv; ) {
numRead = read(sockfd, recvBuf, size_to_recv - totRead);
if (numRead > 0) {
totRead += numRead;
recvBuf += numRead;
} else if (numRead == 0) { /* End of file */
return totRead; /* It could be 0, if it is the first call of read() */
} else { /* Error */
if (errno == EINTR) {
continue; /* Interrupted --> recall read() */
}
else {
errExit("read"); /* Some other error */
}
}
}
/*** END ***/
printf("Ended\n");
/*** WRITE IN NEW FILE ***/
int fd, openFlags;
mode_t filePerms;
openFlags = O_CREAT | O_WRONLY | O_EXCL;
filePerms = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP |
S_IROTH | S_IWOTH; /* rw-rw-rw- */
fd = open("clone_test_file.txt", openFlags, filePerms);
if (write(fd, buf, size_to_recv) == -1) {
errExit("write");
}
/*** END ***/
free(buf);
if (close(fd) == -1) {
errExit("close");
}
if (close(sockfd) == -1) {
errExit("close");
}
return 0;
}
"gen_hdr.h" just includes "standard" libs and declaration of error handling functions:
#ifndef GEN_HDR_H
#define GEN_HDR_H
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdint.h>
typedef enum {FALSE, TRUE} boolean;
#include "error_functions.h"
#define max(a,b) (((a) > (b)) ? (a) : (b))
#define min(a,b) (((a) < (b)) ? (a) : (b))
#endif /* GEN_HDR_H */
Error_functions.c:
#include "gen_hdr.h"
#include "ename.c.inc"
static void outputError(int err, boolean flushStdout, const char* msg) {
#define BUF_SIZE 500
char buf[BUF_SIZE];
snprintf(buf, BUF_SIZE, "ERROR %s: [%s %s]\n", msg, ((err > 0 && err < MAX_ENAME) ? ename[err] : "?UNKNOWN?"), strerror(err));
if (flushStdout)
{
fflush(stdout);
}
fputs(buf, stderr);
fflush(stderr);
}
void errExit(char* msg) {
outputError(errno, TRUE, msg);
exit(EXIT_FAILURE);
}
void errExitEN(int errNum, char* msg) {
outputError(errNum, TRUE, msg);
exit(EXIT_FAILURE);
}
If a file is located in the shared with windows folder, sendfile()
doesn't work, although 'mmap()' works.