【问题标题】:Sockets - keeping a socket open after data transfer套接字 - 数据传输后保持套接字打开
【发布时间】:2017-09-28 03:43:09
【问题描述】:

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

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

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

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

我错过了什么?

【问题讨论】:

  • 你打开套接字时是否在服务器上将其标记为非阻塞? Minimal, Complete, and Verifiable example 会有所帮助...
  • 拿个电话。打电话给你认识的人。说“你好,你好吗?”别说别的了。在这里,您刚刚发送了所有数据。对方应该什么时候挂断?
  • @fredrik - 在我的示例中,我愚蠢地将错误的套接字设置为非阻塞,谢谢
  • @n.m. - 当你这样说时,这是完全合乎逻辑的。没有意识到这一点,我感到有点愚蠢。

标签: c++ linux sockets


【解决方案1】:

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

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

如果您的程序,在接收端,知道 - 由于知道它预期接收什么数据 - 它现在已经接收到它需要接收的所有内容,那么它可以关闭它的结束socket,然后继续手头的下一个任务。

您必须在程序的逻辑中加入一种方法,以某种形式或方式确定所有数据都已传输。其确切性质将由您来定义。也许,在发送套接字上的所有数据之前,您的发送程序会提前在同一个套接字上发送将在数据中跟随的字节数。然后,你的接收程序先读取字节数,再读取数据本身,然后就知道它已经接收到了所有内容,可以继续前进了。

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

【讨论】:

  • 感谢您提供的信息,我不得不承认根本不了解套接字的内部工作原理。当它被拼写出来时,这一切都是合乎逻辑的
【解决方案2】:

您可以在 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;
}

【讨论】:

    猜你喜欢
    • 2012-11-23
    • 1970-01-01
    • 1970-01-01
    • 2013-04-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-17
    • 1970-01-01
    相关资源
    最近更新 更多