2015-06-12 105 views
3

我想通过套接字从C++应用程序发送一个protobuf到Java应用程序。 我在μC++程序上使用一个简单的套接字发送protobuf。在我通过网络发送它之前,我已将它序列化为一个char缓冲区。 在我的Java(服务器)程序上,我使用ServerSocket接收数据。从C++发送protobuf到Java

我有麻烦反序列化Java端protobuf。它不断给我错误:

  1. 在解析协议消息时,输入意外地在字段中间结束。这可能意味着要么输入被截断,要么嵌入的消息误报自己的长度。
  2. CodedInputStream遇到格式错误的varint。

我在做什么错?我的代码如下。

的protobuf的例子是从谷歌的教程采取 - AddressBook.proto教程

C++代码:

#define WIN32_LEAN_AND_MEAN 
#include <iostream> 
#include <fstream> 
#include <string> 
#include <windows.h> 
#include <winsock2.h> 
#include <ws2tcpip.h> 
#include<conio.h> 
#include "addressbook.pb.h" 

#pragma comment (lib, "Ws2_32.lib") 
#pragma comment (lib, "Mswsock.lib") 
#pragma comment (lib, "AdvApi32.lib") 

using namespace std; 

// This function fills in a Person message based on user input. 
void PromptForAddress(tutorial::Person* person) { 
    cout << "Enter person ID number: "; 
    int id; 
    cin >> id; 
    person->set_id(id); 
    cin.ignore(256, '\n'); 

    cout << "Enter name: "; 
    getline(cin, *person->mutable_name()); 

    cout << "Enter email address (blank for none): "; 
    string email; 
    getline(cin, email); 
    if (!email.empty()) { 
     person->set_email(email); 
    } 

    while (true) { 
     cout << "Enter a phone number (or leave blank to finish): "; 
     string number; 
     getline(cin, number); 
     if (number.empty()) { 
      break; 
     } 

     tutorial::Person::PhoneNumber* phone_number = person->add_phone(); 
     phone_number->set_number(number); 

     cout << "Is this a mobile, home, or work phone? "; 
     string type; 
     getline(cin, type); 
     if (type == "mobile") { 
      phone_number->set_type(tutorial::Person::MOBILE); 
     } 
     else if (type == "home") { 
      phone_number->set_type(tutorial::Person::HOME); 
     } 
     else if (type == "work") { 
      phone_number->set_type(tutorial::Person::WORK); 
     } 
     else { 
      cout << "Unknown phone type. Using default." << endl; 
     } 
    } 
} 

// Main function: Reads the entire address book from a file, 
// adds one person based on user input, then writes it back out to the same 
// file. 
int main(int argc, char* argv[]) { 
    // Verify that the version of the library that we linked against is 
    // compatible with the version of the headers we compiled against. 
    GOOGLE_PROTOBUF_VERIFY_VERSION; 

    tutorial::AddressBook address_book; 


    // Add an address. 
    PromptForAddress(address_book.add_person()); 

    { 
     int size = address_book.ByteSize(); 
     char * buffer = new char[size]; 
     address_book.SerializeToArray(buffer, size); 

     WSADATA wsaData; 
     SOCKET ConnectSocket = INVALID_SOCKET; 
     struct addrinfo *result = NULL, 
       *ptr = NULL, 
       hints; 
     int iResult; 

     // Initialize Winsock 
     iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); 

     ZeroMemory(&hints, sizeof(hints)); 
     hints.ai_family = AF_UNSPEC; 
     hints.ai_socktype = SOCK_STREAM; 
     hints.ai_protocol = IPPROTO_TCP; 

     // Resolve the server address and port 
     iResult = getaddrinfo("localhost", "5000", &hints, &result); 
     if (iResult != 0) { 
      printf("getaddrinfo failed with error: %d\n", iResult); 
      WSACleanup(); 
      return 1; 
     } 

     // Attempt to connect to an address until one succeeds 
     for (ptr = result; ptr != NULL; ptr = ptr->ai_next) 
     { 

      // Create a SOCKET for connecting to server 
      ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, 
        ptr->ai_protocol); 

      // Connect to server. 
      iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen); 
      if (iResult == SOCKET_ERROR) { 
       closesocket(ConnectSocket); 
       ConnectSocket = INVALID_SOCKET; 
       continue; 
      } 
      freeaddrinfo(result); 

      // Send an initial buffer 
      iResult = send(ConnectSocket, buffer, (int)strlen(buffer), 0); 
      if (iResult == SOCKET_ERROR) { 
       printf("send failed with error: %d\n", WSAGetLastError()); 
       closesocket(ConnectSocket); 
       WSACleanup(); 
       return 1; 
      } 
      printf("Bytes Sent: %ld\n", iResult); 

      _getch(); 
      // Optional: Delete all global objects allocated by libprotobuf. 
      google::protobuf::ShutdownProtobufLibrary(); 

      return 0; 
     } 
    } 
} 

