如果 send() returns x 字节,recv() 是否在一次调用中获得相同数量的字节?

if send() returns x bytes, does recv() get the same amount of bytes in one call?

我知道需要循环调用 send() 直到发送了所需的字节数。同样在接收端。

这是我写的 recv 包装器的一部分:

do{
            result = socket->receiveData(m_recvBuf + recv_len , BUF_LENGTH - recv_len);
            recv_len += result;
.....

我对一些事情有点困惑,所以这里是:

  1. 如果 send() returns 10 个字节,那十个字节是否仍然只在发送方,但已准备好发送。还是字节已实际到达接收方计算机?

  2. 如果上面的答案是肯定的,那么调用 recv() 是否总是 return 那 10 个字节,因为它们已经到达了?

  3. 我也可以这么说;如果发送被调用三次,每次 returning 10,那么发送的总字节应该是 30。然后调用 recv(),一次,return 30 字节?

问题1.编辑为"still only at the receiver side"应该是"still only at the sender side".

场景: 我在 pc1 中的程序调用 send(); 发送() returns 1; 我的代码已经将一个字节发送到 pc2 中的接收程序。 在我的 send() 函数 returned 1.

之后网线被狗吃掉了

如果在现实生活中是这样,我肯定误解了 TCP 与 UDP 的好处。

感谢大家花时间回答。

我会试试:

  1. 你不知道。所知道的只是网络堆栈已经接受了数据,并将尽最大努力传输它。
  2. N/A
  3. 不,没有这样的保证。 TCP/IP 是面向流的,它是两个字节流,没有进一步的结构(消息、数据包、写入、帧等)。您必须确保您的数据序列化格式支持查找消息边界,以便接收方可以解释数据。

if send() returns x bytes, does recv() get the same amount of bytes in one call?

一般来说,肯定没有!!

例如,对于在 TCP 内部使用的 TCP/IP sockets (see tcp(7) & socket(7)) going through wifi routers and/or intercontinental routers, packets could be fragmented and/or reassembled. So a given send can correspond to several recv and vice versa, and the "boundaries" of messages are not respected. Hence, for applications, TCP is a stream of bytes without any message boundaries. Read also about sliding window protocol and TCP congestiion control

在实践中,您可能会观察到,例如在同一以太网电缆上的两台计算机之间,数据包不会被分割或重新组合。但是你应该用那个假设编写代码。

具体来说,像HTTP or SMTP or JSONRPC or X11 protocols这样的应用层协议应该设计为定义消息边界,服务器端和客户端都应该进行缓冲

您需要使用 poll(2), see this answer

if the send() returns 10 bytes, are those ten bytes still only at the receiver side, but ready to be sent.

定义 "being still at the reciever side" 的真正含义并不容易(因为您并不真正关心内核内部、网络控制器内部或洲际电缆上发生的事情)。所以上面这句话是没有意义的。

您的应用程序代码应该只关心 system calls (listed in syscalls(2)...) like poll(2), send(2) and related, write(2), recv(2) and related, read(2), socket(2), accept(2), connect(2), bind(2) 等...

您可能想使用像 0mq 这样的消息传递库。

The network cable got eaten by a dog the moment after my send() function returned 1.

你为什么那么在意这种场景。您的狗也可能掉落了您的笔记本电脑,或者在上面撒尿。一旦 send 告诉您的应用程序已发出十个字节,您应该信任您的内核。但是接收程序可能还没有得到这些字节(在另一个大陆,你需要等待几十毫秒,这对计算机来说是一个相当大的延迟)。很有可能,当您的狗咬了您的以太网电缆时,这十个字节就在海洋中间(您可以 合理地 编码,就好像它们已被发出一样)。 TCP 协议将检测到 link 已被中断,但该错误会在很久以后才出现在您的程序中(可能是 next 调用 send十秒后发生)。

(TCP定义中有一些较大的宏观延迟,可能有128秒那么大-我忘记了细节-这些延迟对于星际通信来说太小了;所以TCP不能习惯火星)

你应该(大部分时间)简单地在系统调用级别进行推理。

(当然,在某些情况下——想想远程神经外科机器人——这可能还不够)

I surely have misunderstood the benefits of TCP vs UDP.

如果您只使用 UDP,给定的数据包可能会被分割、丢失或收到 几次 次。使用 TCP,这不可能合理地发生(至少当 下一个 数据包已成功发送和接收时)。

我的座右铭:"if in doubt, try it out"。

这是一个完整的程序,它演示了在我的机器上,一百万字节的整个数据包甚至在没有被缓冲到单独的读取(和可能的写入,因为我使用了复合函数 asio::write() :

#include <thread>
#include <mutex>
#include <condition_variable>
#include <boost/asio.hpp>
#include <boost/system/error_code.hpp>
#include <vector>
#include <iostream>

namespace asio
{
    using namespace boost::asio;
    using boost::system::error_code;
}

using protocol = asio::ip::tcp;

struct event
{
    std::mutex mutex;
    std::condition_variable cv;
    bool notified = false;

    void notify()
    {
        auto lock = std::unique_lock<std::mutex>(mutex);
        notified = true;
        lock.unlock();
        cv.notify_all();
    }

    void wait()
    {
        auto lock = std::unique_lock<std::mutex>(mutex);
        cv.wait(lock, [this] { return this->notified; });
    }
};

struct emitter
{
    emitter(std::ostream& os) : os(os) {}

    template<class...Ts>
    void operator()(Ts&&...ts)
    {
        auto lock = std::unique_lock<std::mutex>(m);
        auto emit_item = [&os = this->os](auto&& x)
        {
            os << x;
        };
        using expand = int[];
        void(expand { 0,
            (emit_item(ts),0)...
        });
        os << std::endl;
    }

    std::ostream& os;
    std::mutex m;
};

event rx_ready;
emitter stdout_emit { std::cout };

void sender()
{
    asio::io_service executor;

    auto big_buffer = std::vector<char>(1000000, 'a');
    protocol::socket sock(executor);
    rx_ready.wait();
    asio::error_code ec;
    if(sock.connect(protocol::endpoint(asio::ip::address_v4(0x7f000001), 12345)), ec) {
        stdout_emit("connect failure: ", ec.message());
        return;
    }
    auto written = asio::write(sock, asio::buffer(big_buffer), ec);
    stdout_emit("wrote: ", written);
    if (ec) {
        stdout_emit("write failure: ", ec.message());
    }
    sock.shutdown(protocol::socket::shutdown_send, ec);
    if (ec) {
        stdout_emit("shutdown failure: ", ec.message());
    }
    sock.close(ec);
    if (ec) {
        stdout_emit("close failure: ", ec.message());
    }
}

void start_receiving(protocol::socket& s)
{
    auto huge_buffer_ptr = std::make_shared<std::vector<char>>(1000000);

    s.async_read_some(asio::buffer(*huge_buffer_ptr), [huge_buffer_ptr, &s](asio::error_code ec, std::size_t size)
    {
        stdout_emit("read ", size, " bytes");
        if (ec)
        {
            stdout_emit("read error: ", ec.message());
        }
        else
        {
            start_receiving(s);
        }
    });
}

void receiver()
{
    asio::io_service executor;
    protocol::acceptor acceptor(executor);
    auto ep = protocol::endpoint(protocol::v4(), 12345);
    acceptor.open(ep.protocol());
    acceptor.bind(ep);
    acceptor.listen();

    protocol::socket s(executor);
    acceptor.async_accept(s, [&](asio::error_code ec){
        if (ec) {
            stdout_emit("accept: ", ec.message());
        }
        else
        {
            start_receiving(s);
        }
    });
    rx_ready.notify();
    executor.run();
}

int main()
{
    auto t = std::thread(receiver);

    sender();

    t.join();
}

示例结果:

read 393216 bytes
wrote: 1000000
read 606784 bytes
read 0 bytes
read error: End of file

Process finished with exit code 0

将读写缓冲区更改为 10,000,000 字节给了我这个:

read 393216 bytes
read 638820 bytes
read 639028 bytes
read 639028 bytes
read 639028 bytes
read 638820 bytes
read 639028 bytes
read 639028 bytes
read 639028 bytes
read 638820 bytes
read 639028 bytes
read 639028 bytes
read 639028 bytes
read 638820 bytes
wrote: 10000000
read 639028 bytes
read 639028 bytes
read 22196 bytes
read 0 bytes
read error: End of file

Process finished with exit code 0

if the send() returns 10 bytes, are those ten bytes still only at the sender side, but ready to be sent. Or have the bytes physically arrived to the receiver computer?

您无法确定这 10 个字节的确切位置,它们中的一些可能在发送机器的某处等待,一些通过某些线路等待,一些在接收机器的某个地方等待。

If the answer to the above is yes, does then calling recv() always return those 10 bytes as they have already arrived?

N/A

I could also put it this way; if send has been called three times each time returning 10, so total bytes sent supposedly being 30. Does then calling recv(), one time, return 30 bytes?

你看不出来!您唯一可以说的是,在 TCP 模式下,接收字节的顺序与发送字节的顺序相同。

Scenario: My program in pc1 calls send(); send() returns 1; My code things that one byte has been sent to the receiver program in pc2. The network cable got eaten by a dog the moment after my send() function returned 1.

那你就不能说什么了...

如果在现实生活中是这样,我肯定误解了 TCP 与 UDP 的好处。

UDP 是面向数据报的语义,就像邮政系统(没有保留顺序,没有任何类型的保证,可能丢失,重复等)

TCP 是面向流的语义,如 phone 系统(保留顺序且无丢失)。

当然在网络硬故障的情况下,TCP也不能保证什么!

由于 TCP 是建立在 IP(数据报)之上的,因此通过 TCP 发送的内容会被分段以通过 IP 发送,并且您无法控制此类分段(我忘了说明缓存等)。