Search code examples
c++socketsunixboost-asiosocat

UNIX domain socket C++ server can not return data to client


Here are the simple echo server I'm working on, the server will accept the request from client and return what client sends to it. The program works fine with socat, but will freeze when using my own client.

The problem that my old code has is that I use read instead of read_some. read will block the pipe until it reads certain number of bytes or get a broken pipe exception, whereas read_some will read a chunk at a time. The updated version uses read_some to read input stream and check if the last character the program read is \0, if it is \0, that means it reaches the end of command, so it will echo back. This works because I only pass string literals and there is no binary data in the pipe.

The code of the server is

using namespace std;

const char* epStr = "/tmp/socketDemo";

int main() {
  namespace local = boost::asio::local;

  boost::asio::io_service io_service;

  ::unlink(epStr);

  local::stream_protocol::endpoint ep(epStr);
  local::stream_protocol::acceptor acceptor(io_service, ep);

  while(1) {
    local::stream_protocol::socket *socket = new local::stream_protocol::socket(io_service);
    acceptor.accept(*socket);

    char buf[2048] = {0};
    boost::system::error_code error;

    size_t len = 0;
      while(1) {
        len += socket->read_some(boost::asio::buffer(buf + len, 2048 - len));
        cout << "read " << len << endl;
        if (buf[len] == '\0') {
          break;
        }
      }

    cout << "read " << len << " bytes" << endl;
    cout << buf << endl;

    boost::asio::write(*socket, boost::asio::buffer(buf, len), boost::asio::transfer_all());
  }
}

When testing the server with socat command, for example

echo "12345" | socat - UNIX-CONNECT:/tmp/socketDemo

it will return the desired result.

My client code is

const char* epStr = "/tmp/socketDemo";

int main(int argc, const char* argv[]) {
  boost::asio::io_service io_service;
  boost::asio::local::stream_protocol::endpoint ep(epStr);
  boost::asio::local::stream_protocol::socket socket(io_service);

  socket.connect(ep);

  boost::asio::write(socket, boost::asio::buffer(argv[1], strlen(argv[1])), boost::asio::transfer_all());

  char buf[1024] = {0};

  size_t len = 0;
    while(1) {
      len += socket.read_some(boost::asio::buffer(buf + len, 2048 - len));
      std::cout << "read " << len << std::endl;
      if (buf[len] == '\0') {
        break;
      }
    }

  std::cout << "read " << len << " bytes\n";

  std::cout << buf << std::endl;

  socket.close();

When execute the client, at first both have no output, after I killed the client, the server will output that it reads n bytes and get a broken pipe exception.

Can this be caused by the read function in the server? If so is there a way to let it know how much data it should read without sending the size of data chunk at the beginning of each message? I am also wondering why socat can work with this server without any problem? Thanks!


Solution

  • I am also wondering why socat can work with this server without any problem?

    Probably because socat closes the socket and your client doesn't.

    If so is there a way to let it know how much data it should read without sending the size of data chunk at the beginning of each message?

    For instance, reading one byte at a time until you read an end-of-message character, assuming that you're defining / using a protocol that includes EOM.