Create unbuffered file descriptor in C under linux - c

For testing purposes I want to create a fully unbuffered file descriptor under linux in C.
It shall have a reading and a writing side.
It can be one of:
fifo
pipe
local or tcp socket
using stdin/stdout
virtual kernel file (e.g. /proc/uptime)
(I think the list is complete)
So a call to write() will copy the contents directly into the buffer provided by read().
Is this even possible or is there always a buffer in between?
If it's possible than how to achieve this?
This is my code:
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
int main(int argc, char** argv)
{
int fd = socket(AF_INET, SOCK_STREAM, 0);
int len = 0;
setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &len, 4);
struct sockaddr_in serv_addr = {};
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(80);
inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr);
connect(fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
char buf;
read(fd, &buf, 1);
return 0;
}
But the data is not read directly into buf. The reason is that my setsockopt() call does not really set the receiving buffer to zero. Instead it is set to the minimum value 256 as can be read on man page socket(7).

So a call to write() will copy the contents directly into the buffer provided by read()
Is this even possible
No. Only when using splice() or sendfile() or similar system call. This strongly depends on what the actual file descriptor refers to.
is there always a buffer in between? If it's possible than how to achieve this?
No. You can:
fifo / pipe
use splice() or sendfile()
use shared memory instead
local or tcp socket
open /proc/mem and write directly into the hardware.
write/use a specific kernel device driver that does the same
(I do not know, but maybe copy_from_user allows copying straight to a hardware address)
using stdin/stdout
file descriptor is just a pointer - both stdin and stdout can refer to a tcp socket, to a fifo, or to anything else
this depends on what the file descriptor refers to
virtual kernel file (e.g. /proc/uptime)
depends on the actual "virtual kernel file" implementation
most, if not all, implement seeking/support partial reading or use struct seq_file, which has to have an intermediary buffer.

Related

Broken Pipe for C-Socket. How to only keep server running?

In a simple program where I'm trying to send command-line inputs from client to server, I keep getting a "Broken Pipe" for the server side. I send a string to the server and the server returns the string as lower-case to the client.
Server:
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include<string.h>
#include <ctype.h>
#include <unistd.h>
int main()
{
char str[100];
int listen_fd, comm_fd;
struct sockaddr_in servaddr;
listen_fd = socket(AF_INET, SOCK_STREAM, 0);
bzero( &servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htons(INADDR_ANY);
servaddr.sin_port = htons(37892);
bind(listen_fd, (struct sockaddr *) &servaddr, sizeof(servaddr));
listen(listen_fd, 10);
comm_fd = accept(listen_fd, (struct sockaddr*) NULL, NULL);
while(1){
bzero( str, 100);
read(comm_fd,str,100);
for(int i = 0; i < strlen(str); i++){
str[i] = tolower(str[i]);
}
printf("Echoing back - %s",str);
write(comm_fd, str, strlen(str)+1);
}
}
Client
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include<string.h>
#include<ctype.h>
#include <unistd.h>
int main(int argc,char **argv)
{
int sockfd,n;
char sendline[100];
char recvline[100];
struct sockaddr_in servaddr;
sockfd=socket(AF_INET,SOCK_STREAM,0);
bzero(&servaddr,sizeof servaddr);
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(37892);
inet_pton(AF_INET,"127.0.0.1",&(servaddr.sin_addr));
connect(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
if(argc==1) printf("\nNo arguments");
if (1){
{
bzero( sendline, 100);
bzero( recvline, 100);
strcpy(sendline, argv[1]);
write(sockfd,sendline,strlen(sendline)+1);
read(sockfd,recvline,100);
printf("%s",recvline);
}
}
}
The problem I found was that when the client's side is done sending the string, the command line input does not work like fgets() where the loop will wait for another user input. If I change the if(1) in the client's side to a while(1), it will obviously run an infinite loop as no new inputs are being added.
The dilemma is, how would I be able to keep the server's side running to continuously return the string to the client while processing single requests from the command line on the client's side?
Your program has two problems:
1) read() works differently than you think:
Normally read() will read up to a certain number of bytes from some file or stream (e.g. socket).
Because read() does not distinguish between different types of bytes (e.g. letters, the end-of-line marker or even the NUL byte) read() will not work like fgets() (reading line-wise).
read() is also allowed to "split" the data: If you do a write(..."Hello\n"...) on the client the server may receive "Hel" the first time you call read() and the next time it receives "lo\n".
And of course read() can concatenate data: Call write(..."Hello\n"...) and write(..."World\n"...) on the client and one single read() call may receive "Hello\nWorld\n".
And of course both effects may appear at the same time and you have to call read() three times receiving "Hel", "lo\nWo" and "rld\n".
TTYs (= the console (keyboard) and serial ports) have a special feature (which may be switched off) that makes the read() call behave like fgets(). However only TTYs have such a feature!
In the case of sockets read() will always wait for at least one byte to be received and return the (positive) number of bytes received as long as the connection is alive. As soon as read() returns zero or a negative value the connection has been dropped.
You have to use a while loop that processes data until the connection has been dropped.
You'll have to check the data received by read() if it contains the NUL byte to detect the "end" of the data - if "your" data is terminated by a NUL byte.
2) As soon as the client drops the connection the handle returned by accept() is useless.
You should close that handle to save memory and file descriptors (there is a limit on how many file descriptors you can have open at one time).
Then you have to call accept() again to wait for the client to establish a new connection.
Your client sends one request and reads one response.
It then exits without closing the socket.
Your server runs in a loop reading requests and sending responses.
Your server ignores end of stream.
Little or none of this code is error-checked.

