Sending a c++ object with vector over socket - c++

I have for quite some time now tried to find a good way to serialize or send a state object over tcp socket. My problem is that I am not able to use any 3. party libraries like boost.
My state object contains multiple objects. The most essential is that it got some objects and a vector of objects, but no pointers (eg. probably no deep copying, if vector dont require this).
To my question: Since I cant use boost or any other libraries, what is the best way to send a object with objects over socket?
I have been thinking that I probably could make a copy constructor and send this to a stream, but I am not quite sure about the consequences of doing this.

Define (de-)serialization functions for your data types.
For example, if you have something like:
class MyClass
{
public:
int field_a;
int field_b;
std::string string;
...
};
typedef std::vector<MyClass> MyVector;
You can define the following:
void write(int fd, const MyClass &arg)
{
// either convert arg to byte array and write it, or write field by field
// here we write it field by field
write_int(fd, arg.field_a);
write_int(fd, arg.field_b);
write_string(fd, arg.string);
}
void write(int fd const MyVector &arg)
{
size_t size = arg.size();
::write(fd, &size, sizeof(size)); // beware: machine-dependent code
for (MyVector::const_iterator it = arg.begin(); it != arg.end(); it++)
{
write(*it);
}
}
Helper functions:
void write_int(int fd, int arg)
{
write(fd, &arg, sizeof(int));
}
void write_string(int fd, const std::string &str)
{
size_t len = str.length();
write(fd, &len, sizeof(len)); // string length go first
write(fd, str.data(), len); // write string data
}
And reading:
MyClass readMC(int fd)
{
// read MyClass data from stream, parse it
int f1, f2;
std::string str;
read_int(fd, f1);
read_int(fd, f2);
read_string(fd, str)
return MyClass(f1, f2, str);
}
void read(int fd, MyVector &arg)
{
size_t size;
size_t i;
read(fd, &size, sizeof(size)); // read number of elements;
arg.reserve(size);
for (i = 0; i < size; i++)
{
arg.push_back(readMC(fd));
}
}
Helper functions:
void read_int(int fd, int &res);
{
read(fd, &res, sizeof(res));
}
void read_string(int fd, std::string &string)
{
size_t len;
char *buf;
read(fd, &len, sizeof(len));
buf = new char[len];
read(fd, buf, len);
string.asssign(buf, len);
delete []buf;
}

Related

How to mock system call in C++ Unit Testing during runtime using GoogleMock?

