在 C++ 中通过 TCP 发送 Cap'n Proto 消息
Sending Cap'n Proto messages over TCP in C++
我对网络完全陌生,也刚开始使用 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
作为文件描述符,message
由 MallocMessageBuilder
创建。
我在 Windows 工作 Visual Studio 2017。
我想将 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()
return 一个文件描述符。
所以我只是用
writePackedMessageToFd(sock, message)
,但没有用。
顺便说一下,我不明白这个概念,因为套接字没有 "know" 我想使用哪个 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 连接发送和接收 Cap'n Proto 打包消息到指定的 IP:port。
我怎样才能做到这一点?我真的需要一些帮助。
非常感谢您的帮助!提前致谢!
在 Windows 上,socket()
没有 return 文件描述符。它 return 是 Windows SOCKET
,实际上是 HANDLE
转换为整数。在Windows上,"file descriptors"被实现为C运行时库中的兼容层; OS.
不直接支持它们
您可以使用 kj::HandleInputStream
和 kj::HandleOutputStream
在 Windows 上的套接字上执行 I/O。
kj::HandleOutputStream out((HANDLE)sock);
capnp::writeMessage(out, message);
并且:
kj::HandleInputStream in((HANDLE)sock);
capnp::InputStreamMessageReader message(in);
我对网络完全陌生,也刚开始使用 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
作为文件描述符,message
由 MallocMessageBuilder
创建。
我在 Windows 工作 Visual Studio 2017。
我想将 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()
return 一个文件描述符。
所以我只是用
writePackedMessageToFd(sock, message)
,但没有用。
顺便说一下,我不明白这个概念,因为套接字没有 "know" 我想使用哪个 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 连接发送和接收 Cap'n Proto 打包消息到指定的 IP:port。
我怎样才能做到这一点?我真的需要一些帮助。
非常感谢您的帮助!提前致谢!
在 Windows 上,socket()
没有 return 文件描述符。它 return 是 Windows SOCKET
,实际上是 HANDLE
转换为整数。在Windows上,"file descriptors"被实现为C运行时库中的兼容层; OS.
您可以使用 kj::HandleInputStream
和 kj::HandleOutputStream
在 Windows 上的套接字上执行 I/O。
kj::HandleOutputStream out((HANDLE)sock);
capnp::writeMessage(out, message);
并且:
kj::HandleInputStream in((HANDLE)sock);
capnp::InputStreamMessageReader message(in);