【问题标题】:Sending Cap'n Proto messages over TCP in C++在 C++ 中通过 TCP 发送 Cap'n Proto 消息
【发布时间】:2018-04-13 05:38:57
【问题描述】:

我对网络完全陌生,也刚开始使用Cap'n Proto

这是来自here的一些示例程序:

void writeAddressBook(int fd) {
  ::capnp::MallocMessageBuilder message;

  AddressBook::Builder addressBook = message.initRoot<AddressBook>();
  ::capnp::List<Person>::Builder people = addressBook.initPeople(2);

  Person::Builder alice = people[0];
  alice.setId(123);
  alice.setName("Alice");
  alice.setEmail("alice@example.com");
  // Type shown for explanation purposes; normally you'd use auto.
  ::capnp::List<Person::PhoneNumber>::Builder alicePhones =
      alice.initPhones(1);
  alicePhones[0].setNumber("555-1212");
  alicePhones[0].setType(Person::PhoneNumber::Type::MOBILE);
  alice.getEmployment().setSchool("MIT");

  Person::Builder bob = people[1];
  bob.setId(456);
  bob.setName("Bob");
  bob.setEmail("bob@example.com");
  auto bobPhones = bob.initPhones(2);
  bobPhones[0].setNumber("555-4567");
  bobPhones[0].setType(Person::PhoneNumber::Type::HOME);
  bobPhones[1].setNumber("555-7654");
  bobPhones[1].setType(Person::PhoneNumber::Type::WORK);
  bob.getEmployment().setUnemployed();

  writePackedMessageToFd(fd, message);
}

最后一行使用writePackedMessageToFd(),它将fd作为文件描述符,messageMallocMessageBuilder创建。

我使用 Visual Studio 2017 在 Windows 上工作。

我想将message 发送到远程服务器,该服务器将使用类似的 Cap'nP 对象进行应答。

问题是如何发送和接收答案?

我尝试通过以下方式初始化和创建套接字:

    //Initialize WinSock
    WSAData data;
    WORD ver = MAKEWORD(2, 2);
    int wsResult = WSAStartup(ver, &data);
    if (wsResult != 0) {
        cerr << "Can't start WinSock, Error #" << wsResult << endl;
        return;
    }
    else {
        cout << "Socket initialized!" << endl;
    }


    //Create socket
    SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock == INVALID_SOCKET) {
        cerr << "Can't create socket, Error #" << WSAGetLastError() << endl;
        WSACleanup();
        return;
    }
    else {
        cout << "Socket created!" << endl;
    }

如果一切顺利socket() 返回一个文件描述符。 所以我只是用 writePackedMessageToFd(sock, message),但没用。 顺便说一句,我不理解这个概念,因为套接字不“知道”我想使用哪个 IP 和端口。当我使用connect() 函数时,我应该指定它们。

我试图跳过writePackedMessageToFd() 函数。使用connect() 连接到服务器并使用Windows 的send() 函数发送message。像这样的:

    string ipAddress = "127.0.0.1";     //IP Address of server 
    int port = 58661;           //Listening port of server

    //Initialize WinSock
    WSAData data;
    WORD ver = MAKEWORD(2, 2);
    int wsResult = WSAStartup(ver, &data);
    if (wsResult != 0) {
        cerr << "Can't start WinSock, Error #" << wsResult << endl;
        return;
    }
    else {
        cout << "Socket initialized!" << endl;
    }


    //Create socket
    SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock == INVALID_SOCKET) {
        cerr << "Can't create socket, Error #" << WSAGetLastError() << endl;
        WSACleanup();
        return;
    }
    else {
        cout << "Socket created!" << endl;
    }

    //Fill in a hint structure
    sockaddr_in hint;
    hint.sin_family = AF_INET;
    hint.sin_port = htons(port);
    inet_pton(AF_INET, ipAddress.c_str(), &hint.sin_addr);

    //Connect to server
    int connResult = connect(sock, (sockaddr*)&hint, sizeof(hint));
    if (connResult == SOCKET_ERROR) {
        cerr << "Can't connect to server, Error #" << WSAGetLastError() << endl;
        closesocket(sock);
        WSACleanup();
        return;
    }
    else {
        cout << "Connected to " << ipAddress << " on port " << port << endl;
    }

    //Send and receive data
    char buf[4096];
    //string mess = "Hello";

    //Send message
    //int sendResult = send(sock, mess.c_str(), mess.size() + 1, 0);
    int sendResult = send(sock, (const char*)&message, sizeof(message) + 1, 0);


    //Wait for response
    ZeroMemory(buf, 4096);
    int bytesReceived = recv(sock, buf, 4096, 0);
    if (bytesReceived > 0) {
        cout << "SERVER>" << string(buf, 0, bytesReceived) << endl;
    }


    //Close down everything
    closesocket(sock);
    WSACleanup();
    cout << "Connection closed!" << endl;

这个发送了一些东西,但肯定是错误的,因为服务器没有响应。

简而言之:我想通过 TCP 连接向指定 IP:port 发送和接收 Cap'n Proto 打包消息。

我怎么能做到这一点?我真的需要一些帮助。

非常感谢您的帮助!提前致谢!

【问题讨论】:

  • 您是否尝试过使用 Cap'n Proto 中已内置的 RPC 逻辑?不要从头开始编写自己的。您展示的示例来自 Cap'n Proto 的 Serialization 文档。你看过它的RPC ProtocolC++ RPC 文档吗? "Cap'n Proto C++ RPC 层位于serialization layer 之上并实现RPC protocol"
  • 如果您真的想手动实现套接字 I/O,请查看RPC Protocol 文档底部的“规范”部分。
  • @RemyLebeau 有很多充分的理由希望在原始套接字上使用简单的 Cap'n Proto 序列化,而不是使用整个 RPC 和异步框架。这一切都取决于用例。有时 RPC 系统有点矫枉过正。

标签: windows sockets networking tcp capnproto


【解决方案1】:

在 Windows 上,socket() 不返回文件描述符。它返回一个 Windows SOCKET,它实际上是将 HANDLE 强制转换为整数。在 Windows 上,“文件描述符”作为 C 运行时库中的兼容层实现;操作系统不直接支持它们。

您可以使用 kj::HandleInputStreamkj::HandleOutputStream 在 Windows 上的套接字上执行 I/O。

kj::HandleOutputStream out((HANDLE)sock);
capnp::writeMessage(out, message);

还有:

kj::HandleInputStream in((HANDLE)sock);
capnp::InputStreamMessageReader message(in);

【讨论】:

  • 非常感谢!!我不确定如何告诉sock 套接字我想使用哪个IP:端口?我应该把袜子绑在他们身上吗? (对不起,我对这些东西真的很陌生:()如果使用我在上面的代码中初始化和创建的套接字,那么我在第 679 行的exception.c++ 中得到一个未处理的异常exception: kj::ExceptionImpl 。我错过了什么吗?谢谢!
  • 拜托,你能澄清我的问题吗?谢谢!
  • @Andariel 在服务器上,您需要使用 bind()、listen() 和 accept()。在客户端上,您需要使用 connect()。抱歉,如何使用这些是基本的网络,不是 Cap'n Proto 特定的,我只是来回答 Cap'n Proto 的问题。我建议发布一个关于此的新问题,但不要让 Cap'n Proto 参与其中,以便不了解 Cap'n Proto 的人能够轻松回答。或者,互联网上有 很多 指南。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-08-21
  • 1970-01-01
  • 1970-01-01
  • 2016-05-12
  • 1970-01-01
  • 1970-01-01
  • 2019-03-31
相关资源
最近更新 更多