Is this a legitimate C++ code? [closed] - c++

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 12 years ago.
The code comes from here
Given that in C++ you can use C libraries would you say that the following code is a legitimate C++ code?
If not what changes need to be applied?
This code compiles with g++ and runs as expected.
UPDATE:
Thank you for all answers. I'm still slightly confused as there's no agreement on whether or not this code comply with C++ standards. Will ask another question to dispel my doubts
UPDATE2:
To moderators who closed this question:
Just noticed that the question has been closed which I think is ridiculous. It's a down-to-earth technical question and I received down-to-earth technical answers.In case you haven't read fully the whole thread, here's the visual representation of the conclusion we've agreed on:
Clearly C++ is not a superset of C.
Closing questions that deal with coding standards is just wrong.
Client:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#define PORT "3490" // the port client will be connecting to
#define MAXDATASIZE 100 // max number of bytes we can get at once
// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa){
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(int argc, char *argv[]){
int sockfd, numbytes;
char buf[MAXDATASIZE];
struct addrinfo hints, *servinfo, *p;
int rv;
char s[INET6_ADDRSTRLEN];
if (argc != 2) {
fprintf(stderr,"usage: client hostname\n");
exit(1);
}
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if ((rv = getaddrinfo(argv[1], PORT, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// loop through all the results and connect to the first we can
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1) {
perror("client: socket");
continue;
}
if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
perror("client: connect");
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "client: failed to connect\n");
return 2;
}
inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr),
s, sizeof s);
printf("client: connecting to %s\n", s);
freeaddrinfo(servinfo); // all done with this structure
if ((numbytes = recv(sockfd, buf, MAXDATASIZE-1, 0)) == -1) {
perror("recv");
exit(1);
}
buf[numbytes] = '\0';
printf("client: received '%s'\n",buf);
close(sockfd);
return 0;
}
Server:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#define PORT "3490" // the port users will be connecting to
#define BACKLOG 10 // how many pending connections queue will hold
void sigchld_handler(int s){
while(waitpid(-1, NULL, WNOHANG) > 0);
}
// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa){
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(void){
int sockfd, new_fd; // listen on sock_fd, new connection on new_fd
struct addrinfo hints, *servinfo, *p;
struct sockaddr_storage their_addr; // connector's address information
socklen_t sin_size;
struct sigaction sa;
int yes=1;
char s[INET6_ADDRSTRLEN];
int rv;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; // use my IP
if ((rv = getaddrinfo(NULL, PORT, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// loop through all the results and bind to the first we can
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1) {
perror("server: socket");
continue;
}
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,
sizeof(int)) == -1) {
perror("setsockopt");
exit(1);
}
if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
perror("server: bind");
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "server: failed to bind\n");
return 2;
}
freeaddrinfo(servinfo); // all done with this structure
if (listen(sockfd, BACKLOG) == -1) {
perror("listen");
exit(1);
}
sa.sa_handler = sigchld_handler; // reap all dead processes
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
perror("sigaction");
exit(1);
}
printf("server: waiting for connections...\n");
while(1) { // main accept() loop
sin_size = sizeof their_addr;
new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
if (new_fd == -1) {
perror("accept");
continue;
}
inet_ntop(their_addr.ss_family,
get_in_addr((struct sockaddr *)&their_addr),
s, sizeof s);
printf("server: got connection from %s\n", s);
if (!fork()) { // this is the child process
close(sockfd); // child doesn't need the listener
if (send(new_fd, "Hello, world!", 13, 0) == -1)
perror("send");
close(new_fd);
exit(0);
}
close(new_fd); // parent doesn't need this
}
return 0;
}

From your comments it seems like you're really interested in how portable the code is, or "how can you be sure that it will compile on any C++ compiler not just g++".
Since each compiler has s different set of extensions they support (or even a different level of how well they support the standard, or which standard they support), one way is to try compiling it with different compilers.
If you can't do that, you can specify exactly which set of extenstions and/or standards that gcc will use to compile. For example:
--std=c89
--std=c90
--std=c99
--std=gnu90 (default for C compiles)
--std=gnu99
--std=c++98
--std=c++0x
--std=gnu++98 (default for C++ compiles)
--std=gnu++0x
There are probably other, see the gcc docs for more detail: http://gcc.gnu.org/onlinedocs/gcc/Standards.html
Also, the --pedantic option can make gcc much more strict about standards compliance.