I have a module written in C. In which I have API defined as follow.
int ReadData(void* data, int data_size, char* filename, int offset)
When performing unit test in order to cover full coverage I need to hit at error condition for fseek and fread system call.
Can somebody explain me How can I override fseek/fread during particular test case so instead of system call it will call mock fseek/fread for that test case ?
ReadAPI.c
#include<stdio.h>
#include<stdlib.h>
int readdata(void* data,int size, int offset, char* filename)
{
if(data == NULL || size == 0 || filename == NULL)
{
return -1;
}
FILE* fp = fopen(filename,"rb");
if(fp == NULL)
{
return -1;
}
if(fseek(fp,offset,SEEK_SET) != 0)
{
fclose(fp);
return -1;
}
fread(data,size,1,fp);
if(ferror(fp))
{
fclose(fp);
return -1;
}
fclose(fp);
return 1;
}
readapi.h
#include<stdio.h>
#include<stdlib.h>
int readdata(void* data,int size, int offset, char* filename)
Test.cpp
#include<gtest/gtest.h>
#include"readapi.h"
TEST(test, Test1fseek)
{
// When fseek called in readdata API call mock fseek to hit fseek fail
// fseek(){return -1;}
char data[10] = {0};
int status = readdata(data,sizeof(data),0,"test.txt");
EXPECT_EQ(status, -1);
}
TEST(test, Test2fread)
{
//When fread called in readdata API call mock fread to hit read fail
// fread(){return -1;}
char data[10] = {0};
int status = readdata(data,sizeof(data),0,"test.txt");
EXPECT_EQ(status, -1);
}
int main()
{
return RUN_ALL_TEST();
}
You can refer to mocking-free-functions in Gmock's documentation.
Unfortunately, using the recommended method means you will have to change your code, which may not work if your code need to strictly be a C code.
However, if you are willing to accept this, based on the documentation, you will have to create a wrapper around all the system functions that you are using and then mock that wrapper.
Also, don't forget that you will have to add EXPECT_CALL or ON_CALL for all functions that are expected to be called and return a non-default (i.e. 0) value. For example, in your first test, you should also provide EXPECT_CALL or ON_CALL for fopen.
Here is an example implementation:
// System wrapper interface class.
class MySystemWrapperInterface {
public:
virtual FILE* fopen(const char* filename, const char* mode) = 0;
virtual int fseek(FILE* stream, long int offset, int whence) = 0;
virtual size_t fread(void* ptr, size_t size, size_t nmemb, FILE* stream) = 0;
virtual int ferror(FILE* stream) = 0;
virtual int fclose(FILE* stream) = 0;
};
// System wrapper actual class used in production code.
class MySystemWrapperClass : public MySystemWrapperInterface {
public:
FILE* fopen(const char* filename, const char* mode) {
return ::fopen(filename, mode);
}
int fseek(FILE* stream, long int offset, int whence) {
return ::fseek(stream, offset, whence);
}
size_t fread(void* ptr, size_t size, size_t nmemb, FILE* stream) {
return ::fread(ptr, size, nmemb, stream);
}
int ferror(FILE* stream) { return ::ferror(stream); }
int fclose(FILE* stream) { return ::fclose(stream); }
};
// Mocked System wrapper used for testint.
class MySystemWrapperMockClass : public MySystemWrapperInterface {
public:
MOCK_METHOD(FILE*, fopen, (const char*, const char*), (override));
MOCK_METHOD(int, fseek, (FILE*, long int, int), (override));
MOCK_METHOD(size_t, fread, (void*, size_t, size_t, FILE*), (override));
MOCK_METHOD(int, ferror, (FILE*), (override));
MOCK_METHOD(int, fclose, (FILE*), (override));
};
// Wrapper class for your own readdata function.
class MyClass {
// The system wrapper passed by dependency injection through constructor.
MySystemWrapperInterface* system_wrapper_;
public:
// Initialize the system wrapper in constructor.
MyClass(MySystemWrapperInterface* system_wrapper)
: system_wrapper_(system_wrapper) {}
int readdata(void* data, int size, int offset, char* filename) {
if (data == NULL || size == 0 || filename == NULL) {
return -1;
}
FILE* fp = system_wrapper_->fopen(filename, "rb");
if (fp == NULL) {
return -1;
}
if (system_wrapper_->fseek(fp, offset, SEEK_SET) != 0) {
system_wrapper_->fclose(fp);
return -1;
}
system_wrapper_->fread(data, size, 1, fp);
if (system_wrapper_->ferror(fp)) {
system_wrapper_->fclose(fp);
return -1;
}
system_wrapper_->fclose(fp);
return 1;
}
};
TEST(test, Test1fseek) {
// Create the mock object and inject it into your class.
MySystemWrapperMockClass mock_system_wrapper;
MyClass my_object(&mock_system_wrapper);
// When fseek called in readdata API call mock fseek to hit fseek fail
// fseek(){return -1;}
// IMPORTANT: Don't forget to add EXPECT_CALL or ON_CALL for all functions
// that are expected to be called.
EXPECT_CALL(mock_system_wrapper, fopen)
.Times(1)
.WillOnce(Return(reinterpret_cast<FILE*>(0x1)));
EXPECT_CALL(mock_system_wrapper, fseek).Times(1).WillOnce(Return(1));
EXPECT_CALL(mock_system_wrapper, fclose).Times(1).WillOnce(Return(1));
char data[10] = {0};
int status = my_object.readdata(data, sizeof(data), 0, "test.txt");
EXPECT_EQ(status, -1);
}
TEST(test, Test2fread) {
// Create the mock object and inject it into your class.
MySystemWrapperMockClass mock_system_wrapper;
MyClass my_object(&mock_system_wrapper);
// When fread called in readdata API call mock fread to hit read fail
// fread(){return -1;}
// IMPORTANT: Don't forget to add EXPECT_CALL or ON_CALL for all functions
// that are expected to be called.
EXPECT_CALL(mock_system_wrapper, fopen)
.Times(1)
.WillOnce(Return(reinterpret_cast<FILE*>(0x1)));
EXPECT_CALL(mock_system_wrapper, fseek).Times(1).WillOnce(Return(0));
EXPECT_CALL(mock_system_wrapper, fread).Times(1).WillOnce(Return(-1));
EXPECT_CALL(mock_system_wrapper, ferror).Times(1).WillOnce(Return(-1));
EXPECT_CALL(mock_system_wrapper, fclose).Times(1).WillOnce(Return(1));
char data[10] = {0};
int status = my_object.readdata(data, sizeof(data), 0, "test.txt");
EXPECT_EQ(status, -1);
}
Live example: https://godbolt.org/z/qxf74fWGh
If you are using gcc/g++ or clang/clang++ you can use the linker option --wrap=symbol to redirect calls to your own versions of these functions.
Your wrapper functions will have the name __wrap_symbol and the real functions will be named __real_symbol.
Example:
int __wrap_fseek(FILE *stream, long offset, int whence) {
printf("__wrap_fseek: %ld %d\n", offset, whence);
return __real_fseek(stream, offset, whence);
}
size_t __wrap_fread(void *restrict ptr, size_t size, size_t nmemb,
FILE *restrict stream)
{
return __real_fread(ptr, size, nmemb, stream);
}
Compile with g++ ... -Wl,--wrap=fseek,--wrap=fread