Java程序:

package networkmonitor; 

import com.example.tutorial.AddressBookProtos.AddressBook; 
import com.example.tutorial.AddressBookProtos.Person; 
import com.google.protobuf.CodedInputStream; 
import com.google.protobuf.Parser; 
import java.io.IOException; 
import java.io.InputStream; 
import static java.lang.System.in; 
import java.net.ServerSocket; 
import java.net.Socket; 

class NetworkMonitor { 
    // Iterates though all people in the AddressBook and prints info about them. 
    static void Print(AddressBook addressBook) { 
     for (Person person: addressBook.getPersonList()) { 
      System.out.println("Person ID: " + person.getId()); 
      System.out.println(" Name: " + person.getName()); 
      if (person.hasEmail()) { 
       System.out.println(" E-mail address: " + person.getEmail()); 
      } 

      for (Person.PhoneNumber phoneNumber : person.getPhoneList()) { 
       switch (phoneNumber.getType()) { 
       case MOBILE: 
        System.out.print(" Mobile phone #: "); 
        break; 
       case HOME: 
        System.out.print(" Home phone #: "); 
        break; 
       case WORK: 
        System.out.print(" Work phone #: "); 
        break; 
       } 
       System.out.println(phoneNumber.getNumber()); 
      } 
     } 
    } 

    // Main function: Reads the entire address book from a file and prints all 
    // the information inside. 
    public static void main(String[] args) throws Exception { 

     ServerSocket server = null; 
     try 
     { 
      server = new ServerSocket(5000); 
     } 
     catch (IOException e) 
     { 
      System.out.println("Error on port: 5000 " + ", " + e); 
      System.exit(1); 
     } 

     System.out.println("Server setup and waiting for client connection ..."); 

     Socket client = null; 
     try 
     { 
      client = server.accept(); 
     } 
     catch (IOException e) 
     { 
      System.out.println("Did not accept connection: " + e); 
      System.exit(1); 
     } 

     System.out.println("Client connection accepted. Moving to local port  ..."); 

     try 
     { 
      InputStream inStream = client.getInputStream(); 
      AddressBook addressBook = AddressBook.parseDelimitedFrom(inStream); 
      Print(addressBook); 
      in.close(); 
      client.close(); 
      server.close(); 
     } 
     catch(IOException e) 
     { System.out.println("IO Error in streams " + e); 
     e.printStackTrace();} 
    } 
} 
+0

只有使用下面的解决方案才能解决您的问题吗?因为我试图重现您的实现,但它不适合我,客户端不断得到“InvalidProtocolBufferException:解析协议消息时,输入在字段中间意外结束”,使用protobuf3库... – Hayra

回答

3

确定。我阅读文档。

int size = address_book.ByteSize(); 
char * buffer = new char[size]; 
address_book.SerializeToArray(buffer, size); 

生成消息的完整大小与消息的大小。该消息不是一个字符串。无论如何,尽可能少地传递信息都是一团糟。

iResult = send(ConnectSocket, buffer, (int)strlen(buffer), 0); 

将发送消息可达嵌入在缓冲区或缓冲区后,如果缓存中不包含任何空的第一个空。你很可能会发送太多或太少。

幸运的是,您已经知道邮件的大小:size

iResult = send(ConnectSocket, buffer, size, 0); 

应该这样做。

+0

工作!非常感谢 ! – chrisrhyno2003