Personally I see C++ are more declarative than C which is highly procedural (though not as declarative as things like prolog).
server
ServerAction action;
Server server(action);
EventLoop loop(server);
loop.processes();
Client
Address addr("123.45.67");
Connection connect(addr);
Message message(Connection::Type::HTTP, "Plop");
connection.Send(message);

This does comply with C++ standards, and in that sense it is legally C++.
However, there is nothing here that is C++ specific. You could have just as easily compiled this with gcc.
I think what others are noting here is the fact that this code is written in a procedural way, not in an object-oriented way. Most people associate C++ with object-oriented programming, and most modern books that discuss morally responsible C++ programming would say this is a bad way to write an app. In other words, it is very much not in vogue to write programs like this when an object-oriented paradigm is available to write this sort of application in.
This application is very difficult to maintain, and is written almost haphazardly. A good C programmer would split this problem into multiple functions instead of dumping most of it in main(). It works as an example of how to do client-server, but I would hesitate to write new code like this.

You’re not writing C++, you’re taking advantage of the fact that conforming, clean C code will compile as C++ with no problems. There are some places where C++ diverges from C, but the compiler will yell at you in those cases. You could use this as a base on top of which to write some effective C++; otherwise, there’s nothing special going on.

This is valid, but not in the C++ preferred style.
For example, all the standard C libraries should be included <cstdio>, not <stdio.h>.
Another example, this code uses a #define when it could have used a constant integer.

If I were to visit you at Scotland and speak Scottish with my Portuguese accent and (short) English vocabulary ... would you say I was speaking Scottish?
Assume everybody understood me and I understood everybody :-)

Using C++, you could use better type checking features to avoid things like void*, #define, etc.
But since it does not use exclusive C features, it compiles with g++.

It is 'C' code that also conforms to the compatible subset within C++.
If it compiles it's legitimate, absent compiler bugs, but it seems to me that you have a different meaning for 'legitimate'.

I don't quite understand the question. I think you're asking whether you should just keep using it without any changes, and not worry about it - in which case I'd say yes. C++ is a superset of C - any valid C code is valid C++ code, as you see.
By convention, the standard headers have a new format -
#include <string.h>
becomes
#include <cstring>
but this isn't strictly necessary. You can't do that with the unistd and sys/ headers, anyway.
So if you're asking whether you can safely keep using it - go for it, but you may want to change errno.h to cerrno, stdio.h to cstdio, and so on.

Related

TCP need to discard info on the buffer or make it faster

