Serving images in C++ server - c++

I have a C++ linux server, with basic server sockets. Here's what i'm using to server png images:
string ms = "HTTP/1.0 200 OK\r\nContent-type: image/png\r\n\r\n";
ifstream myfile("xampl.png", ios::in|ios::binary|ios::ate);
string line;
char* memblock;
streampos size;
if(myfile.is_open()){
size = myfile.tellg();
memblock = new char [size];
myfile.seekg (0, ios::beg);
myfile.read (memblock, size);
myfile.close();
}
ms.append(string(memblock));
cout << "\"" << ms << "\"" << endl;
char* msg = new char[ms.size()+1];
copy(ms.begin(), ms.end(), msg);
msg[ms.size()] = '\0';
int len;
ssize_t bytes_sent;
len = strlen(msg);
bytes_sent = send(new_sd, msg, len, 0);
I know i'm trying to read the png file as a binary file, but i have no idea what else to do. When i telnet to this server, i get a response with weird characters which make me believe that i have served the file, but when i check it out in my browser, i get the image not found icon, in all browsers. Please help...

OK, let's start with the equivalent PHP code to get rid of the mess it is to do the same thing in C++
// send required headers as plain text
header("Content-type: image/png");
// read the image as a binary block
$img_data = file_get_contents("xample.png");
// send it
echo $img_data;
C++ equivalent:
string headers = "HTTP/1.0 200 OK\r\nContent-type: image/png\r\n\r\n";
send (new_sd, headers.data(), headers.length(), 0);
ifstream f("xampl.png", ios::in|ios::binary|ios::ate);
if(!f.is_open()) error ("bloody file is nowhere to be found. Call the cops");
streampos size = f.tellg();
char* image = new char [size];
f.seekg (0, ios::beg);
f.read (image, size);
f.close();
send (new_sd, image, size, 0);

Converting memblock to a string is not going to work if it has embedded null characters, which it almost certainly does, and you don't pass the length. I'm not sure why you're doing all that faffing about with char*s and std::strings, but once you have your memblock and size, use them for send. If you want to prefix the response string, just send it first.

Related

rabbitmq-c send nlohmann json