std::vector buffer throwing bad_alloc in TCP socket code

I am trying to send and receive a string using a TCP socket. I found some code online and modified it. Here is my sendString and receiveString code:
static inline void sendString(int socket, std::string s) {
size_t size = s.size();
size_t size_size = sizeof(size_t); // We make our buffer:
std::vector<char> buffer(size + size_size); // Put the size at the front:
char* size_begin = reinterpret_cast<char*>(&size);
std::copy(size_begin, size_begin + size_size, &(buffer[0])); // Copy the string data:
std::copy(s.begin(), s.end(), &(buffer[size_size])); // And finally send it:
send(socket, &buffer, size + size_size, 0);
}
std::string receiveString(int socket) {
size_t size_size = sizeof(size_t);
size_t size; // We read the size:
recv(socket, (char*)&size, size_size, 0);
std::vector<char> buffer(size); /** XXX: BAD ALLOC*/
recv(socket, &buffer[0], size, 0);
return std::string(buffer.begin(), buffer.end());
}
When I try to have my client send an actual string, the server side throws a std::bad_alloc in receiveString where indicated by a comment. Why did similar code work in sendString but not in receiveString? What is causing the bad::alloc issues? Also, would my code work for sending and receiving a string over a TCP socket?
Thanks!
In sendString(), you are not passing the prepared vector content to send() correctly. You need to change &buffer to either &(buffer[0]) or buffer.data() instead.
That being said, the vectors are completely unnecessary in sendString() and recvString(). Just call send()/recv() multiple times, you can send/receive the size_t and string separately, and let the socket handle the buffering of bytes for you.
For that matter, send() and recv() are not guaranteed to actually send/receive the requested buffer in one go. You have to pay attention to their return values, calling them in loops until all bytes have actually been sent/received.
Also, you are not taking into account that different platforms have different sizes and endians for multi-byte integers. So you need to handle that better, too.
Try something more like this:
static inline void sendRaw(int socket, const void *buffer, size_t bufsize) {
const char *ptr = static_cast<const char*>(buffer);
while (bufsize > 0) {
int numSent = send(socket, ptr, bufsize, 0);
if (numSent < 0)
throw std::runtime_error("send failed");
ptr += numSent;
bufsize -= numSent;
}
}
static inline void sendUint32(int socket, uint32_t value) {
value = htonl(value);
sendRaw(socket, &value, sizeof(value));
}
static inline void sendString(int socket, const std::string &s) {
size_t size = s.size();
if (size > std::numeric_limits<uint32_t>::max())
throw std::runtime_error("string is too long in length");
sendUint32(socket, static_cast<uint32_t>(size));
sendRaw(socket, s.c_str(), size);
}
static inline void recvRaw(int socket, void *buffer, size_t bufsize) {
char *ptr = static_cast<char*>(buffer);
while (bufsize > 0) {
int numRecv = recv(socket, ptr, bufsize, 0);
if (numRecv < 0) throw std::runtime_error("recv failed");
if (numRecv == 0) throw std::runtime_error("peer disconnected");
ptr += numRecv;
bufsize -= numRecv;
}
}
static inline uint32_t recvUint32(int socket) {
uint32_t value;
recvRaw(socket, &value, sizeof(value));
return ntohl(value);
}
std::string receiveString(int socket) {
uint32_t size = recvUint32(socket);
std::string s;
if (size > 0) {
s.resize(size);
recvRaw(socket, &s[0], size);
}
return s;
}
std::bad_alloc is thrown when the system can't allocate the requested memory. Most likely - the size is too big.
My crystal ball tells me that you may witness an issue with endianness. I would convert host-to-network going up, and network-to-host on receive.
UPDATE:
As was pointed in multiple comments, if your call to recv() fails, the size will contain uninitialized garbage. You need to do two things to avoid that: initialize size with 0 AND check if recv() succeeded