I am making a 3d application that works with the data of the accelerometer and gyroscope of the mobile. The problem is that the mobile sends data faster than the computer reads. The application increasingly makes the movements of the mobile with more delay as time passes. For example, at the beginning 2~3s is faithful to what the mobile does in reality, but over 10s it is making the movements that I did 6 seconds before.
I understand that it is reading data from the beginning buffer while the front of the most current data grows and never reaches it. I think the problem is how I read the data that comes to me.
Here is an example code that is implemented in the application. What could I do?
#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <string.h>
#include <fcntl.h>
#include <algorithm>
#define PORT 8080
int main(int argc, char const *argv[])
{
int server_fd, new_socket, valread;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
char buffer[1024] = {0};
const char *ACK = "ACKDAT\n";
std::string data;
socklen_t len;
char *error;
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0)
{
perror("socket failed");
exit(EXIT_FAILURE);
}
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT,&opt, sizeof(opt)))
{
perror("setsockopt");
exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons( PORT );
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address))<0)
{
perror("bind failed");
exit(EXIT_FAILURE);
}
if (listen(server_fd, 3) < 0)
{
perror("listen");
exit(EXIT_FAILURE);
}
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen))<0)
{
perror("accept");
exit(EXIT_FAILURE);
}
fcntl(new_socket, F_SETFL, O_NONBLOCK);
while(true){
valread = read( new_socket , buffer, 1024);
for(int i = 0;i < 1024 ; i++){
if(buffer[i]!=0){
data = data + buffer[i];
}
buffer[i]=0;
}
if(!data.empty()){
//remove /n from data
data.erase(std::remove(data.begin(), data.end(), '\n'), data.end());
std::cout<<"#"<<data<<"#"<<std::endl;
send(new_socket , ACK , strlen(ACK) , 0 );
}
data.clear();
}
return 0;
}
While Sam Varshavchik's suggestion of using a thread is good, there's another option.
You already set your socket to non-blocking with fcntl(new_socket, F_SETFL, O_NONBLOCK);. So, at each loop you should read everything there is to read and send everything there is to send. If you don't tie one-to-one the reading and writing, both sides will be able to catch up.
The main hint that you need to fix this is that you don't use the read return value, valread. Your code should look like:
while(true){ // main loop
...
valread = read( new_socket , buffer, 1024);
while(valread > 0)
{
// deal with the read values.
// deal with receiving more than one packet per iteration
}
// send code done a single time per loop.
There still plenty of architecture you need to have a clean resilient main loop that sends and receives, but I hope that points you in a useful direction.
You can have all reading from the socket done in a separate std::thread, that does nothing but read from the socket and save the data in your program's internal buffer. Your existing program reads from the internal buffer, rather than a socket, whenever it is capable of doing so.
If the internal buffer is full you'll have to figure out what to do about it, probably discard unwanted data. Perhaps stash a copy of the most recent received record, in a safe place, and put it into the buffer when things get unclogged. The dedicated execution thread will likely need to do some minimal processing, to parse the raw data from the socket into some meaningful, logical form. It's unlikely that you can simply throw away random parts of the raw data stream from the socket. It will need to be logically parsed, in some form or fashion that's particular to your application, so this can be done in a meaningful way.
You will need to implement your application's buffer in a thread-safe manner, so that it can be simultaneously accessed by multiple execution threads. If you're not familiar with multi-threaded programming in C++, you will find more information in your C++ textbook, on this topic.

getaddrinfo bind: Address already in use

Whenever I attempt to run my app on macOS Catalina 10.15.2 (19C57) or Linux 5.4.3-arch1-1 x86_64 GNU/Linux, I get:
_rSockd: bind: Address already in use
libc++abi.dylib: terminating with uncaught exception of type std::runtime_error: failed to bind to socket
[1] 31181 abort Saol/saol
Now I have checked for open ports with sudo netstat -tulp udp and sudo lsof -i udp, but none of these report any applications of mine using these ports. Nothing is currently listening on the port(45100).
I have browsed several similar posts that mentioned adding the SO_REUSEADDR, but this did not change anything when I have it or when I don't.
Posts also mentioned checking for something using the port, but I was unable (I believe unless the above commands were the wrong ones to use) to find my ports locked up. I have tried several different ports(currently using 45100); the _port var is set to an int before the _Init function is called. getaddrinfo's second argument is a const char *service, and stated in the man pages:
service sets the port in each returned address structure. If this
argument is a service name (see services(5)), it is translated to the
corresponding port number. This argument can also be specified as a
decimal number, which is simply converted to binary. If service is
NULL, then the port number of the returned socket addresses will be
left uninitialized. If AI_NUMERICSERV is specified in hints.ai_flags
and service is not NULL, then service must point to a string contain‐
ing a numeric port number. This flag is used to inhibit the invoca‐
tion of a name resolution service in cases where it is known not to
be required.
All I am doing to the _port is this: int -> string -> char *
Below is the relevant section of my code:
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#define PORT "45100"
auto main(void) -> int {
int rv;
int _rSockd;
const char * port = std::to_string(8080).c_str();
struct addrinfo hints;
struct addrinfo *servinfo;
struct addrinfo *p;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_PASSIVE;
if((rv = getaddrinfo(NULL, port, &hints, &servinfo)) != 0) {
std::cerr << "getaddrinfo: " << std::endl << gai_strerror(rv) << std::endl;
throw std::runtime_error("failed to getaddrinfo");
}
for(p = servinfo; p != NULL; p = p->ai_next) {
if((_rSockd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
std::perror("_rSockd: socket");
close(_rSockd);
continue;
}
if(bind(_rSockd, p->ai_addr, p->ai_addrlen) == -1) {
close(_rSockd);
std::perror("_rSockd: bind");
continue;
}
break;
}
if(p == NULL) {
throw std::runtime_error("failed to bind to socket");
}
freeaddrinfo(servinfo);
return 0;
}
Any help would be appreciated; I feel I have been staring at it too long and am missing something basic.
NOTE: When I added the MCVE (the above updated code), I no longer have my issue when running with port 8080 and 45100. So I will now explain the ??build?? process now to see if this is causing the issue.
UPDATE
When I made the MCVE, it worked. It never had the issues from my project base.
My project is a library being called from another executable project. libSaol has the netcode, saol is the executable that links libSaol.
To be honest, this confuses me more...

POP3 server - basic client operations in raw C++

I've spent at least 2 last hours searching for a way to make a simple connection to a POP3 server and get the number of messages waiting on it. As it's childlish-easy in C# and seems pretty basic in C++ on linux, I just can't find even the slightest tutorial on how to make it work on Windows.
I don't want to use any third-party libraries - i just want to code a simple console program, using raw C++ only, just to do some basic stuff as described above. All the sources I've tried to study are like:
POP3 is a protocol that has somethng to do with emails and it's very simple. Now let's proceed to writing a multi-platform POP3 server-client application, using a F16 fighter jet and inventing a time machine in progress.
I just can't seem to find any SIMPLE solutions...
I've written (with some help) a simple snippet that SHOULD work on linux - at least according to the tutorials; I have no means to check it right now.
Hovewer, the C++ is not my "native language" and when I try to transfer it into Windows, I just fall from one hole into the other and have to spend yet another quarter of an hour GGoogle'ing the solution.
At this point, the code is compiling, but the linker fails. It's strange, because I've added the ws2_32.lib to the linker, so it SHOULD work just fine. In return, I only get loads of LNK2019.
Can you please help me with the code or provide any link to a SIMPLE solution that works on Windows?
The code:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN 1
#include <winsock2.h>
#include <windows.h>
#else
#endif
#ifndef in_addr_t
#define in_addr_t long
#endif
#include <string.h>
void err(char *where) {
fprintf(stderr, "error in %s: %d\n", where, errno);
exit(1);
}
int main(int argc, char *argv[]) {
char *remote = "some_address";
struct servent *sent;
struct protoent *pent;
int port;
int sock;
int result;
in_addr_t ipadr;
struct sockaddr_in addr;
struct hostent *hent;
char buf[2048];
sent = getservbyname("http", "pop3");
if(sent == NULL)
err("getservbyname");
port = sent->s_port;
pent = getprotobyname("pop3");
if(pent == NULL)
err("getprotobyname");
hent = gethostbyname(remote);
printf("Host: %s\n", hent->h_name);
printf("IP: %s\n", inet_ntoa(*((struct in_addr *)hent->h_addr)));
addr.sin_family = AF_INET;
addr.sin_port = port;
addr.sin_addr = *((struct in_addr *)hent->h_addr);
memset(addr.sin_zero, '\0', 8);
sock = socket(AF_INET, SOCK_STREAM, pent->p_proto);
if(sock < 0)
err("socket");
result = connect(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr));
if(result < 0)
err("connect");
}
You have to add WSAStartup before you use any Winsock function. When you are done, you have to call WSACleanup.
Example(from msdn):
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0)
{
return 1;
}
//Do stuf here
WSACleanup();