Want to send image through RabbitMQ-C but the image file too big. Receiver cannot retrieve the image. So, I converted image to base64 then put it in JSON.
const char *msg;
FILE *image1;
if (image1 = fopen(path, "rb")) {
fseek(image1, 0, SEEK_END); //used to move file pointer to a specific position
// if fseek(0 success, return 0; not successful, return non-zero value
//SEEK_END: end of file
length = ftell(image1); //ftell(): used to get total size of file after moving the file pointer at the end of the file
sprintf(tmp, "size of file: %d bytes", length);
//convert image to base64
std::string line;
std::ifstream myfile;
myfile.open(path, std::ifstream::binary);
std::vector<char> data((std::istreambuf_iterator<char>(myfile)), std::istreambuf_iterator<char>() );
std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len);
std::string code = base64_encode((unsigned char*)&data[0], (unsigned int)data.size());
//convert std::string to const char
const char* base64_Image = code.c_str();
json j ;
j.push_back("Title");
j.push_back("content");
j.push_back(base64_Image);
std::string sa = j.dump();
msg = sa.c_str(); //convert std::string to const char*
}
else {
return;
}
Using RabbitMQ-C to send the message(msg) to receiver but failed
[error point to here]
is const char* cannot use amqp_cstring_bytes(msg) to convert to amqp_bytes_t??
respo = amqp_basic_publish(conn, 1, amqp_cstring_bytes(exchange), amqp_cstring_bytes(routing_key),0, 0, NULL, amqp_cstring_bytes(msg));
and get this error
If there is a handler for this exception, the program may be safely continued.```
Anyone know how to send image as JSON using RabbitMQ-C & C++ ?
amqp_cstring_bytes expects a C string, which is normally terminated by a NUL byte. Your PNG file is almost guaranteed to contain a NUL byte, so that explains why your message got cut off midway through.
As for the code in your paste: the pointer returned by sa.c_str() is only valid while sa is alive and unmodified. Once you exit the block containing sa's definition, the variable is dead and buried.
Instead, get a buffer of the appropriate size with amqp_bytes_alloc and return that:
amqp_bytes_t bytes = amqp_bytes_malloc(sa.length());
strncpy((char *)bytes.bytes, sa.c_str(), sa.length());
then pass the bytes object to amqp_basic_publish. Don't forget to ampqp_bytes_free it when you're done.

When file is sent via SMTP it lose some bytes. c++

i'm trying to send email via SMTP, i send multipart mail, with text part and application/octet-stream part. when i try to send not *.txt" files, for example .jpg or .docx it is got corrupted and some bytes are lost. For example, when i try to send file 123.docx, size of this file is 166 020 bytes. i recieve file in my email, but it has only 166 006 and i can't open it. Variable "total" shows correct number of bytes is sent.
My code is bellow:
#include <windows.h>
#include <winsock.h>
#include <stdio.h>
#include <iostream>
#include <fstream>
using namespace std;
int fileSize(char fileName[])
{
std::streampos fsize = 0;
std::ifstream myfile (fileName, ios::in); // File is of type const char*
fsize = myfile.tellg(); // The file pointer is currently at the beginning
myfile.seekg(0, ios::end); // Place the file pointer at the end of file
fsize = myfile.tellg() - fsize;
myfile.close();
cout << "size is: " << fsize << " bytes.\n";
return fsize;
}
int main() {
char text[2048];
//connecting to smtp server
//sending DATA
strcpy(text,"DATA\n");
send(s,text,strlen(text),0);
recv(s,text,sizeof(text),0);
cout<<"recv - "<<text<<endl;
//FROM field
strcpy(text,"FROM: laboratory4_mm#rambler.ru\n");
send(s,text,strlen(text),0);
//TO field
strcpy(text,"TO: ");
strcat(text,reciever);
strcat(text,"\n");
send(s,text,strlen(text),0);
// SUBJECT field
char subject[2048];
cout<<"Enter the theme of the letter"<<endl;
cin.getline(subject,2048);
strcpy(text,"SUBJECT: ");
strcat(text,subject);
strcat(text,"\n");
send(s,text,strlen(text),0);
// delimeter of multipart message
strcpy(text,"Content-Type: multipart/mixed; boundary=\"---nsabnqeaSA43ds2\"\n");
send(s,text,strlen(text),0);
//Text part
strcpy(text,"-----nsabnqeaSA43ds2\nContent-Type: text/plain; charset=utf8\nContent-Transfer-Encoding: 8bit\n\n");
send(s,text,strlen(text),0);
cout<<"Enter the text:"<<endl;
cin.getline(text,2048);
send(s,text,strlen(text),0);
//File part
char fileName[256];
cout<<"Enter file name: ";
cin.getline(fileName,255);
int size = fileSize(fileName);
char fileLength[1024];
itoa(size,fileLength,10);
cout<<fileLength<<endl;
strcpy(text,"\n-----nsabnqeaSA43ds2 \nContent-Type: application/octet-stream\nContent-Length: ");
strcat(text,fileLength);
strcat(text,"\nContent-Transfer-Encoding: binary\nContent-Disposition: attachment;\n\n");
send(s,text,strlen(text),0);
ifstream fin;
fin.open(fileName,ios::binary);
char *buf = new char[1024];
int readBytes;
int total =0;
while((readBytes = fin.read(buf,1024).gcount())>0) {
int sent= send(s,buf,readBytes,0);
total+=sent;
delete buf;
buf = new char[1024];
}
fin.close();
delete buf;
cout<< total<<endl;
strcpy(text,"\n-----nsabnqeaSA43ds2--\n");
send(s,text,strlen(text),0);
// telling that DATA is over
strcpy(text,"\n.\n");
send(s,text,strlen(text),0);
fout<<text;
recv(s,text,sizeof(text),0);
cout<<"recv - "<<text<<endl;
//Disconnecting from server
return 0;
}
There are several fundamental bugs in the MIME encoding, here:
First, a blank line is supposed to separate headers from content. The blank line is missing:
strcpy(text,"Content-Type: multipart/mixed; boundary=\"---nsabnqeaSA43ds2\"\n");
One newline generated here.
strcpy(text,"-----nsabnqeaSA43ds2\nContent-Type: text/plain; charset=utf8\nContent-Transfer-Encoding: 8bit\n\n");
Mail content begins here, without a preceding blank line.
Additionally, the newline that precedes a boundary delimiter is a logical part of the boundary delimiter, so that newline is missing as well. See MIME documentation for more information.
strcpy(text,"\n-----nsabnqeaSA43ds2 \nContent-Type: application/octet-stream\nContent-Length: ");
Note that here you are explicitly sending a newline before the boundary delimiter, so you must be aware of this requirement.
Secondly:
while((readBytes = fin.read(buf,1024).gcount())>0) {
int sent= send(s,buf,readBytes,0);
Sending the contents of the binary file, as is? That's not going to fly. Even though it may or may not be correct MIME encoding, SMTP is still a plain text-based transmission protocol. There is an extension for transmission of binary data, but the shown code does not use it.
As is, this is the SMTP version of undefined behavior. No guaranteed results. If you need to attach this file reliably, you must base64-encode it.

C++ Send a file containing \0 via sockets without accidental closure

I serialize the file via the code beneath, and send it over winsocks, this works fine with textfiles, but when I tried to send a jpg, the string contains \0 as some of the character elements, so the sockets only send part of the string, thinking \0 is the end, i was considering replacing \0 with something else, but say i replace it with 'xx', then replace it back on the other end, what if the file had natural occurrences of 'xx' that get lost? Sure I could make a large, unlikely sequence, but that bloats the file.
Any help appreciated.
char* read_file(string path, int& len)
{
std::ifstream infile(path);
infile.seekg(0, infile.end);
size_t length = infile.tellg();
infile.seekg(0, infile.beg);
len = length;
char* buffer = new char[len]();
infile.read(buffer, length);
return buffer;
}
string load_to_buffer(string file)
{
char* img;
int ln;
img = read_file(file, ln);
string s = "";
for (int i = 1; i <= ln; i++){
char c = *(img + i);
s += c;
}
return s;
}
Probably somewhere in your code (that isn't seen in the code you have posted) you use strlen() or std::string::length() to send the data, and/or you use std::string::c_str() to get the buffer. This results in truncated data because these functions stop at \0.
std::string is not good to handle binary data. Use std::vector<char> instead, and remove the new[] stuff.

How do I Write a .wav file on C++ using the socket layer in Linux?

I am trying to send wav files in C++ via TCP on Linux over a socket but I don't understand how a wav file can be read correctly.
My goal is to read the file on the client into a char array, send it with "write()" to the server, and the server should write the data into a local wav file again.
I read the .wav file like this:
////////////define socket - left out for simplicity
ifstream file ("audio.wav", ios::in|ios::binary|ios::ate); //open .wav file
char * buffer; //declare data buffer, should contain .wav data to write to socket
streampos filesize; //size of file
int n; //number of written bytes
//if file opened correctly, read content and write to socket
if (file.is_open()){
filesize = file.tellg();
buffer = new char [filesize];
file.seekg (0, ios::beg);
file.read (buffer, filesize);
file.close();
n = write(socket, buffer, sizeof(buffer));
}
On the server, this returns the array "RIFF" of length "4", so its part of the header of the wav file.
How can I read the whole .wav file content correctly for writing to the TCP socket?
Thanks.
That is simple: filesize is size of the file in bytes. However, sizeof(buffer) is only 4 on a 32-bit OS. Modify your code like this:
if(file.is_open()) {
filesize = file.tellg();
buffer = new char [filesize];
file.seekg (0, ios::beg);
file.read (buffer, filesize);
file.close();
n = write_all(socket, buffer, filesize); // use filesize here
delete[] buffer; // !!
}
To simplify processing on the other side, you may want to send filesize first to avoid parsing of the RIFF header to know how many bytes to accept. I would also suggest allocating a smaller buffer and reading several times to send the larger files over:
if(file.is_open()) {
filesize = file.tellg();
file.seekg(0, ios::beg);
uint32_t remains = filesize;
write(socket, &remains, sizeof(uint32_t));
// write 4B with size of the file (optional)
buffer = new char[(filesize > 4096)? 4096 : filesize];
// only up to 4k buffer to avoid running out of memory
n = 0;
while(remains > 0) {
int chunk = (remains > 4096)? 4096 : remains;
// decide how much to read in at one time (not more than size of the buffer)
file.read(buffer, chunk);
n += write_all(socket, buffer, chunk);
// read a chunk and write it to the socket
remains -= chunk;
// update number of bytes that remains to be transferred
}
// send the file several times
file.close();
delete[] buffer; // !!
}
You may notice the use of a helper function write_all. That is required, because the socket might get full and then write will not write all the data given to it. It could look like this:
size_t write_all(int socket, const char *buffer, size_t size)
{
size_t n = 0;
while(size > 0) {
size_t written = write(socket, buffer, size);
if(written == -1)
return written; // handle errors
n += written;
size -= written;
}
return n;
}
buffer is of type char*, so sizeof(buffer) is the size of an ordinary data pointer on your platform. So you're writing 4 or 8 bytes (assuming ordinary plateform) to your socket.
You need to put filesize in the write call instead of sizeof(buffer).
(sizeof is a compile-time construct. It's evaluated when your code is compiled. It can't return a size that's determined at runtime.)
In the line n = write(socket, buffer, sizeof(buffer)); the value of the last buffer becomes the size of a pointer on your platform. Since only four bytes are sent, I will assume that you are either on a 32-bit platform or compiling the application in 32-bit mode.
Replacing sizeof(buffer) will only be a partial solution since write() may not write all the data at once. You will need to check the value returned from write() and keep record of how many bytes have been written to the socket.
I used sndfile for handling wav files.

How to send a file with http with c++

I want to write a server side code. It should work with popular browsers and wget. My server check that file exists or not, if exists then browser can download it. But I have some problems.
Honestly, I read lots of question-answer (for example: Send binary file in HTTP response using C sockets) but I didn't find out. My browser (Chrome) can get text. But I cannot send any binary data or images etc. I am changing header according to downloading files. But I cannot send a downloadable files yet.
I have some questions.
void *clientWorker(void * acceptSocket) {
int newSocket = (int) acceptSocket;
char okStatus[] = "HTTP/1.1 200 OK\r\n"
"Content-Type: text/html\r\n"
"Connection: close\r\n"
"Content-Length: 20\r\n"
"\r\n"
"s";
writeLn(newSocket, okStatus);
const char * fileName = "/home/tyra/Desktop/example.txt";
sendF(newSocket, fileName);
}
1- If I wouldn't write "s" or something else inokStatus, my message cannot send. I don't understand anything of this.
This is writeLn function :
void writeLn(int acceptSocket, const char * buffer) {
int n = write(acceptSocket, buffer, strlen(buffer) - 1);
if (n < 0) {
error("Error while writing");
}
}
This is sendF function :
string buffer;
string line;
ifstream myfile(fileName);
struct stat filestatus;
stat(fileName, &filestatus);
int fsize = filestatus.st_size;
if (myfile.is_open()) {
while (myfile.good()) {
getline(myfile, line);
buffer.append(line);
}
cout << buffer << endl;
}
writeLn(acceptSocket, buffer.c_str());
cout << fsize << " bytes\n";
A little messy. I haven't used file size yet. If I send a file, then I rearrange these things.
2- I can send text and browser demonstrates it but browser didn't understand new lines.
If text file contains (123\n456\n789), browser demonstrates (123456789). I think I should change Content-Type header, but I didn't find out.
I don't want that browser demonstrates text files. Browser should download it. How can I send downloadable files?
Sorry, I explain everything pretty complicated.
As to your first question, you should find out the exact size of the file and specify it in your "Content-Length: xxxx\r\n" header. And, of course, you should ensure that the data is sent completely out.
Indeed, in your writeF function you use a std::string as a buffer:
string buffer;
this is not appropriate for binary data. You should allocate a raw char array of the right size:
int fsize = file.tellg();
char* buffer = new char[fsize];
file.seekg (0, ios::beg);
file.read (buffer, size);
file.close();
As to the second point, when your data is not HTML, specify as Content-Type: text/plain;
otherwise, your carriage return should be represented by <br> instead of "\r\n".
In case of binary downloads, to have the data download as a file (and not shown in the browser), you should specify
Content-Type: application/octet-stream
The issue is strlen here. strlen terminates when it gets a '\0' character. In binary file you will have a number of '\0' characters.
While reading the file you should find out the file size. This size should be used in int n = write(acceptSocket, buffer, strlen(buffer) - 1); in place of strlen
Change the writeLn(acceptSocket, buffer.c_str()); to writeLn(acceptSocket, buffer.c_str(), buffer.size()); and try...
For the case of 123\n456\n789 you need to send <PRE>123\n456\n789</PRE> as browser will parse this text as html and not like the OS parses and shows the output. The other way you can do is replace all \n with <BR> ...
Regarding question 1 - if you don't want to send any content back then remove the s from the end of okStatus and specify Content-Length: 0\r\n in the header