套接字 - 在数据传输后保持套接字打开

时间:2017-04-30 13:37:05

标签: c++ linux sockets

我编写了简单的服务器/客户端程序,其中客户端将一些硬编码的数据以小块的形式发送到服务器程序,服务器程序正在等待数据,以便将其打印到终端。在客户端,我在循环中调用send(),而有更多数据要发送,而在服务器上,我用read()做同样的事情,即字节数返回的是> 0,我继续读。

如果我在完成发送后专门在客户端套接字上调用close(),这个示例效果很好,但是如果我不这样做,服务器实际上不会退出read()循环,直到我关闭客户端并断开连接。在服务器端,我使用:

while((bytesRead = read(socket, buffer, BUFFER_SIZE)) > 0)

当收到所有数据时,不应该是字节读数是0吗?如果是这样,为什么在关闭套接字之前它不会退出这个循环?在我的最终应用程序中,在请求之间保持套接字打开是有益的,但是我发现的所有示例代码和信息在发送数据后立即调用close(),这不是我想要的。

我错过了什么?

2 个答案:

答案 0 :(得分:3)

当套接字的另一端连接到世界各地的某个其他网络系统时,接收套接字知道“何时收到所有数据”的唯一方法就是当套接字的另一端关闭时。这就是套接字的另一面告诉“所有数据都已收到”。

套接字知道的是它连接到其他一些套接字端点。而已。故事结局。套接字对程序的内部工作没有特别的了解,该程序具有套接字连接的另一面。也不应该知道。这恰好是套接字打开的程序的责任,而不是套接字本身。

如果您的计划在接收方拥有知识 - 通过知道预期接收的数据 - 它现在已经收到了它需要接收的所有数据,那么它可以关闭它的结束套接字,然后继续下一个任务。

您必须在程序的逻辑中加入一种方式,以某种形式或方式确定所有数据都已传输。具体的性质取决于你定义。也许,在发送套接字上的所有数据之前,您的发送程序将在同一套接字上预先发送将要跟随的数据中的字节数。然后,您的接收程序首先读取字节数,然后读取数据本身,然后知道它已收到所有内容,并且可以继续。

这是一种简单的方法。具体细节取决于您。或者,您也可以实现超时:设置计时器,如果在某段规定的时间内没有收到任何数据,则假设没有更多。

答案 1 :(得分:0)

您可以在recv调用上设置一个标志以防止阻塞。

轻松检测此问题的一种方法是包装recv调用:

enum class read_result
{
    // note: numerically in increasing order of severity
    ok,
    would_block,
    end_of_file,
    error,
};

template<std::size_t BufferLength>
read_result read(int socket_fd, char (&buffer)[BufferLength], int& bytes_read)
{
    auto result = recv(socket_fd, buffer, BufferLength, MSG_DONTWAIT);
    if (result > 0)
    {
        return read_result::ok;
    }
    else if (result == 0)
    {
        return read_result::end_of_file;
    }
    else {

        auto err = errno;

        if (err == EAGAIN or err == EWOULDBLOCK) {
            return read_result::would_block;
        }
        else {
            return read_result ::error;
        }
    }
}

一个用例可能是:

#include <unistd.h>
#include <sys/socket.h>
#include <cstdlib>
#include <cerrno>
#include <iostream>

enum class read_result
{
    // note: numerically in increasing order of severity
    ok,
    would_block,
    end_of_file,
    error,
};

template<std::size_t BufferLength>
read_result read(int socket_fd, char (&buffer)[BufferLength], int& bytes_read)
{
    auto result = recv(socket_fd, buffer, BufferLength, MSG_DONTWAIT);
    if (result > 0)
    {
        return read_result::ok;
    }
    else if (result == 0)
    {
        return read_result::end_of_file;
    }
    else {

        auto err = errno;

        if (err == EAGAIN or err == EWOULDBLOCK) {
            return read_result::would_block;
        }
        else {
            return read_result ::error;
        }
    }
}

struct keep_reading
{
    keep_reading& operator=(read_result result)
    {
        result_ = result;
    }

    const operator bool() const {
        return result_ < read_result::end_of_file;
    }

    auto get_result() const -> read_result { return result_; }

private:
    read_result result_ = read_result::ok;
};

int main()
{
    int socket; // = open my socket and wait for it to be connected etc

    char buffer [1024];
    int bytes_read = 0;
    keep_reading should_keep_reading;
    while(keep_reading = read(socket, buffer, bytes_read))
    {
        if (should_keep_reading.get_result() != read_result::would_block) {
            // read things here
        }
        else {
            // idle processing here
        }
    }

    std::cout << "reason for stopping: " << should_keep_reading.get_result() << std::endl;
}