You are sending strings through stream sockets and expect them to be sent and received atomically, e.g. either nothing is sent/received or the entire string is sent/received. This is not how stream sockets work.
Stream sockets often send only part of your data, so you need to keep sending until all data has been sent. Same is for receiving.
You also need to delimit the messages somehow, otherwise when receiving you won't know when a message ends and the next one starts. The two most common ways are a) prefix messages with their size, b) use a message delimiter (e.g. new-line symbol).
ZeroMQ can do both of these tasks for you: your applications end up sending and receiving complete messages, without you having to implement message framing and sending/receiving on byte level.
The updated code still does not correctly use send
and recv
calls.
Here is correct usage with functions to send and receive a std::string
:
#include <stdexcept>
#include <stdint.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
ssize_t recv_all(int fd, void* buf, size_t buf_len) {
for(size_t len = buf_len; len;) {
ssize_t r = ::recv(fd, buf, len, 0);
if(r <= 0)
return r;
buf = static_cast<char*>(buf) + r;
len -= r;
}
return buf_len;
}
ssize_t send_all(int fd, void const* buf, size_t buf_len) {
for(size_t len = buf_len; len;) {
ssize_t r = ::send(fd, buf, len, 0);
if(r <= 0)
return r;
buf = static_cast<char const*>(buf) + r;
len -= r;
}
return buf_len;
}
void send_string(int fd, std::string const& msg) {
ssize_t r;
// Send message length.
uint32_t len = msg.size();
len = htonl(len); // In network byte order.
if((r = send_all(fd, &len, sizeof len)) < 0)
throw std::runtime_error("send_all 1");
// Send the message.
if((r = send_all(fd, msg.data(), msg.size())) < 0)
throw std::runtime_error("send_all 2");
}
std::string recv_string(int fd) {
ssize_t r;
// Receive message length in network byte order.
uint32_t len;
if((r = recv_all(fd, &len, sizeof len)) <= 0)
throw std::runtime_error("recv_all 1");
len = ntohl(len);
// Receive the message.
std::string msg(len, '\0');
if(len && (r = recv_all(fd, &msg[0], len)) <= 0)
throw std::runtime_error("recv_all 2");
return msg;
}