如果send()返回x字节,则recv()在一个呼叫中获取相同数量的字节

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

本文关键字:字节 获取 呼叫 一个 返回 send 如果 recv      更新时间:2023-10-16

我知道需要在循环中调用send(),直到发送了所需的字节量为止。也在接收方。

这是我写过的Recv周围包装器的一部分:

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

我对几件事有点困惑,所以这里有:

  1. 如果send()返回10字节,则只有十个字节仍然只在发件人侧,但可以发送。还是字节物理到达接收器计算机?

  2. 如果上述答案是肯定的,那么请拨打recv()始终返回10个字节,因为它们已经到达?

  3. 我也可以这样说。如果每次返回10次发送三次,则据说发送的总字节为30。然后一次调用recv(),一次,返回30字节?

问题1.被编辑为"仍然仅在接收方的侧"应"仅在发件人侧"。

方案:我在pc1呼叫中的程序send();发送()返回1;我的代码已将一个字节发送到PC2中的接收器程序。我的send()功能返回1。

后,网络电缆被狗吃掉了。

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

感谢大家给予时间回答。

我会尝试:

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

如果send()返回x字节,recv()是否在一个呼叫中获得相同数量的字节?

一般而言,当然 no !!

例如,对于TCP/IP插座(请参阅通过WiFi路由器和/或洲际路由器,可以分散和/或重新组装数据包。因此,给定的send可以对应于几个recv,反之亦然,并且消息的"边界"不尊重。因此,对于应用程序,TCP是字节的A 而没有任何消息边界。还请阅读有关滑动窗口协议和TCP充血控制TCP内部使用的有关的。

实际上,您可能会观察到,例如在同一以太网电缆上的两台计算机之间,该数据包没有碎片或重新组装。但是您应该使用该假设不是代码。

具体而言,应设计诸如HTTP或SMTP或JSONRPC或X11协议之类的应用程序级别协议来定义消息边界,并且服务器和客户端端都应进行缓冲。

您需要使用民意调查(2),请参阅此答案。

如果send()返回10个字节,则只有十个字节仍位于接收器侧,但可以发送。

不容易定义"仍然在接收器一侧"的意思是什么意思(因为您并不真正在乎内核内发生的事情,网络控制器内部或洲际电缆)。因此,上述句子毫无意义。

您的应用程序代码只能关心系统调用(在Syscalls(2)中列出),例如Poll(2),Send(2)和相关,Write(2),Recv(2),Recv(2),相关,请阅读(2),插座(2),接受(2),连接(2),绑定(2)等...

您可能想使用诸如0MQ的消息库。

我的send()功能返回1。

后,网络电缆被狗吃掉了。

您为什么要那么关心这种情况。您的狗也可以丢下笔记本电脑,或者在上面放着小便。一旦send告诉您的申请比发出了十个字节,您应该信任您的内核。但是接收程序可能还没有得到这些字节(在另一个大陆上,您需要等数十个毫秒,这对于计算机来说是一个很大的延迟)。当您的狗咬住以太网电缆时,十个字节在海洋中间(并且您可以合理地代码,就好像它们已经发射一样)。TCP协议将检测到该链接已中断,但是该错误将在您的程序中稍后延迟(也许是 next 呼叫send的错误)。

(TCP定义中有一些巨大的宏观延迟,也许大约128秒 - 我忘记了细节 - 这些延迟太小了,无法用于星际通信;因此TCP不能用于火星)

您应该(大多数时候)仅在系统呼叫级别的原因。

(当然,在某些情况下 - 考虑到远程神经外科机器人 - 可能还不够)

我肯定误解了TCP与UDP的好处。

如果您刚刚使用了UDP,则给定的数据包可能会被碎片,丢失或接收到次。使用TCP,这是无法合理地发生的(至少在成功发送并接收到下一个数据包时)。

如果send()返回10个字节,则十个字节仍然仅在 发件人方,但准备发送。或物理上的字节 到达接收器计算机?

您无法分辨出10个字节的确切位置,其中一些可以在发送机器中的某个地方等待,有些是在接收机中等待的地方。

如果上述答案是肯定的,则确实会调用recv()总是 返回那10个字节,因为它们已经到达?

n/a

我也可以这样说。如果发送三次 返回10的时间,因此发送的总字节据说是30。 调用recv(),一次,返回30个字节?

您无法分辨!在TCP模式下,您唯一可以说的是,以与发送的顺序相同的顺序收到字节。

方案:我在pc1呼叫中的程序send();发送()返回1;我的代码 一个字节已发送到PC2中的接收器程序。这 我的send()功能后,网络电缆被狗吃掉了 返回1。

那么你什么也不能说...

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

udp是面向数据报的语义,例如邮政系统(无保留订单,不保证任何形式,损失是可能的,重复的等)

TCP是面向流的语义,例如电话系统(保留订单,无损失)。

当然,如果发生硬网络故障,TCP无法确保任何事情!

由于TCP是在IP(数据报)上建立的,因此通过IP发送了通过IP发送的片段,而您不控制这种碎片(我忘了告诉Caching等)。

)。 。

我的座右铭:"如果有疑问,请尝试一下"。

这是一个完整的程序,证明在我的机器上,整个100万个字节的包都没有通过回环适配器完成,而无需将其缓冲到单独的读取中(可能是写入的,因为我使用了复合函数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