简体   繁体   English

如何在使用 C/C++ 的 AF_UNIX 套接字中使用 recv 逐条读取消息?

[英]How can I read message by message with recv in an AF_UNIX socket with C/C++?

I am trying to write a client for my AF_UNIX server.我正在尝试为我的 AF_UNIX 服务器编写一个客户端。 The server occasionally does a服务器偶尔会做一个

write(fd, buffer, bufferSize);

where the message always ends in a newline.消息总是以换行符结尾。

Now, I want a C client to read it, message by message.现在,我想要一个 C 客户端来逐条阅读它。

With bash, it's possible to do something like this:使用 bash,可以执行以下操作:

socat - UNIX-CONNECT:/tmp/myepicsocket.sock | while read line; do handle $line; done

How would I go about implementing this in C/C++?我将如何 go 在 C/C++ 中实现这个?

I've tried a simple我试过一个简单的

char buffer[1024]; // my sent messages are max 1024 bytes
size_t recieved = recv(socketfd, buffer, 1024, NULL);

std::string messageRecieved(buffer);
messageRecieved = messageRecieved.substr(0, messageRecieved.find_first_of('\n'));

in a loop, but it seems like recv() gets the message from the beginning of the "file" (meaning once the first message is sent, it will keep spamming that first one)在一个循环中,但似乎 recv() 从“文件”的开头获取消息(这意味着一旦发送了第一条消息,它将继续发送第一条消息)

is there a way to read it line by line, message by message, or flush?有没有办法逐行、逐个消息或刷新?

recv(socketfd, buffer, 1024, NULL);

With stream sockets, like AF_UNIX sockets, recv() is equivalent to read() .对于 stream sockets,像AF_UNIX sockets 一样, recv()等效于read() That's what makes them "stream" sockets.这就是使它们“流式传输”sockets 的原因。 So, for all practical matters, this is:因此,对于所有实际问题,这是:

read(socketfd, buffer, 1024);

And just like with a regular file you have no guarantees whatsoever that this reads only up until the first newline.就像使用常规文件一样,您无法保证它只会读取到第一个换行符。 If the peer managed to write two short messages before you get around here, this read() will cough up both of them, at once, and place them into the buffer .如果对等方在你到达这里之前设法写了两条短消息,这个read()将立即把它们都咳出来,并将它们放入buffer中。

So what to do?那么该怎么办?

Well, the answer here is the same answer this question always has: write C++ logic to implement it.好吧,这里的答案与这个问题总是有相同的答案:编写 C++ 逻辑来实现它。

The buffer needs to be persistent.缓冲区需要是持久的。 You need to keep track of how many "unread" characters exist in the buffer, and the buffer is effectively "initialized" to an empty state by setting the unread count to 0.您需要跟踪缓冲区中存在多少“未读”字符,并且通过将未读计数设置为 0,缓冲区有效地“初始化”为空 state。

Each time your code decides to read a "line", it checks if the buffer contains anything, and that "anything" includes a newline.每次您的代码决定读取“行”时,它都会检查缓冲区是否包含任何内容,并且“任何内容”都包含换行符。 If so, there's your line.如果是这样,那就是你的线。 Pull it out of the buffer, and update the contents of the buffer, and the unread count, to reflect that this "line" is no longer there.将其拉出缓冲区,并更新缓冲区的内容和未读计数,以反映该“行”不再存在。 If there isn't a newline in there, only then you read from the socket, and append what's read after the buffer's existing contents, so if the previous read left ended with a partially read line, this will correctly reassemble the pieces.如果那里没有换行符,那么只有你从套接字读取, append在缓冲区的现有内容之后读取的内容,所以如果之前的读取以部分读取的行结束,这将正确地重新组装这些片段。 Then go back to Step 1.然后 go 回到步骤 1。

Of course, if the buffer carries a fixed size, like 1024 bytes, there's a possibility that you'll read 1024 characters without seeing a newline.当然,如果缓冲区的大小是固定的,比如 1024 字节,那么您可能会读取 1024 个字符而没有看到换行符。 You don't have anywhere to read() anything more, into.您没有任何地方可以read()更多内容。 You'll need to decide how to handle that situation, and implement the appropriate logic for that too.您需要决定如何处理这种情况,并为此实施适当的逻辑。 And you'll also need to figure out what you will do if your read() or recv() fails or indicates that the socket is closed.而且您还需要弄清楚如果您的read()recv()失败或指示套接字已关闭,您将做什么。

So that's the most direct and the simplest way of handling that.所以这是最直接和最简单的处理方式。 Another approach, that involves slightly more advanced C++ knowledge and expertise is to implement your own subclass of std::streambuf that uses the socket as the underlying data source, and override the appropriate methods, then use it to construct a std::istream , and then simply use std::getline() whenever you feel the need to pull out the next line out of it.另一种方法,涉及稍微更高级的 C++ 知识和专业知识,是实现您自己的std::streambuf子类,该子类使用套接字作为底层数据源,并覆盖适当的方法,然后使用它来构造std::istream ,然后只要您觉得需要从中拉出下一行,只需使用std::getline()即可。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM