Linux send file tcp

Send and Receive a file in socket programming in Linux with C/C++ (GCC/G++)

I would like to implement a client-server architecture running on Linux using sockets and C/C++ language that is capable of sending and receiving files. Is there any library that makes this task easy? Could anyone please provide an example?

4 Answers 4

The most portable solution is just to read the file in chunks, and then write the data out to the socket, in a loop (and likewise, the other way around when receiving the file). You allocate a buffer, read into that buffer, and write from that buffer into your socket (you could also use send and recv , which are socket-specific ways of writing and reading data). The outline would look something like this:

while (1) < // Read data into buffer. We may not have enough to fill up buffer, so we // store how many bytes were actually read in bytes_read. int bytes_read = read(input_file, buffer, sizeof(buffer)); if (bytes_read == 0) // We're done reading from the file break; if (bytes_read < 0) < // handle errors >// You need a loop for the write, because not all of the data may be written // in one call; write will return how many bytes were written. p keeps // track of where in the buffer we are, while we decrement bytes_read // to keep track of how many bytes are left to write. void *p = buffer; while (bytes_read > 0) < int bytes_written = write(output_socket, p, bytes_read); if (bytes_written bytes_read -= bytes_written; p += bytes_written; > > 

Make sure to read the documentation for read and write carefully, especially when handling errors. Some of the error codes mean that you should just try again, for instance just looping again with a continue statement, while others mean something is broken and you need to stop.

For sending the file to a socket, there is a system call, sendfile that does just what you want. It tells the kernel to send a file from one file descriptor to another, and then the kernel can take care of the rest. There is a caveat that the source file descriptor must support mmap (as in, be an actual file, not a socket), and the destination must be a socket (so you can’t use it to copy files, or send data directly from one socket to another); it is designed to support the usage you describe, of sending a file to a socket. It doesn’t help with receiving the file, however; you would need to do the loop yourself for that. I cannot tell you why there is a sendfile call but no analogous recvfile .

Beware that sendfile is Linux specific; it is not portable to other systems. Other systems frequently have their own version of sendfile , but the exact interface may vary (FreeBSD, Mac OS X, Solaris).

Читайте также:  Change linux user home directory

In Linux 2.6.17, the splice system call was introduced, and as of 2.6.23 is used internally to implement sendfile . splice is a more general purpose API than sendfile . For a good description of splice and tee , see the rather good explanation from Linus himself. He points out how using splice is basically just like the loop above, using read and write , except that the buffer is in the kernel, so the data doesn’t have to transferred between the kernel and user space, or may not even ever pass through the CPU (known as «zero-copy I/O»).

Источник

Linux send file tcp

Вызов sendfile() копирует данные из одного файлового дескриптора в другой. Так как копирование производится в ядре, использование sendfile() более эффективно чем комбинация read(2) и write(2), в которой требуется скопировать данные в и из пользовательского пространства. В in_fd должен указываться файловый дескриптор, открытый для чтения, а в out_fd должен указываться файловый дескриптор, открытый для записи. Если значение offset не равно NULL, то оно указывает на переменную, содержащую файловое смещение с которого sendfile() начнёт чтение данных из in_fd. При завершении sendfile() значение этой переменной будет содержать указатель на следующий байт после последнего прочитанного. Если значение offset не равно NULL, то sendfile() не изменяет текущее файловое смещение in_fd; иначе текущее файловое смещение изменяется для отражения количества прочитанных из in_fd байт. Если значение offset равно NULL, то данные будут прочитаны из in_fd начиная с текущего файлового смещения, и по окончании работы вызова файловое смещение будет обновлено. В count содержится количество байт, копируемых между файловыми дескрипторами. Значение in_fd должно описывать файл, который поддерживает операции типа mmap(2) (т.е., не сокет). В ядрах Linux до версии 2.6.33, значение out_fd должно указывать на сокет. Начиная с Linux 2.6.33 можно указывать любой файл. Если это обычный файл, то sendfile() изменит файловое смещение соответствующим образом.

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ

Если пересылка прошла успешно, то возвращается количество записанных в out_fd байт. Заметим, что при успешном выполнении вызов sendfile() может записать меньше байт, чем запрашивалось; вызывающий должен быть готов повторить вызов, если останутся не отправленные байты. Также смотрите ЗАМЕЧАНИЯ. В случае ошибки возвращается -1 и значение errno устанавливается соответствующим образом.

ОШИБКИ

EAGAIN Был выбран неблокирующий ввод-вывод с помощью O_NONBLOCK, но запись привела бы блокировке. EBADF Входной файл не открыт для чтения или выходной файл не открыт для записи. EFAULT Неправильный адрес. EINVAL Неправильный или заблокированный дескриптор, или для in_fd недоступна операция типа mmap(2) или значение count отрицательно. EINVAL У out_fd установлен флаг O_APPEND. Пока это не поддерживается в sendfile(). EIO Неизвестная ошибка при чтении in_fd. ENOMEM Не хватает памяти для чтения in_fd. EOVERFLOW Значение count слишком велико, операция вернула бы результат, превышающий максимальный размер входного или выходного файла. ESPIPE Значение offset не равно NULL, но для входного файла не работает seek(2).

Читайте также:  Linux mint восстановление пароля

ВЕРСИИ

СООТВЕТСТВИЕ СТАНДАРТАМ

Отсутствует в POSIX.1-2001 и других стандартах. В других системах UNIX вызов sendfile() реализован с другими семантиками и прототипами. Не должен использоваться в переносимых программах.

ЗАМЕЧАНИЯ

Вызов sendfile() передаст не больше 0x7ffff000 (2 147 479 552) байт, возвращая число байт, переданных на самом деле (это утверждение справедливо как к 32-битным, так и к 64-битным системам). Если вы планируете использовать sendfile() для отправки файлов через сокет TCP и вам нужно послать некоторые заголовочные данные перед содержимым файла, то обратите внимание на параметр TCP_CORK, описанный в tcp(7), он поможет минимизировать количество пакетов и оптимизировать производительность. В Linux 2.4 и более ранних, значение out_fd может также указывать на обычный файл; эта возможность была удалена в ядрах Linux 2.6.x, но возвращена в 2.6.33. Первоначальная версия Linux sendfile() не была приспособлена для работы с большими файловыми смещениями. В последствии в Linux 2.4 был добавлен вызов sendfile64() с более широким диапазоном значений аргумента offset. В glibc sendfile() представляет собой обёрточную функцию, которая делает незаметным разницу между версиями ядер. Приложение может попытаться воспользоваться read(2)/write(2), если вызов sendfile() завершится с ошибкой EINVAL или ENOSYS. Если out_fd ссылается на сокет или канал с поддержкой нулевого копирования, то вызывающие должны гарантировать, что переданные части файла, указываемого in_fd, останутся неизменёнными до тех пор, пока читающий на другом конце out_fd не закончит обрабатывать переданные данные. Специальный вызов Linux splice(2) поддерживает пересылку данных между произвольными файлами (например, между парой сокетов).

Источник

File transfer using TCP on Linux

I’m trying TCP file transfer on Linux. After establishing the connection, the server should send «send.txt» to the client, and the client receives the file and saves it as «receive.txt». Then the connection breaks. The correct input and output should be: Server terminal:

$./server & [server] obtain socket descriptor successfully. [server] bind tcp port 5000 in addr 0.0.0.0 successfully. [server] listening the port 5000 successfully. [server] server has got connect from 127.0.0.1. [server] send send.txt to the client…ok! [server] connection closed. 
$./client [client] connected to server at port 5000…ok! [client] receive file sent by server to receive.txt…ok! [client] connection lost. 
$ ./server & [server] obtain socket descriptor successfully. [server] bind tcp port 5000 in addr 0.0.0.0 sucessfully. [server] listening the port 5000 sucessfully. [server] server has got connect from 127.0.0.1. [server] send send.txt to the client. ok! [server] connection closed. /*Here the server doesn't exit*/ $ ./client [client] connected to server at port 5000. ok! /*Here the client doesn't exit*/ 

Also, an EMPTY «receive.txt» is generated. My code was first written for transferring simple strings, and it worked correctly. So I guess the problem lies in the file transferring part. My code is as follows: server.c

