The send function in winsock2 accepts only char pointers.
How do I send integers or objects through it too?
const char *buf which you need to pass to send() function as an argument is just a pointer to array of bytes. You need to convert integers to bytes:
const int MAX_BUF_SIZE = 1024;
int int_data = 4;
const char *str_data = "test";
char *buf = (char*) malloc(MAX_BUF_SIZE);
char *p = buf;
memcpy(&int_data, p, sizeof(int_data));
p += sizeof(int_data);
strcpy(p, str_data);
p += strlen(str_data) + 1;
send(sock, buf, p - buf, 0);
free(buf);
and reading code:
const int MAX_BUF_SIZE = 1024;
int int_data = 0;
const char *str_data = NULL;
char *buf = (char*) malloc(MAX_BUF_SIZE);
char *p = buf;
recv(sock, buf, MAX_BUF_SIZE, 0);
memcpy(p, &int_data, sizeof(int_data));
p += sizeof(int_data);
str_data = malloc(strlen(p) + 1);
strcpy(str_data, p);
p += strlen(p) + 1;
free(buf);
and complex objects needs to be serialized to stream of bytes.
Note 1: The code sample is valid iff both server and client use the same platforms (x32 / x64 / ...) that means int has the same amount of bytes and byte order is the same.
Note 2: Writing code should check that there is no buffer (MAX_BUF_SIZE) overflow on each step.
Just store the value into a variable and then type-cast the variable to char*. The send() and recv() functions operate on binary data, despite taking char* parameters.
Sending:
int int_data = 4;
send(sock, (char*) &int_data, sizeof(int), 0);
Reading:
int int_data;
recv(sock, (char*) &int_data, sizeof(int), 0);
Generally, the easiest way is to print the integer or object to a string, and send that string. Textual representations are more portable, and also easier to debug.
std::stringstream may be a useful class both to create the string and parse it on the other end.
Related
I'm using following code to read from socket:
char buf[4097];
int ret = read(fd, buf, sizeof(buf) - 1);
buf[ret] = 0x0;
std::cout << buf << "\n";
However, I don't like the need for 4097 and sizeof(buf) - 1 in there. It's that kind of stuff that's easy to forget. So I wonder, is there some nice way to force compiler to but 0x0 directly on stack right after the array?
What I would love is something like
char buf[4096];
char _ = 0x0;
int ret = read(fd, buf, sizeof(buf));
buf[ret] = 0x0;
std::cout << buf << "\n";
but I have no idea how to force compiler to not but anything in between (afaik #pragma pack works only on structures, not on stack).
I'd keep things simple:
ssize_t read_and_put_0(int fd, void *buf, size_t count)
{
ssize_t ret = read(fd, buf, count - 1);
if (ret != -1) // Or `if (ret <= 0)`
((char *)buf)[ret] = 0x0;
return ret;
}
// ...
char buf[4097];
read_and_put_0(fd, buf, sizeof buf);
I don't like the need for 4097 and sizeof(buf) - 1 in there
Simplicity is beautiful:
constexpr std::size_t size = 4096;
char buf[size + 1];
int ret = read(fd, buf, size);
buf[ret] = 0x0;
You specify exactly the size that you need, no neet to do manual adding. And there's need for neither sizeof, nor subtracting 1.
Remembering the + 1 for terminator is easier in my opinion than remembering to declare a separate character object - which can't be forced to be directly after the array anyway.
That said, there are less error prone ways to read a text file than read.
The relative location in memory of the values of distinct variables in unspecified. Indeed, some variables might not reside in memory at all. If you want to ensure relative layout of data in memory then use a struct or class. For example:
struct {
char buf[4096];
char term;
} tbuf = { { 0 }, 0 };
int ret = read(fd, tbuf.buf, sizeof(tbuf.buf));
if (ret >= 0 && ret < sizeof(tbuf.buf)) {
tbuf.buf[ret] = '\0';
}
The members of the struct are guaranteed to be laid out in memory in the same order that they are declared, so you can be confident that the fail-safe terminator tbuf.term will follow tbuf.buf. You cannot, however, be confident that there is no padding between. Furthermore, this is just a failsafe. You still need to write the null terminator, as shown, in case there is a short read.
Additionally, even though the representation of tbuf is certain to be larger than its buf member by at least one byte, it still produces UB to access tbuf.buf outside its bounds. Overall, then, I don't think you gain much, if anything, by this.
An alternative to HolyBlackCats answer that doesn't require giving the size argument as long as you have the array and not a pointer to some array.
template <size_t N> ssize_t read_and_put_0(int fd, char (&buf)[N]) {
ssize_t ret = read(fd, buf, N - 1);
if(ret != -1) // Or `if (ret <= 0)`
buf[ret] = 0x0;
return ret;
}
char buf[4097];
read_and_put_0(fd, buf);
I am reading buffer bytes from a socket but I don't know how to initialize the buffer array with the length info.
uint32_t len;
int lengthbytes = 0;
int databytes = 0;
// receive the length info of an image data
lengthbytes = recv(clientSocket, (char *)&len, sizeof(len), 0);
// convert hexadecimal data to length in bytes
len = ntohl(len);
// ????? how to initialize the buffer array with the length info ????
char buf[len]; -----> this is illegal in C
// read the image data
databytes = recv(clientSocket, buf, sizeof(buf), 0);
len = ntohl(len);
char buf[len]; //----> this is illegal in C
This is valid in C99 and it is called a variable length array. If you are not using C99 use malloc to allocate the array (and declare buf as a char *).
When you declare buf you declare a variable length array. This is legal in C (from the C99 standard) but illegal in C++. In C++ you can instead use std::vector:
std::vector<char> buf(len);
You can use this vector in the call to recv as well:
databytes = recv(clientSocket, &buf[0], buf.size(), 0);
To use the vector inside a loop, you have two choices:
Declare the variable outside the loop, and use clear and resize when needed:
std::vector<char> buf;
// ...
for (int i = 0; i < number_of_images; i++)
{
std::cout << "Fetching image #" << (i + 1) << '\n';
// Get the image length
size_t length = get_image_length();
buf.clear(); // Clear the buffer
buf.resize(length); // Set the size to the image length
// Receive the image
databytes = recv(clientSocket, &buf[0], buf.size(), 0);
}
Declare the vector to be local inside the loop:
for (int i = 0; i < number_of_images; i++)
{
std::cout << "Fetching image #" << (i + 1) << '\n';
// Get the image length
size_t length = get_image_length();
std::vector<char> buf(length);
// Receive the image
databytes = recv(clientSocket, &buf[0], buf.size(), 0);
}
You have to use dynamic memory allocation;
char* buf = new char[len];
If you're done using buf, don't forget to call delete to free the memory.
delete[] buf;
Please allocate the buffer through malloc i.e. buf = malloc(sizeof(char) * len);
You can do it with new or malloc.
Don't forget to delete the buffer when done!
You can use an std::vector<char>, and then use it's data() as an array buffer:
#include <vector>
std::vector<char> buf(len);
databytes = recv(clientSocket, buf.data(), buf.size(), 0); // access underlying char array
databytes = recv(clientSocket, &buf[0], buf.size(), 0); // as above, C++03 version
I wrote a class called tempbuf precisely for this purpose in C++.
You can find it here:
small_lib.cpp
small_lib.h
These two files are MIT-licensed, so you can use it anyway you like.
How to use this class?
tempbuf buf(len);
databytes = recv(clientSocket, buf.get(), buf.size(), 0); // if you want char* returned
databytes = recv(clientSocket, buf.constchar(), buf.size(), 0); // if you want constchar* returned
And guess why I wrote this class? You don't need to delete or deallocate the dynamically allocated memory because it is done in the destructor of the class.
Why didn't I use std::auto_ptr? Because from my understanding, that's only for non-arrays as in it supports new X but not new X[10].
I want to receive bytes into conbuf.buffer. either of test 1 or test2 is ok. I am not seeing any value in printf statement. Am I passing in the pointer correctly? How do I allocate memory to a char pointer inside a struct.
typedef struct cBuf
{
int size;
char *buffer;
} cbuf;
class A
{
cbuf conbuf;
void test();
}
void A::test()
{
int buffersize = 20;
char buf[buffersize];
conbuf.buffer = (char *)malloc(buffersize * sizeof(char *));
// test 1
int n = socket.receivebytes(conbuf.buffer, buffersize);
// test 2
//int n = socket.receivebytes(buf, buffersize);
//strcpy(conbuf.buffer, buf);
printf("conbuf.buffer %s \n", conbuf.buffer);
}
this
conbuf.buffer = (char *)malloc(buffersize * sizeof(char *));
should be
conbuf.buf = (char *)malloc(buffersize * sizeof(char)); //allocate space for char not pointer to char. Your struct only has buf member not "buffer"
Use this:
conbuf.buffer = (char *)calloc( (buffersize * sizeof(char))+1,1);
since printf requires last char of string to be null terminated, this ensures it. But data received from network may already contain NUL in the middle. So instead of using printf you should use fwrite:
fwrite(conbuf.buffer,buffersize , STDOUT);
I'd suggest you to redirect it in a file and do a hex dump to see the output.
I need to convert an unsigned 64-bit integer into a string. That is in Base 36, or characters 0-Z. ulltoa does not exist in the Linux manpages. But sprintf DOES. How do I use sprintf to achieve the desired result? i.e. what formatting % stuff?
Or if snprintf does not work, then how do I do this?
You can always just write your own conversion function. The following idea is stolen from heavily inspired by this fine answer:
char * int2base36(unsigned int n, char * buf, size_t buflen)
{
static const char digits[] = "0123456789ABCDEFGHI...";
if (buflen < 1) return NULL; // buffer too small!
char * b = buf + buflen;
*--b = 0;
do {
if (b == buf) return NULL; // buffer too small!
*--b = digits[n % 36];
n /= 36;
} while(n);
return b;
}
This will return a pointer to a null-terminated string containing the base36-representation of n, placed in a buffer that you provide. Usage:
char buf[100];
std::cout << int2base36(37, buf, 100);
If you want and you're single-threaded, you can also make the char buffer static -- I guess you can figure out a suitable maximal length:
char * int2base36_not_threadsafe(unsigned int n)
{
static char buf[128];
static const size_t buflen = 128;
// rest as above
How could it be possible to send int (not using third party libraries) via windows sockets Send:
it requires (const char *) as parameter.
My attempt was to send int like this:
unsigned char * serialize_int(unsigned char *buffer, int value)
{
/* Write big-endian int value into buffer; assumes 32-bit int and 8-bit char. */
buffer[0] = value >> 24;
buffer[1] = value >> 16;
buffer[2] = value >> 8;
buffer[3] = value;
return buffer + 4;
}
but Send() wants (const char *). I'm stuck...
Ah, this is easily fixed. The compiler wants a char* (ignore const for now), but you're passing an unsigned char*, and the only real difference is how the compiler interprets each byte when manipulated. Therefore you can easily cast the pointer from unsigned char* to char*. This way:
(char*)serialize_int(...)
const int networkOrder = htonl(value);
const result = send(socket, reinterpret_cast<const char *>(&networkOrder), sizeof(networkOrder), 0);