【问题标题】:How to receive a custom data type from socket read?如何从套接字读取接收自定义数据类型?
【发布时间】:2015-10-25 09:41:59
【问题描述】:

我想接收数据(自定义类型如 CMYType) 我有以下代码;

using namespace boost::asio;

streambuf receivedStreamBuffer;
streambuf::mutable_buffers_type bufs;
bufs = receivedStreamBuffer.prepare( sizeof(CMYType) );
std::size_t length = read( *m_pReciver->ConSocket(), 
                           buffer(bufs, sizeof(CMYType)),  
                           transfer_all(),
                           ec);

知道如何将 receivedStreamBuffer 转换为 CMYType 类型的对象吗?!

【问题讨论】:

  • 你有没有想过先序列化 CMYType ?或者您确定它是简单且标准的布局(又名 POD),以便可以无风险地以二进制形式传输?在后一种情况下,您确定套接字的两侧都使用相同的字节序和浮点编码吗?
  • 1) 我是 tcp/ip 编程新手。所以我不确定是否要序列化数据包 2)我确信双方都使用相同的数据类型。有人可以帮我弄清楚如何获取有用的数据并将其分配给对象 CMYType 吗?!
  • 您可能想获得一份“有效的 TCP/IP 编程”的副本。这是一本好书,涵盖了基础知识。大多数时候,您希望在应用程序之间交换某种形式的消息。我建议您查看一些更高级别的库来执行此操作。我更喜欢 zeroMQ,并建议阅读优秀指南:zguide.zeromq.org/page:all

标签: c++ sockets boost


【解决方案1】:

您的代码“可能”确实有效。但是,正如Christophe 所指出的,您需要考虑许多问题。

TCP/IP 编程

在您的评论中,您提到您是 TCP/IP 编程的新手。我建议你通读"Beej's guide to network programming"。这是一个简短的(大约 20 页)和体面的介绍,几乎涵盖了网络编程的所有方面,包括以下内容。值得花时间,而且是免费的。

字节序

这是网络通信的一般基本问题。如果您无法控制软件运行的机器,您绝对需要处理它。或者,如果您想要可移植的代码。 只有在您绝对确定通信的两台(所有)机器都具有相同架构时,您才可以跳过此步骤。

如果您需要了解更多问题,thesethreewebsites 是很好的起点。

序列化

序列化既是对问题的概括,也是可以解决上述问题的“灵丹妙药”(如果应用正确的话)。基本思想是将您的数据类型(结构)转换为“串行流”,可以通过任何通道(网络)传输并转换回您的类型。 Boost 有一个serialization library 可用于此目的。

【讨论】:

    【解决方案2】:

    请记住其他人提到的可移植性和 ABI 警告。

    如果,正如您在评论中所说,您确定您的客户端和服务器在这些方面兼容:

    对于 POD 数据

    只要你的类型满足 POD 要求,你可以使用buffer_cast:

    事实上,你可以使用buffer 函数将其隐式化:

    Live On Coliru

    #include <boost/asio.hpp>
    #include <iostream>
    #include <vector>
    #include <boost/thread.hpp>
    
    struct CMYType {
        float a,b,c;
    };
    
    static_assert(std::is_pod<CMYType>::value, "Not bitwise serializable");
    
    static std::ostream& operator<<(std::ostream& os, CMYType const& cmy) {
        return os << "[" << cmy.a << "," << cmy.b << "," << cmy.c << "]";
    }
    
    static boost::mutex mx;
    
    void server() {
        boost::system::error_code ec;
        using namespace boost::asio;
    
        io_service svc;
        ip::tcp::acceptor acc(svc, ip::tcp::endpoint{ {}, 6767 });
    
        {
            ip::tcp::socket sock(svc);
            acc.accept(sock, ec);
    
            CMYType data;
            std::size_t length = read(sock, buffer(&data, sizeof(data)), ec);
            boost::lock_guard<boost::mutex> lk(mx);
            std::cout << "length:" << length << " data: " << data << "\n";
        }
    
        {
            ip::tcp::socket sock(svc);
            acc.accept(sock, ec);
    
            std::vector<CMYType> data(10);
            std::size_t length = read(sock, buffer(data), ec);
    
            boost::lock_guard<boost::mutex> lk(mx);
            std::cout << "length:" << length << " data: { ";
    
            for(auto& cmy : data)
                std::cout << cmy << ", ";
    
            std::cout << " }\n";
        }
    }
    
    void client() {
        boost::system::error_code ec;
        using namespace boost::asio;
    
        io_service svc;
    
        {
            ip::tcp::socket sock(svc);
            sock.connect({ {}, 6767 }, ec);
    
            CMYType data { 1, 2, 3 };
    
            std::size_t length = write(sock, buffer(&data, sizeof(data)), ec);
            boost::lock_guard<boost::mutex> lk(mx);
            std::cout << "sent: " << length << " bytes\n";
        }
    
        {
            ip::tcp::socket sock(svc);
            sock.connect({ {}, 6767 }, ec);
    
            std::vector<CMYType> data { 
                { 1,  2,  3  }, { 4,  5,  6  }, { 7,  8,  9  }, { 10, 11, 12 }, { 13, 14, 15 },
                { 16, 17, 18 }, { 19, 20, 21 }, { 22, 23, 24 }, { 25, 26, 27 }, { 28, 29, 30 },
            };
            std::size_t length = write(sock, buffer(data), ec);
    
            boost::lock_guard<boost::mutex> lk(mx);
            std::cout << "sent: " << length << " bytes\n";
        }
    
    }
    
    int main() {
        boost::thread_group tg;
        tg.create_thread(server);
        tg.create_thread(client);
    
        tg.join_all();
    }
    

    打印

    sent: 12 bytes
    length:12 data: [1,2,3]
    sent: 120 bytes
    length:120 data: { [1,2,3], [4,5,6], [7,8,9], [10,11,12], [13,14,15], [16,17,18], [19,20,21], [22,23,24], [25,26,27], [28,29,30],  }
    

    对于非 POD 数据

    您可以使用例如Boost Serialization 对数据进行序列化

    【讨论】:

    • 当您将 POD 类型传输到具有其他字节序的机器时会发生什么?还是在 32/64 位之间?或者在可能产生不同布局的不同编译器之间?甚至在使用不同编译器设置编译的代码之间?
    • @Jens 你得到了实现定义的行为,还有什么?它已经被其他人和 cmets 解决了
    • 我可以通过一种可以通过网络传输 POD 类型的方式来阅读您的答案。我已经多次在代码中看到过这个假设,所以我想阻止那些没有明确说明这种方法的限制的答案。
    • @Jens 我现在更明确地说明了警告。我只是想在这里放大用于 asio 缓冲区处理的 API,大大简化了 OP 代码中的代码。
    • 扩展了 POD 示例以在 Live On Coliru 上拥有客户端和服务器
    猜你喜欢
    • 1970-01-01
    • 2011-09-15
    • 2014-04-08
    • 1970-01-01
    • 2014-06-07
    • 2023-03-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多