Linux TCP recv() with MSG_TRUNC - writes to buffer?

I've just encountered a surprising buffer overflow, while trying to use the flag MSG_TRUNC in recv on a TCP socket.
And it seems to only happen with gcc (not clang) and only when compiling with optimization.
According to this link: http://man7.org/linux/man-pages/man7/tcp.7.html
Since version 2.4, Linux supports the use of MSG_TRUNC in the flags argument of recv(2) (and recvmsg(2)). This flag causes the received bytes of data to be discarded, rather than passed back in a caller-supplied buffer. Since Linux 2.4.4, MSG_PEEK also has this effect when used in conjunction with MSG_OOB to receive out-of-band data.
Does this mean that a supplied buffer will not be written to? I expected so, but was surprised.
If you pass a buffer (non-zero pointer) and size bigger than the buffer size, it results in buffer overflow when client sends something bigger than buffer. It doesn't actually seem to write the message to the buffer if the message is small and fits in the buffer (no overflow).
Apparently if you pass a null pointer the problem goes away.
Client is a simple netcat sending a message bigger than 4 characters.
Server code is based on:
http://www.linuxhowtos.org/data/6/server.c
Changed read to recv with MSG_TRUNC, and buffer size to 4 (bzero to 4 as well).
Compiled on Ubuntu 14.04. These compilations work fine (no warnings):
gcc -o server.x server.c
clang -o server.x server.c
clang -O2 server.x server.c
This is the buggy (?) compilation, it also gives a warning hinting about the problem:
gcc -O2 -o server.x server.c
Anyway like I mentioned changing the pointer to null fixes the problem, but is this a known issue? Or did I miss something in the man page?
UPDATE:
The buffer overflow happens also with gcc -O1.
Here is the compilation warning:
In function ‘recv’,
inlined from ‘main’ at server.c:47:14:
/usr/include/x86_64-linux-gnu/bits/socket2.h:42:2: warning: call to ‘__recv_chk_warn’ declared with attribute warning: recv called with bigger length than size of destination buffer [enabled by default]
return __recv_chk_warn (__fd, __buf, __n, __bos0 (__buf), __flags);
Here is the buffer overflow:
./server.x 10003
* buffer overflow detected *: ./server.x terminated
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x7338f)[0x7fcbdc44b38f]
/lib/x86_64-linux-gnu/libc.so.6(__fortify_fail+0x5c)[0x7fcbdc4e2c9c]
/lib/x86_64-linux-gnu/libc.so.6(+0x109b60)[0x7fcbdc4e1b60]
/lib/x86_64-linux-gnu/libc.so.6(+0x10a023)[0x7fcbdc4e2023]
./server.x[0x400a6c]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf5)[0x7fcbdc3f9ec5]
./server.x[0x400879]
======= Memory map: ========
00400000-00401000 r-xp 00000000 08:01 17732 > /tmp/server.x
... more messages here
Aborted (core dumped)
And gcc version:
gcc (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4
The buffer and recv call:
char buffer[4];
n = recv(newsockfd,buffer,255,MSG_TRUNC);
And this seems to fix it:
n = recv(newsockfd,NULL,255,MSG_TRUNC);
This will not generate any warnings or errors:
gcc -Wall -Wextra -pedantic -o server.x server.c
And here is the complete code:
/* 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>
void error(const char *msg)
{
perror(msg);
exit(1);
}
int main(int argc, char *argv[])
{
int sockfd, newsockfd, portno;
socklen_t clilen;
char buffer[4];
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, 0);
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,4);
n = recv(newsockfd,buffer,255,MSG_TRUNC);
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");
close(newsockfd);
close(sockfd);
return 0;
}
UPDATE:
Happens also on Ubuntu 16.04, with gcc version:
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.2) 5.4.0 20160609
I think you have misunderstood.
With datagram sockets, MSG_TRUNC option behaves as described in man 2 recv man page (at Linux man pages online for most accurate and up to date information).
With TCP sockets, the explanation in the man 7 tcp man page is a bit poorly worded. I believed it is not a discard flag, but a truncate (or "throw away the rest") operation. However, the implementation (in particular, net/ipv4/tcp.c:tcp_recvmsg() function in the Linux kernel handles the details for TCP/IPv4 and TCP/IPv6 sockets) indicates otherwise.
There is also a separate MSG_TRUNC socket flag. These are stored in the error queue associated with the socket, and can be read using recvmsg(socketfd, &msg, MSG_ERRQUEUE). It indicates a datagram that was read was longer than the buffer, so some of it was lost (truncated). This is rarely used, because it is really only relevant to datagram sockets, and there are much easier ways to determine overlength datagrams.
Datagram sockets:
With datagram sockets, the messages are separate, and not merged. When read, the unread part of each received datagram is discarded.
If you use
nbytes = recv(socketfd, buffer, buffersize, MSG_TRUNC);
it means that the kernel will copy up to first buffersize bytes of the next datagram, and discard the rest of the datagram if it is longer (as usual), but nbytes will reflect the true length of the datagram.
In other words, with MSG_TRUNC, nbytes may exceed buffersize, even though only up to buffersize bytes are copied to buffer.
TCP sockets in Linux, kernels 2.4 and later, edited:
A TCP connection is stream-like; there are no "messages" or "message boundaries", just a sequence of bytes flowing. (Although, there can be out-of-band data, but that is not pertinent here).
If you use
nbytes = recv(socketfd, buffer, buffersize, MSG_TRUNC);
the kernel will discard up to next buffersize bytes, whatever is already buffered (but will block until at least one byte is buffered, unless the socket is in non-blocking mode or MSG_TRUNC | MSG_DONTWAIT is used instead). The number of bytes discarded is returned in nbytes.
However, both buffer and buffersize should be valid, because a recv() or recvfrom() call goes through the kernel net/socket.c:sys_recvfrom() function, which verifies buffer and buffersize are valid, and if so, populates the internal iterator structure to match, before calling the aforementioned net/ipv4/tcp.c:tcp_recvmsg().
In other words, the recv() with a MSG_TRUNC flag does not actually try to modify buffer. However, the kernel does check if buffer and buffersize are valid, and if not, will cause the recv() syscall to fail with -EFAULT.
When buffer overflow checks are enabled, GCC and glibc recv() does not just return -1 with errno==EFAULT; it instead halts the program, producing the shown backtraces. Some of these checks include mapping the zero page (where the target of a NULL pointer resides in Linux on x86 and x86-64), in which case the access check done by the kernel (before actually trying to read or write to it) succeeds.
To avoid the GCC/glibc wrappers (so that code compiled with e.g. gcc and clang should behave the same), one can use real_recv() instead,
#define _GNU_SOURCE
#include <unistd.h>
#include <sys/syscall.h>
#include <errno.h>
ssize_t real_recv(int fd, void *buf, size_t n, int flags)
{
long retval = syscall(SYS_recvfrom, fd, buf, n, flags, NULL, NULL);
if (retval < 0) {
errno = -retval;
return -1;
} else
return (ssize_t)retval;
}
which calls the syscall directly. Note that this does not include the pthreads cancellation logic; use this only in single-threaded test programs.
In summary, with the stated problem regarding MSG_TRUNC flag for recv() when using TCP sockets, there are several factors complicating the full picture:
recv(sockfd, data, size, flags) actually calls the recvfrom(sockfd, data, size, flags, NULL, NULL) syscall (there is no recv syscall in Linux)
With a TCP socket, recv(sockfd, data, size, MSG_TRUNC) acts as if it were to read up to size bytes into data, if (char *)data+0 to (char *)data+size-1 are valid; it just does not copy them into data. The number of bytes thus skipped is returned.
The kernel verifies data (from (char *)data+0 to (char *)data+size-1, inclusive) is readable, first. (I suspect this check is erroneous, and might be turned into a writability check sometime in the future, so do not rely on this being a readability test.)
Buffer overflow checks can detect the -EFAULT result from the kernel, and instead halts the program with some kind of "out of bounds" error message (with a stack trace)
Buffer overflow checks may make NULL pointer seem like valid from the kernel point of view (because the kernel test is for reading, currently), in which case the kernel verification accepts the NULL pointer as valid. (One can verify if this is the case by recompiling without buffer overflow checks, using e.g. the above real_recv(), and seeing if a NULL pointer causes an -EFAULT result then.)
The reason for such a mapping (that, if allowed by hardware and the kernel structures, only exists, and is not readable or writable) is that with such a mapping, any access generates a SIGBUS signal, which a (library or compiler-provided signal handler) can catch, and dump not just a stack trace, but more details about the exact access (address, code that attempted the access, and so on).
I do believe the kernel access check treats such mappings readable and writable, because there needs to be a read or write attempt for the signal to be generated.
Buffer overflow checks are done by both the compiler and the C library, so different compilers may implement the checks, and the NULL pointer case, differently.
Nota bene: I’m adding this answer here after all this time, as this is still one of the first results on google for recv buffer overflow MSG_TRUNC, and if someone else ends up here, they’ll save themselves a lot of grief, searching and trial-and-error.
The original question is answered well enough already, but the subtlety I wanted to highlight, is the difference between stream and datagram sockets.
A common code pattern is to use recv( socket_, NULL, 0, MSG_DONTWAIT | MSG_PEEK | MSG_TRUNC ) to find how much data is queued before a read. This works perfectly for stream sockets (TCP and SCTP) but for datagram sockets (UDP, UDPL and DCCP) it will intermittently buffer overflow, but only if the executable is compiled with gcp and with optimisations enabled. Without optimisations it seems to work perfectly, which means it will sail through development QA, only to fail in staging/live.
Finding this was a total PITA. You’re welcome. ;)

C trying to understand select() and FD_ISSET()

Im trying to make a basic non blocking chat client, but i cant really understand select() and FD_ISSET(). im trying to listen to the socket with the code below, but it wont work, it doesn't print anything, why not?
#include <string.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
int main(int argc, char const* argv[])
{
fd_set readfs;
char sendline[100];
char str[100];
char *some_addr;
int listen_fd, comm_fd;
struct sockaddr_in servaddr;
listen_fd = socket(AF_INET, SOCK_STREAM, 0);
//Socket error
if (listen_fd == -1) {
printf("Error on getting socket, Exiting!\n");
return 1;
}
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htons(INADDR_ANY);
servaddr.sin_port=htons(22000);
bind(listen_fd, (struct sockaddr *) &servaddr, sizeof(servaddr));
listen(listen_fd, 10);
comm_fd = accept(listen_fd, (struct sockaddr *) NULL, NULL);
FD_ZERO(&readfs);
FD_SET(comm_fd, &readfs);
while (1)
{
select(listen_fd,&readfs, NULL, NULL, NULL);
if(FD_ISSET(listen_fd,&readfs))
{
bzero(str,100);
read(listen_fd,str,100);
printf("%s", str);
/* write(listen_fd, "read!", strlen(str)+1); */
}
}
return 0;
}
EDIT:
My code trying to Connect to the server:
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include<string.h>
int main(int argc,char **argv)
{
int sockfd,n;
char sendline[100];
char recvline[100];
struct sockaddr_in servaddr;
sockfd=socket(AF_INET,SOCK_STREAM,0);
bzero(&servaddr,sizeof servaddr);
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(22000);
inet_pton(AF_INET,"127.0.0.1",&(servaddr.sin_addr));
connect(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
while(1)
{
bzero( sendline, 100);
bzero( recvline, 100);
fgets(sendline,100,stdin); /*stdin = 0 , for standard input */
write(sockfd,sendline,strlen(sendline)+1);
read(sockfd,recvline,100);
printf("%s\n",recvline);
}
return 0;
}
Four major problems, here:
Your select() call and the read/write loop should be using comm_fd, not listen_fd. If you call select() on listen_fd it'll return when there is an accept()able connection available, but you want to wait on the connected socket you already have for input, so use comm_fd.
The first argument to select() should be the highest file descriptor in the sets plus one. Since you only have one file descriptor, here, it should be comm_fd + 1.
You should move your FD_ZERO and FD_SET macros inside the while loop, and execute them prior to every select() call, because select() is going to modify those fd sets you pass to it.
You don't check the return from your system calls for errors. You should.
Other points:
bzero() has been removed from POSIX for quite some time, now, you should be using the standard memset() instead.
You shouldn't pass INADDR_ANY though htons(), just use it as it is.
It's only a comment in your program, but while STDIN_FILENO may be 0, stdin is a FILE pointer, and is not 0.
but i cant really understand select() and FD_ISSET()
An fd_set is like a bit array. Each bit in the array represents a socket or file descriptor.
FD_ISSET() is a macro or function that tells you whether a given socket descriptor (4, for example) is set in the bit array (fd_set). FD_SET() allows you to set a bit yourself, and FD_CLR() lets you clear a bit.
The bits don't just get set magically, you use select() to ask the OS kernel to set or clear each bit in the fd_set accordingly, then you check each bit with FD_ISSET() and act accordingly. Before calling select() you must setup the sets to tell the kernel which descriptors you are interested in polling by setting the bits in the fd_set using FD_SET() or if you have lots of sockets/bits to set, using a master fd_set and copying the whole thing to your read, write or error set. I usually did the latter for efficiency. These are integers typically from 0 to N (first 3 are usually not sockets so you normally poll 3 .. N). After select returns, you must check the bits. If a bit is set in readfds it is ready for reading.
select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
The supported statuses are "ready for read", "ready for write", and "error condition"
If you don't set a particular bit in the set, the kernel won't report its status to you.
As well, if you don't set the nfds param (max descriptor value) high enough, any descriptors above the max will be ignored. The descriptors do not have to be contiguous, just within the range of nfds.
All of this logic assumes successful return values on the system calls. If a system call returns an error status, you don't even regard the data structures for that call and must recover or process appropriately.
The primary problem that jumps out at me in your code is your select call's first argument. It isn't going to check comm_fd is comm_fd is lower than listen_fd.
I recommend you keep an int value of max_desc and each each time you accept a new socket, set max_desc = MAX(max_desc, new_fd+1), as well, you'll need to adjust it downward when closing out sockets. I always prefer to keep a separate fd_set just to track the descriptors my process has open (never pass it to select() just use it for bookkeeping).

linux socket client/server program

Hello my server program is :
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main()
{
int sfd, cfd;
int ch='k';
struct sockaddr_in saddr, caddr;
sfd= socket(AF_INET, SOCK_STREAM, 0);
saddr.sin_family=AF_INET; /* Set Address Family to Internet */
saddr.sin_addr.s_addr=htonl(INADDR_ANY); /* Any Internet address */
saddr.sin_port=htons(29008); /* Set server port to 29008 */
/* select any arbitrary Port >1024 */
bind(sfd, (struct sockaddr *)&saddr, sizeof(saddr));
listen(sfd, 1);
while(1) {
printf("Server waiting...");
cfd=accept(sfd, (struct sockaddr *)NULL, NULL);
if(read(cfd, &ch, 1)<0) perror("read");
ch++;
if(write(cfd, &ch, 1)<0) perror("write");
close(cfd);
}
}
so i got a simple server program and i also got a client program. The problem i have is that when i run both at the same machine on different terminals the client output works fine. The server although doesnt print the waiting line and also it stacks making me unable to use terminal. Whats the problem ?
As the comment points out, you need send a new line or fflush(stdout) to make the server print what you want, or disable buffering entirely before operating on stdout: setbuf(stdout, NULL), which is not necessary. By default, stdout is line buffered, stderr is none buffered. And note that Microsoft runtime libraries do not support line buffering, so if you implement this program with Winsock, it will print immediately.
Before bind(), you can set the SO_REUSEADDR option so that when your program exits uncleanly(in the code above, you didn't close the listening socket sfd explicitly, though the OS will clean up upon termination, but it's a good practice to do so), the port which may remain in TIME_WAIT state can be re-used immediately.
int yes = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int))
If you want to use the current terminal after you run the program, add a & after your command, so the program will run in the background.