Calling socket::connect, socket::bind, socket::listen without using getaddrinfo( ) function before it

In all the example including Beej's Guide, the IP address is provided in dot notation and then it's fed to ::getaddrinfo(). This post doesn't answer my question.
After which the addrinfo struct is used for socket related functions (e.g. connect(), bind(), listen()). For example:
struct addrinfo hints, *res;
// ... create socket etc.
connect(sockfd, res->ai_addr, res->ai_addrlen);
Example
The variable ai_addr is of type sockaddr which can be safely typecasted to sockaddr_storage, sockaddr_in and sockaddr_in6.
Question:
If I typecast sockaddr to sockaddr_in (or sockaddr_in6)
sockaddr_in& ipv4 = (sockaddr_in&)(sockaddr_variable);
and feed below info:
ipv4.sin_family = AF_INET
ipv4.sin_addr = [IP Address in net byte order]
ipv4.sin_port = [Port number in net byte order]
Can I call the connect() method directly using above info?
connect(sockfd, &ipv4, sizeof(ipv4));
With my program it doesn't appear to work. Am I missing something, or is there a better way?
The motivation behind is that, if we have the information of IPAddress, Port etc. in socket readable format then why to go through the cycle of getaddrinfo()
Be sure you're placing your values in network order, here's a small example:
#include<stdio.h>
#include<sys/socket.h>
#include<arpa/inet.h>
int main(int argc, char *argv[])
{
int sock;
struct sockaddr_in server;
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1)
{
printf("Could not create socket\n");
}
printf("Socket created\n");
server.sin_family = AF_INET;
// 173.194.32.207 is a google address
server.sin_addr.s_addr = 173 | 194 << 8 | 32 << 16 | 207 << 24;
server.sin_port = 0x5000; // port 80
if (connect(sock, (struct sockaddr *)&server, sizeof(server)) < 0)
{
perror("connect failed. Error");
return 1;
}
printf("Connected\n");
close(sock);
return 0;
}
First check whether the machine is reachable & the server application is running on the machine using "netstat" utility. Use inet_aton method to convert dotted address to network byte order. Finally, log the error value returned by the connect to get the exact reason of failure.
It's worth noting that calling socket::{connect, bind, ...} is wrong: these are C APIs and C doesn't have namespaces, classes and so on.
You should use getaddrinfo as it's much easier and safer to use. But nothing prevents you from using struct sockaddr and all its variants. Indeed, getaddrinfo is a sort of wrapper as stated in man(3) getaddrinfo:
The getaddrinfo() function combines the functionality
provided by the gethostbyname(3) and getservbyname(3) functions into a
single interface, but unlike the latter functions, getaddrinfo() is
reentrant and allows programs to eliminate IPv4-versus-IPv6 dependen‐
cies.
An example:
#include <sys/socket.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main()
{
struct sockaddr_in addr = {0};
addr.sin_family = AF_INET;
addr.sin_port = htons(80);
inet_pton(addr.sin_family, "198.252.206.16", &addr.sin_addr);
int fd = socket(addr.sin_family, SOCK_STREAM, 0);
if (fd == -1)
; /* could not create socket */
if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1)
; /* could not connect */
close(fd);
}

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?
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 <= 0) {
// handle errors
}
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).
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").
Do aman 2 sendfile. You only need to open the source file on the client and destination file on the server, then call sendfile and the kernel will chop and move the data.
Minimal runnable POSIX read + write example
Usage:
get two computers on a LAN.
For example, this will work if both computers are connected to your home router in most cases, which is how I tested it.
On the server computer:
Find the server local IP with ifconfig, e.g. 192.168.0.10
Run:
./server output.tmp 12345
On the client computer:
printf 'ab\ncd\n' > input.tmp
./client input.tmp 192.168.0.10 12345
Outcome: a file output.tmp is created on the sever computer containing 'ab\ncd\n'!
server.c
/*
Receive a file over a socket.
Saves it to output.tmp by default.
Interface:
./executable [<output_file> [<port>]]
Defaults:
- output_file: output.tmp
- port: 12345
*/
#define _XOPEN_SOURCE 700
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <netdb.h> /* getprotobyname */
#include <netinet/in.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <unistd.h>
int main(int argc, char **argv) {
char *file_path = "output.tmp";
char buffer[BUFSIZ];
char protoname[] = "tcp";
int client_sockfd;
int enable = 1;
int filefd;
int i;
int server_sockfd;
socklen_t client_len;
ssize_t read_return;
struct protoent *protoent;
struct sockaddr_in client_address, server_address;
unsigned short server_port = 12345u;
if (argc > 1) {
file_path = argv[1];
if (argc > 2) {
server_port = strtol(argv[2], NULL, 10);
}
}
/* Create a socket and listen to it.. */
protoent = getprotobyname(protoname);
if (protoent == NULL) {
perror("getprotobyname");
exit(EXIT_FAILURE);
}
server_sockfd = socket(
AF_INET,
SOCK_STREAM,
protoent->p_proto
);
if (server_sockfd == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
if (setsockopt(server_sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) < 0) {
perror("setsockopt(SO_REUSEADDR) failed");
exit(EXIT_FAILURE);
}
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = htonl(INADDR_ANY);
server_address.sin_port = htons(server_port);
if (bind(
server_sockfd,
(struct sockaddr*)&server_address,
sizeof(server_address)
) == -1
) {
perror("bind");
exit(EXIT_FAILURE);
}
if (listen(server_sockfd, 5) == -1) {
perror("listen");
exit(EXIT_FAILURE);
}
fprintf(stderr, "listening on port %d\n", server_port);
while (1) {
client_len = sizeof(client_address);
puts("waiting for client");
client_sockfd = accept(
server_sockfd,
(struct sockaddr*)&client_address,
&client_len
);
filefd = open(file_path,
O_WRONLY | O_CREAT | O_TRUNC,
S_IRUSR | S_IWUSR);
if (filefd == -1) {
perror("open");
exit(EXIT_FAILURE);
}
do {
read_return = read(client_sockfd, buffer, BUFSIZ);
if (read_return == -1) {
perror("read");
exit(EXIT_FAILURE);
}
if (write(filefd, buffer, read_return) == -1) {
perror("write");
exit(EXIT_FAILURE);
}
} while (read_return > 0);
close(filefd);
close(client_sockfd);
}
return EXIT_SUCCESS;
}
client.c
/*
Send a file over a socket.
Interface:
./executable [<input_path> [<sever_hostname> [<port>]]]
Defaults:
- input_path: input.tmp
- server_hostname: 127.0.0.1
- port: 12345
*/
#define _XOPEN_SOURCE 700
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <netdb.h> /* getprotobyname */
#include <netinet/in.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <unistd.h>
int main(int argc, char **argv) {
char protoname[] = "tcp";
struct protoent *protoent;
char *file_path = "input.tmp";
char *server_hostname = "127.0.0.1";
char *server_reply = NULL;
char *user_input = NULL;
char buffer[BUFSIZ];
in_addr_t in_addr;
in_addr_t server_addr;
int filefd;
int sockfd;
ssize_t i;
ssize_t read_return;
struct hostent *hostent;
struct sockaddr_in sockaddr_in;
unsigned short server_port = 12345;
if (argc > 1) {
file_path = argv[1];
if (argc > 2) {
server_hostname = argv[2];
if (argc > 3) {
server_port = strtol(argv[3], NULL, 10);
}
}
}
filefd = open(file_path, O_RDONLY);
if (filefd == -1) {
perror("open");
exit(EXIT_FAILURE);
}
/* Get socket. */
protoent = getprotobyname(protoname);
if (protoent == NULL) {
perror("getprotobyname");
exit(EXIT_FAILURE);
}
sockfd = socket(AF_INET, SOCK_STREAM, protoent->p_proto);
if (sockfd == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
/* Prepare sockaddr_in. */
hostent = gethostbyname(server_hostname);
if (hostent == NULL) {
fprintf(stderr, "error: gethostbyname(\"%s\")\n", server_hostname);
exit(EXIT_FAILURE);
}
in_addr = inet_addr(inet_ntoa(*(struct in_addr*)*(hostent->h_addr_list)));
if (in_addr == (in_addr_t)-1) {
fprintf(stderr, "error: inet_addr(\"%s\")\n", *(hostent->h_addr_list));
exit(EXIT_FAILURE);
}
sockaddr_in.sin_addr.s_addr = in_addr;
sockaddr_in.sin_family = AF_INET;
sockaddr_in.sin_port = htons(server_port);
/* Do the actual connection. */
if (connect(sockfd, (struct sockaddr*)&sockaddr_in, sizeof(sockaddr_in)) == -1) {
perror("connect");
return EXIT_FAILURE;
}
while (1) {
read_return = read(filefd, buffer, BUFSIZ);
if (read_return == 0)
break;
if (read_return == -1) {
perror("read");
exit(EXIT_FAILURE);
}
/* TODO use write loop: https://stackoverflow.com/questions/24259640/writing-a-full-buffer-using-write-system-call */
if (write(sockfd, buffer, read_return) == -1) {
perror("write");
exit(EXIT_FAILURE);
}
}
free(user_input);
free(server_reply);
close(filefd);
exit(EXIT_SUCCESS);
}
GitHub upstream.
Further comments
Possible improvements:
Currently output.tmp gets overwritten each time a send is done.
This begs for the creation of a simple protocol that allows to pass a filename so that multiple files can be uploaded, e.g.: filename up to the first newline character, max filename 256 chars, and the rest until socket closure are the contents. Of course, that would require sanitation to avoid a path transversal vulnerability.
Alternatively, we could make a server that hashes the files to find filenames, and keeps a map from original paths to hashes on disk (on a database).
Only one client can connect at a time.
This is specially harmful if there are slow clients whose connections last for a long time: the slow connection halts everyone down.
One way to work around that is to fork a process / thread for each accept, start listening again immediately, and use file lock synchronization on the files.
Add timeouts, and close clients if they take too long. Or else it would be easy to do a DoS.
poll or select are some options: How to implement a timeout in read function call?
A simple HTTP wget implementation is shown at: How to make an HTTP get request in C without libcurl?
Tested on Ubuntu 15.10.
This file will serve you as a good sendfile example : http://tldp.org/LDP/LGNET/91/misc/tranter/server.c.txt