send and receive binary files properly using sockets c++ - c++

hello stackflow users,
so i want to send and receive my binary file using sockets in c++ and here is how i send it from server program
send(Connections[conindex], reinterpret_cast<char*>(rawData), sizeof(rawData), NULL);
and here is how my client program receives it
char raw[647680];
recv(Connection, raw, sizeof(raw), NULL);
is there any proper way than this? i want so that i don't have to hard code the size every time.
or any other alternatives etc

A rather general way to achieve this (in both C and C++) is something like this:
if (FILE *fp = fopen(filename, "rb"))
{
size_t readBytes;
char buffer[4096];
while ((readBytes = fread(buffer, 1, sizeof(buffer), fp) > 0)
{
if (send(Connections[conindex], buffer, readBytes, 0) != readBytes)
{
handleErrors();
break;
}
}
close(Connections[conindex]);
}
And on the client side:
if (FILE *fp = fopen(filename, "wb"))
{
size_t readBytes;
char buffer[4096];
while ((readBytes = recv(socket, buffer, sizeof(buffer), 0) > 0)
{
if (fwrite(buffer, 1, readBytes, fp) != readBytes)
{
handleErrors();
break;
}
}
}
Alternatives to this rather FTP-esque connection style includes sending the size of the file first, so the client will know when to stop listening instead of waiting for the disconnect.
Please note that this code is untested and merely meant to illustrate the concept.

Related

Time between ssh_channel_write and read is abnormaly long?

I'm setting up and client/server application in c++ with the libraby openssh (they are on the same host)
When I try to send data from the server to the client I use the function ssh_channel_write (and ssh_channel_read to get the data)
My code looks like this :
std::string msg = "hello";
gettimeofday(&start, NULL);
ssh_channel_write(chan, msg.c_str(), msg.size());
And for the client :
char buffer[256];
int nbytes;
nbytes = ssh_channel_read(chan, buffer, sizeof(buffer), 0);
gettimeofday(&end, NULL);
It takes about 40ms before the data is read. It's too long for my application (I'm sending a lot of messages).
Does someone as ever encountered this problem?
How could I fix it?

Fastest way to send multiple char array over TCP

I'm working on a webserver framework in C++ mostly for my own understanding, but I want to optimize it as well.
My question is is it faster to write multiple char arrays to the TCP connection for every html response or to spend the time to concatenate up front and only write to the TCP connection once. I was thinking about benchmarking it, but I am not quite sure how to go about it.
This is my first post on stackoverflow, although I have benefitted from the website very often!
Thanks!
Here is what I am talking about for sending many char arrays individually. The alternate would be concatenate all of these char arrays into one char array then sending that.
int main() {
sockaddr_in address;
int server_handle;
int addrlen = sizeof(address);
if ((server_handle = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("cannot create socket");
exit(0);
}
memset((char *) &address, 0, sizeof(address));
address.sin_family = AF_INET;
address.sin_addr.s_addr = htonl(INADDR_ANY);
address.sin_port = htons(PORT);
if (bind(server_handle, (sockaddr *) &address, (socklen_t) addrlen) < 0)
{
perror("bind failed");
exit(0);
}
if (listen(server_handle, 3) < 0)
{
perror("In listen");
exit(EXIT_FAILURE);
}
while(1) {
std::cout << "\n+++++++ Waiting for new connection ++++++++\n\n";
int client_handle;
if ((client_handle = accept(server_handle, (struct sockaddr *)&address, (socklen_t *) &addrlen))<0)
{
perror("In accept");
exit(EXIT_FAILURE);
}
// read and respond to client request
char buffer[30000] = {0};
int bytesRead = read(client_handle, buffer, 30000);
char * httptype = "HTTP/1.1 ";
char * status = "200 \n";
char * contenttype = "Content-Type: text/html \n";
char * contentlength = "Content-Length: 21\n\n";
char * body = "<h1>hello world!</h1>";
write(client_handle, httptype, 9);
write(client_handle, status, 5);
write(client_handle, contenttype, 26);
write(client_handle, contentlength, 20);
write(client_handle, body, 21);
std::cout << "------------------Response sent-------------------\n";
close(client_handle);
}
}
If you want to send multiple buffers with a single write call you can use vectored IO (aka scatter/gather IO) as the manual suggests:
char *str0 = "hello ";
char *str1 = "world\n";
struct iovec iov[2];
ssize_t nwritten;
iov[0].iov_base = str0;
iov[0].iov_len = strlen(str0);
iov[1].iov_base = str1;
iov[1].iov_len = strlen(str1);
nwritten = writev(STDOUT_FILENO, iov, 2);
In fact it writing to a socket is not really different from writing to a file descriptor. And the fwrite function was introduced to the C library for a reason: write (be it to a TCP connection or to a file descriptor) involve a system call on common OS and a context change user/kernel. That context change has some overhead, mainly if you write small chunks of data.
On the other hand, if you write larger chunks of data in sizes that are close to the physical size for the underlying system call (disk buffer for a file descriptor, or max packet size for a network socket), the fwrite call or in your example the code concatenating char arrays will not really lower the system overhead and will just add some user code processing.
TL/DR: this depends on the average size of what you write. The smaller it is, the higher benefit of concatenating the date in larger chunks before writing. And remember: this is a low level optimization that should only be considered if you have identified a performance bottleneck or if the code could be used in a broadly distributed library.

Execute a command on the server and send the result to the client in Windows

I'm writing a simple server program that executes a command and sends the result to the client. I read countless examples that involve using popen(), pipe(), dup2(), fork(), etc., but none of them worked for me and they didn't explain the code very well. I also tried to do it myself, but without success. Could you please provide me with a well documented example?
Here's the code that receives commands/messages from the client:
void server_receive() {
struct sockaddr_in from;
int from_len, recv_len;
char buf[BUFLEN], path[256]; // BUFLEN = 1024
// Getting the path for the command to execute
strcpy(path, getenv("SYSTEMDRIVE"));
strcat(path, "\\WINDOWS\\System32\\tasklist.exe");
from_len = sizeof(from);
memset(buf, '\0', BUFLEN);
// Receiving the command
// I'll add some if-else statements to handle various commands, but for
// now I just need to see if I can even get one to work.
if((recv_len = recvfrom(sockt, buf, BUFLEN, 0, (struct sockaddr*) &from, &from_len)) == SOCKET_ERROR) {
printf("[ERROR] recvfrom() failed: %d.\n\n", WSAGetLastError());
} else {
printf("Packet received from %s:%d\n", inet_ntoa(from.sin_addr), ntohs(from.sin_port));
printf("Data: %s\n\n", buf);
// Code to execute tasklist (I used _popen())
// and send everything back to the client (I used TransmitFile())
}
}
And here's the code that sends commands/messages to the server:
void client_send(char server[], unsigned short port) {
struct sockaddr_in to;
int s, to_len = sizeof(to);
char buf[BUFLEN]; // BUFLEN = 1024
char message[BUFLEN];
memset((char*) &to, 0, sizeof(to));
to.sin_family = AF_INET;
to.sin_port = htons(port);
to.sin_addr.S_un.S_addr = inet_addr(server);
while(true) {
printf("Enter message: ");
gets(message);
if (sendto(sockt, message, strlen(message), 0, (struct sockaddr*) &to, to_len) == SOCKET_ERROR) {
printf("[ERROR] sendto() failed: %d.\n\n" , WSAGetLastError());
}
memset(buf, '\0', BUFLEN);
if (recvfrom(sockt, buf, BUFLEN, 0, (struct sockaddr*) &to, &to_len) == SOCKET_ERROR) {
printf("[ERROR] recvfrom() failed: %d.\n\n", WSAGetLastError());
} else {
printf("Server's response: %s\n\n", buf); /* The result of tasklist
should be outputted by this line of code, however I'm concerned about the
relatively small receive length (BUFLEN = 1024).*/
}
}
}
Needless to say that these two functions are just a part of my code.
That you mention _popen (with the leading underscore) and TransmitFile indicates that you are on Windows, which doesn't have fork or pipe or related functions.
There are many alternatives to executing commands in Windows. One is through _popen as you already mentioned (but you don't say what's wrong with that method). Others include the "classic" system CRT function. And of course the Windows native CreateProcess function. If you want to open a "document" there's the ShellExecute function.
Knowing which functions are available will help you in your search for examples. Adding the term windows to your searches will help finding Windows-specific examples and tutorials. And adding the term msdn will help finding topics on the Microsoft Developer Network.

C++ TCP Socket Exponential Increasing Delay

I seem to have an issue with increasing latency on my packet transmission with my TCP server. Now, this server has to be TCP, since UDP is blocked by firewalls (this is a client-server-client type of communication). I'm also aware that the sending of a struct with floating point integers as I am is extremely non-portable, however, this system will operate Windows client to Windows server to Windows client for the foreseeable future.
The issue is this: the client begins receiving the data properly from the other client, however, there is a delay which gets exponentially worse (where, by about 3 minutes in, the packets are nearly 30 seconds behind - but correct, when they DO arrive). I researched it and found an answer on a Microsoft page explaining it is due to full send buffers, however, their syntax for the setsockopt doesn't match the documented examples, so perhaps I'm wrong.
Anyway, any advice would be appreciated:
The relevant part of the server:
(When accept() is called:)
int buff_size = 2048000;
int nodel = 1;
setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char*)&buff_size, sizeof(int));
setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*)&buff_size, sizeof(int));
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char*)&nodel, sizeof(nodel));
The message redirect loop:
if (gp->curr_pilot < sz && gp->users[gp->curr_pilot].pilot == TRUE) {
char* pbuf = new char[1024];
int recvd = recv(gp->users[gp->curr_pilot].sockfd_data, pbuf, 1024, NULL);
if (recvd > 0) {
for (int i = 0; i < sz; i++) {
if (i != gp->curr_pilot && gp->users[i].unioned == TRUE)
send(gp->users[i].sockfd_data, pbuf, recvd, NULL);
}
}
delete[] pbuf;
}
The client (master is set when it's sending, and it does get set properly by my code):
(data is my struct of doubles that gets written by the client, cdata is a copy of it that gets written into the client).
while (kill_dataproc == FALSE) {
if (master == TRUE) {
char* buff = new char[1024];
int packet_signer = 1192;
memcpy_s(buff, intsz, &packet_signer, intsz);
memcpy_s((void*)(buff + intsz), sz, data, sz);
send(server_sock, buff, buffsize, NULL);
delete[] buff;
}
else {
char* buffer = new char[1024];
int recvd = recv(server_sock, buffer, 1024, MSG_PEEK);
if (recvd > 0) {
int newpacketsigner = 0;
memcpy_s(&newpacketsigner, intsz, buffer, intsz);
if (newpacketsigner == 1192) {
if (recvd >= buffsize) {
char* nbuf = new char[buffsize];
int recvd2 = recv(server_sock, nbuf, buffsize, NULL);
int err = WSAGetLastError();
memcpy_s(&newpacketsigner, intsz, nbuf, intsz);
memcpy_s(cdata, sz, (void*)(nbuf + intsz), sz);
//do things w/ the struct
delete[] nbuf;
}
}
else
recv(server_sock, buffer, 1024, NULL);
}
delete[] buffer;
}
Sleep(10);
}
As well, identical calls to setsockopt and are called for the client's sockets, and all of the sockets, server and client, are nonblocking.
You're assuming that your reads are filling the buffer. They are only obliged to transfer at least one byte. You you need to loop.
So, you have unread data backing up and stalling the sender.
NB Those receive buffers are greater than 64k and so may be inoperative unless they are set before the socket is connected. In the case of the server you need to set the receive buffer size on the listening socket. Accepted sockets will inherit it. If you don't to it his way, window scaling won't be in effect so a window > 64k cannot be advertised (unless the platform has window scaling on by default).

recv() socket loop never end C

A am writing a client-server program in C. It sends a directory name and receives a list of files as answer. The problem I have is that it gets stuck in an infinite loop.
If I send only one directory name it works, but if I send a list of directories it never ends and outputs nothing.
Server
while(recv(sock, name, BUFSIZE, 0) > 0){
if ((fddir=opendir(name)) == NULL){
send(sock, strerror(errno), strlen(strerror(errno)), 0);
close(sock);
return 1;
}
send(sock, name, strlen(name), 0);
send(sock, ":", strlen(":"), 0);
send(sock, "\n", strlen("\n"), 0);
while ((dirbuf = readdir(fddir)) != NULL){
buf[0] = '\0';
strcat(buf, dirbuf->d_name);
strcat(buf, "\t");
send(sock, buf, BUFSIZE, 0);
}
}
Client
for (int i=1;i<3;i++){
send(sock, argv[i], strlen(path), 0);
while(recv(sock, buf, BUFSIZE, 0) > 0)
printf("%s", buf);
}
The server waits until all directory names are received, and then the client wait until server send all files in it. How do I trace where the program gets stuck?
TCP is not message based, so you have no way of knowing where the boundaries between two client send() calls is when you call recv() on the server. Thus when you send multiple names back-to-back it is possible for the server to receive them all in a single recv() (or however many bytes you allocated for BUFSIZE). This is probably mangling your directory names, causing opendir to fail. This would be more obvious to you if you were checking for errors from send and recv and Captain Obvlious describes in another answer.
You need to check the calls to recv for errors. It returns 0 if the connection was disconnected and -1 on an error. You are only checking for values > 0 which will not work. The example below shows how to approach checking the errors.
while(true)
{
const int result = recv(sock, buf, BUFSIZE, 0);l
if(result == -1)
{
std::cout << "Error: " << errno << std::endl;
break;
}
else if(result == 0)
{
std::cout << "Disconnected" << std::endl;
break;
}
// process the data here. No errors
}
You should also be checking the value returned by send as it works in the same way.