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());
}
Related
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
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!
I'd like to use the method "read_some()" of boost::asio::ip::tcp::socket to fill a buffer represented as a char*.
Here is my method implementation so far:
template<class charType>
int receive_some(charType* buffer, int size)
{
int total_received = 0;
boost::array<charType, size> buf;
while (1)
{
int received = 0;
boost::system::error_code error;
received = _socket.read_some(boost::asio::buffer(buf), error);
if (error == boost::asio::error::eof)
{
break;
}
std::cout.write(buf.data(), received);
total_received += received;
}
return total_received;
}
My problem is I don't see how to convert my charType* buffer into boost::array buf. It seems expensive to iterate over the elements of my boost::array at the end of the process just to fill-in the buffer object...
Any idea ?
template<class charType>
int receive_some(charType* buffer, int size)
{
int total_received = 0;
while (1)
{
int received = 0;
boost::system::error_code error;
received = _socket.read_some(boost::asio::buffer(buffer, size), error);
if (error == boost::asio::error::eof)
{
break;
}
std::cout.write(buffer, received);
total_received += received;
}
return total_received;
}
The boost::asio::buffer function has a lot of overloads to allow to create an asio buffer from diffrent types of sources.
It's worth noting that size has to be the number of bytes to read into buffer and not the number of charType.
Bonus tip: As comments pointed out, that template is suspicious, the best you could do with it is directly write into wide strings but that might better be somewhere else than in a read_some function (actually it might even be better nowhere), in a network function you deal with bytes not characters so you'd better take a simple char* or even void* as a type for the buffer parameter.
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;
}
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Closed 8 years ago.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Improve this question
This long code is sending a POST request using a Socket, the whole code works without any complain, but what i'm facing right now is, that it eating a lot of cpu power, uses too much memory (RAM)
I can see that, because my laptop is getting hot very fast and by looking checking at my mac.
i have tried to find the bug or where , that very large memory Issue is, but couldn't. I have spent over a month trying to fix it by my own, but don't know what i'm doing to be honest.
i was so desperate, that i putted all kind of freeing metod.. even that it wrong.. just to see if that made a change, but nothing did..
So now i don't know what wrong or how to fix this, please help me out…
Going update the code with select, moment... trying to clean it up first
First thing: do NOT mix malloc() and delete[]. They might or might not refer to the same memory allocator. So use the malloc()/free() or new char[]/delete[] pairs.
The problems is here (in Database() function): you've got a terrible memleak. Do not allocate memory like this for result passing. Better use buffers. You program is multithreaded so use the buffers on the stack. Do NOT call delete[] on anything that is not allocated by you (var declaration like "char Buf[100];" is not an allocation).
New version (I omitted main() and strip() functions. Also the includes):
#define MAX_ECHO_SIZE (1024)
#define MAX_RESPONSE_SIZE (1024)
void process_http(int sockfd, const char *host, const char *page, const char *poststr, char* OutResponse)
{
char* ptr;
char sendline[MAXLINE + 1], recvline[MAXLINE + 1];
ssize_t n;
snprintf(sendline, MAXSUB,
"POST %s HTTP/1.0\r\n" // POST or GET, both tested and works. Both HTTP 1.0 HTTP 1.1 works, but sometimes
"Host: %s\r\n" //oth HTTP 1.0 HTTP 1.1 works, but sometimes HTTP 1.0 works better in localhost type
"Content-type: application/x-www-form-urlencoded\r\n"
"Content-length: %d\r\n\r\n"
"%s\r\n", page, host, (unsigned int)strlen(poststr), poststr);
if (write(sockfd, sendline, strlen(sendline))>= 0)
{
while ((n = read(sockfd, recvline, MAXLINE)) > 0)
{
recvline[n] = '\0';
if(fputs(recvline,stdout) ==EOF) { cout << ("fputs erros"); }
ptr = strstr(recvline, "\r\n\r\n");
strip(ptr, "\r\n\r\n");
// check len for OutResponse here ?
snprintf(OutResponse, 6000,"%s", ptr);
}
}
}
int Database( const char * hname, const char * page, const char * var, const char * poststr, int port, char* EchoResponse, int MaxEchoLen){
char url[MAXLINE];
char response[MAX_RESPONSE_SIZE];
snprintf(url, MAXLINE, "%s=%s", var, poststr);
short int sockfd ;
struct sockaddr_in servaddr;
struct hostent *hptr;
char str[MAXLINE];
char** pptr;
hptr = gethostbyname(hname);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (!hptr) {
cout << ("host not found\n");
return -1; // "host not found";
}
if (hptr->h_addrtype == AF_INET && (pptr = hptr->h_addr_list) != NULL) {
inet_ntop(hptr->h_addrtype, *pptr, str, sizeof(str));
}
if (sockfd >= 0 ) {
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(port);
inet_pton(AF_INET, str, &servaddr.sin_addr);
if (connect(sockfd, (SA *) & servaddr, sizeof(servaddr)) < 0) {
return -2; // "Server down, connect error";
}
else {
process_http(sockfd, hname, page, url, &response[0], MAX_RESPONSE_SIZE);
int len = strlen(response)+1;
if(len >= MaxEchoLen) { return -3; /* buffer too small*/ }
// Copy the contents with
strcpy(EchoResponse, response);
/// You must not free all of char[] allocated on stack
close(sockfd);
return 0; // OK
}
}
}
void *multithreading1( void *ptr ) {
char LocalEchoResponse[MAX_ECHO_SIZE];
while (1) {
int RetCode = Database("2.107.xx.xxx", "/ajax.php", "submit", "HEllo WORLD", 80, &LocalEchoResponse[0], MAX_ECHO_SIZE);
/// check the error
}
}
You have a pretty big memory leak in strip_copy. Whatever you put after the return will never get executed. Im surprised the compiler didnt complain about this. Same problem in the process_http() function.
Fix it like this:
static void strip_copy(char const *s, char *buf, const char * SPACE)
{
if (buf)
{
char *p = buf;
char const *q;
int n;
for (q = s; *q; q += n + strspn(q+n, SPACE))
{
n = strcspn(q, SPACE);
strncpy(p, q, n);
p += n;
}
*p++ = '\0';
buf = (char*)realloc(buf, p - buf);
}
}
// Then call it like this
char *buf = new[1 + strlen(s)];
strip_copy(s, buf, ' ');
// use buf
delete [] buf;
And in process_http()
const char* process_http(int sockfd, const char *host, const char *page, const char *poststr)
{
....
// delete here only what you dynamically
// allocated with new() BEFORE the return
return response; // n
}
And DONT mix malloc() with delete():
malloc() goes with free()
new() goes with delete()
This isnt related to the memory used, but instead of calling read()/write() directly, you should use select() to know when its ready to be read or written. Here's a related question: https://stackoverflow.com/a/10800029/1158895