#include #include #include #include #include #include #include #include #define PORT 5000 // The port which is communicate with server #define BACKLOG 10 #define LENGTH 512 // Buffer length int main () < int sockfd; // Socket file descriptor int nsockfd; // New Socket file descriptor int num; int sin_size; // to store struct size struct sockaddr_in addr_local; struct sockaddr_in addr_remote; /* Get the Socket file descriptor */ if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ) < printf ("ERROR: Failed to obtain Socket Descriptor.\n"); return (0); >else printf ("[server] obtain socket descriptor successfully.\n"); /* Fill the local socket address struct */ addr_local.sin_family = AF_INET; // Protocol Family addr_local.sin_port = htons(PORT); // Port number addr_local.sin_addr.s_addr = INADDR_ANY; // AutoFill local address bzero(&(addr_local.sin_zero), 8); // Flush the rest of struct /* Bind a special Port */ if( bind(sockfd, (struct sockaddr*)&addr_local, sizeof(struct sockaddr)) == -1 ) < printf ("ERROR: Failed to bind Port %d.\n",PORT); return (0); >else printf("[server] bind tcp port %d in addr 0.0.0.0 sucessfully.\n",PORT); /* Listen remote connect/calling */ if(listen(sockfd,BACKLOG) == -1) < printf ("ERROR: Failed to listen Port %d.\n", PORT); return (0); >else printf ("[server] listening the port %d sucessfully.\n", PORT); int success = 0; while(success == 0) < sin_size = sizeof(struct sockaddr_in); /* Wait a connection, and obtain a new socket file despriptor for single connection */ if ((nsockfd = accept(sockfd, (struct sockaddr *)&addr_remote, &sin_size)) == -1) printf ("ERROR: Obtain new Socket Despcritor error.\n"); else printf ("[server] server has got connect from %s.\n", inet_ntoa(addr_remote.sin_addr)); /* Child process */ if(!fork()) < char* f_name = "send.txt"; char sdbuf[LENGTH]; // Send buffer printf("[server] send %s to the client. ", f_name); FILE *fp = fopen(f_name, "r"); if(fp == NULL) < printf("ERROR: File %s not found.\n", f_name); exit(1); >bzero(sdbuf, LENGTH); int f_block_sz; while((f_block_sz = fread(sdbuf, sizeof(char), LENGTH, fp))>0) < if(send(nsockfd, sdbuf, f_block_sz, 0) < 0) < printf("ERROR: Failed to send file %s.\n", f_name); break; >bzero(sdbuf, LENGTH); > printf("ok!\n"); success = 1; close(nsockfd); printf("[server] connection closed.\n"); while(waitpid(-1, NULL, WNOHANG) > 0); > > > 
#include #include #include #include #include #include #include #include #define PORT 5000 #define LENGTH 512 // Buffer length int main(int argc, char *argv[]) < int sockfd; // Socket file descriptor char revbuf[LENGTH]; // Receiver buffer struct sockaddr_in remote_addr; /* Get the Socket file descriptor */ if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) < printf("ERROR: Failed to obtain Socket Descriptor!\n"); return (0); >/* Fill the socket address struct */ remote_addr.sin_family = AF_INET; remote_addr.sin_port = htons(PORT); inet_pton(AF_INET, "127.0.0.1", &remote_addr.sin_addr); bzero(&(remote_addr.sin_zero), 8); /* Try to connect the remote */ if (connect(sockfd, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr)) == -1) < printf ("ERROR: Failed to connect to the host!\n"); return (0); >else printf("[client] connected to server at port %d. ok!\n", PORT); //printf ("OK: Have connected to %s\n",argv[1]); printf("[client] receive file sent by server to receive.txt. "); char* f_name = "receive.txt"; FILE *fp = fopen(f_name, "a"); if(fp == NULL) printf("File %s cannot be opened.\n", f_name); else < bzero(revbuf, LENGTH); int f_block_sz = 0; int success = 0; while(success == 0) < while(f_block_sz = recv(sockfd, revbuf, LENGTH, 0)) < if(f_block_sz < 0) < printf("Receive file error.\n"); break; >int write_sz = fwrite(revbuf, sizeof(char), f_block_sz, fp); if(write_sz < f_block_sz) < printf("File write failed.\n"); break; >bzero(revbuf, LENGTH); > printf("ok!\n"); success = 1; fclose(fp); > > close (sockfd); printf("[client] connection lost.\n"); return (0); > 

Источник

Оцените статью
Adblock
detector