Best way to send video over TCP Socket in C

I am trying to think of the best way to have a video file sent over a TCP socket. I have made a standard socket program, but after the read command I'm not sure how I can go about saving it.
Code sample
//bind server socketfd
if (bind(sfdServer, (struct sockaddr*)&adrServer, ServerAddrLen) < 0)
error("ERROR binding");
listen(sfdServer, 5);
while(1){
printf("Waiting for connections...\n");
sfdClient = accept(sfdServer, (struct sockaddr*)&adrClient, &ClientAddrLen);
if(sfdClient < 0)
error("ERROR accepting");
printf("Connection Established.\n");
//set buffer to zero
bzero(buff, 2048);
printf("Reading from client.\n");
numChar = read(sfdClient, buff, 2048);
//What should go here?
close(sfdClient);
close(sfdServer);
}
Would I just save the buffer as a file movie.mp4 or something like that? I realize I may need to change my buffer size or possibly send it in chunks. But I can't find any good info on the best way to do this. Any help or a point in the right direction would be appreciated!
You'd write the buffer chunks to a file.
First, open an output file for writing:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int outfd;
outfd = open("movie.mp4", O_WRONLY|);
Then, after the read(), write your bytes:
numWrite = write(outfd, buff, numChar);
Note that you will need to deal with a number of chunking/buffering cases, such as:
Either the read() or the write() returning zero or -1 (error)
Reading until no more bytes are available. Right now, your code only reads 2048 bytes and then closes the socket.
The write() writing fewer bytes than requested (this can happen on network filesystems)
Firstly, you should consider using sendfile() or the equivalent for your system. That is, rather than read a chunk of the file into your memory only to write it back out somewhere, you should memory-map your file descriptor and send everything in one go.
As for chunking, TCP takes care of splitting the stream into packets for you. Ideally the remote node should know exactly how much to expect upfront so it can handle pre-mature termination (that's why HTTP headers contain a content length).
So use some sort of handshake before sending the data. Then the sender should just call sendfile() in a loop, paying attention to the amount of sent data and the returned offet. The receiver should call recv() in a loop to make sure it all arrives (there is no standard "recvfile()").

Resources