Using std::string in ImGui::InputText(...)

The call to ImGui::InputText() takes a char array which I need to initialise from a std::string and then transfer the contents back to the std::string. In it's simplest form:
char buf[255]{};
std::string s{"foo"};
void fn() {
strncpy( buf, s.c_str(), sizeof(buf)-1 );
ImGui::InputText( "Text", buf, sizeof(buf) );
s=buf;
}
However, it appears wasteful to have two buffers (buf and the buffer allocated within std::string) both doing much the same thing. Can I avoid the buf buffer and the copying to and from it by using just the std::string and a simple wrapper "X".
I don't care about efficiency, I just want the simplest code at the call site.
This code does work but is it safe and is there a better way?
class X {
public:
X(std::string& s) : s_{s} { s.resize(len_); }
~X() { s_.resize(strlen(s_.c_str())); }
operator char*(){ return s_.data(); }
static constexpr auto len() { return len_-1; }
private:
std::string& s_;
static constexpr auto len_=255;
};
std::string s{"foo"};
void fn() {
ImGui::InputText( "Text", X(s), X::len() );
}
If you want to use InputText() with std::string or any custom dynamic string type, see misc/cpp/imgui_stdlib.h and comments in imgui_demo.cpp.
misc/cpp/imgui_stdlib.h
namespace ImGui
{
// ImGui::InputText() with std::string
// Because text input needs dynamic resizing, we need to setup a callback to grow the capacity
IMGUI_API bool InputText(const char* label, std::string* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
IMGUI_API bool InputTextMultiline(const char* label, std::string* str, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
IMGUI_API bool InputTextWithHint(const char* label, const char* hint, std::string* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
}
Your first code
std::string s{"foo"};
void fn() {
ImGui::InputText( "Text", &s );
}
Reading manuals works wonders.

Is there a way return and delete content of an std::string?

I am writing a socket server in C++ and need to wrapper the read function, from an std::string buffer. As you can see in the below code for reading a single byte (ClientConnection.readByte), it uses a char to store the value, delete it, then return the char. However I also want to implement a readEverything (ClientConnection.readEverything) function, which should return the entire buffer.
Right now, I just copy the buffer, but this doesn't seem memory efficient. This would be easy if I could return before readBuffer.erase(), but that can't work. Is there an efficient workaround, or do I just have to use my original method?
class ClientConnection{
private:
int connfd;
std::string writeBuffer;
std::string readBuffer;
struct sockaddr *address;
public:
ClientConnection(int fd){
connfd = fd;
}
bool available(){
char toReturn;
recv(connfd, &toReturn, 1, MSG_PEEK);
return toReturn!=-1;
}
char readByte(){
char thingy = readBuffer[0];
readBuffer.erase(0, 1);
return thingy;
}
std::string readEverything(){
std::string readBufferCopy = readBuffer;
readBuffer.erase();
return readBufferCopy;
}
void writeByte(char byte){
writeBuffer += byte;
}
void polleverything(){
write(connfd, writeBuffer.c_str(), writeBuffer.length());
char readBuffer[READOUTBUFFERSIZE];
while (recv(connfd, &toReturn, READOUTBUFFERSIZE, 0) == READOUTBUFFERSIZE){
std::cout << "HELLO";
};
}
};
The ClientConnection class is supposed to be passed file descriptors and otherwise used by a server class, which for the sake of easy-reading I omitted from this snippet.
Thanks in advance!

Send big string into socket

I'm new with C++ and came to this problem. I'm trying to send big string to a socket. I've seen the similar questions on stack but could not found the real answer. For example these:
Sending a long String over a Socket C++
Send a string with sockets in C++ (Winsock TCP/IP)
C++ sending string over socket
Most of them rely on fact that send would send the whole data in one call, or they would use char * instead of std::string.
Here is little code written in C:
int SendAll(SOCKET client_socket, const void *data, int data_size)
{
const char *data_ptr = (const char*) data;
int bytes_sent;
while (data_size > 0)
{
bytes_sent = send(client_socket, data__ptr, data_size, 0);
if (bytes_sent == SOCKET_ERROR)
return -1;
data_ptr += bytes_sent;
data_size -= bytes_sent;
}
return 1;
}
and now imagine that instead of const void *data we have std::string data. The question is how can I move pointer into data like this data_ptr += bytes_sent; with std::string?
One way that I came out is to retrieve the row pointer of std::stirng save it in some const char * var then use that variable in the same way(var += bytes_sent). But as I'm new with C++ I don't know if it's the "C++ way" of doing this? Is this the best solution to this problem or is there better one? thanks
Yes, that is the best way.
You have to obtain a pointer to the data anyway, to use send, so just adjust the pointer as you see fit.
Something like:
int SendAll(SOCKET client_socket, const std::string& str)
{
const char* data_ptr = str.data();
std::size_t data_size = str.size();
int bytes_sent;
while (data_size > 0)
{
bytes_sent = send(client_socket, data_ptr, data_size, 0);
if (bytes_sent == SOCKET_ERROR)
return -1;
data_ptr += bytes_sent;
data_size -= bytes_sent;
}
return 1;
}
This is perfectly fine and idiomatic.
If you want to keep both versions of the function, just forward the string's buffer to your existing overload:
int SendAll(SOCKET client_socket, const std::string& str)
{
return SendAll(
client_socket,
reinterpret_cast<const void*>(str.data()),
str.size()
);
}
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
This is the signature of send. It requires a pointer to the buffer. Although a C++ API would probably prefer a pair of iterators, rather than a pointer and a size, this is not really possible here, seeing that the pointer to the actual buffer is required. So, there's nothing you can do about it, really. You can just use the string's data() member function to get a poninter to the start of the buffer, and work with that. This should be perfectly fine.
As suggested by Some programmer dude in the comments, you could add a simple overload that facilitates this:
int SendAll(SOCKET client_socket, std::string const& str) {
return SendAll(client_socket, reinterpret_cast<const void*>(str.data()), str.size());
}