【问题标题】:Re-use socket multiple times for sending and receiving strings/bytes between C++ and Java多次重复使用套接字在 C++ 和 Java 之间发送和接收字符串/字节
【发布时间】:2018-03-17 00:21:18
【问题描述】:

我正在尝试重用相同的套接字在 C++ 客户端和 Java 服务器之间发送和接收字符串(和/或字节)(反之亦然)。从某种意义上说,我可以重用套接字,因为我可以从 Java 发送(一次)并从 C++ 接收(一次),并且反过来做同样的事情(从 C++ 发送到 Java)。但是,如果我多次尝试从 C++ 发送到 Java,第二个(字符串或字节)将不会出现。对于字符串(从 C++ 发送一个字符数组),我总是得到一个空值。请注意,在使用相同代码在 C++ 和 C++ 或 Java 和 Java 之间进行通信时,我没有这个问题。我知道 BufferedInputReader 需要一个新行。对于字节,我得到 IndexOutOfBound。请参见下面的代码。 Java服务器-->

 // in main...
    TCP tcp = new TCP();
    tcp.sendStr("Send File");
    String x = tcp.recvStr();
    System.out.println(x);
    x = tcp.recvStr();
    System.out.println(x);
    //issue starts from here
    x = tcp.recvStr();
    System.out.println(x);

 //.... IN TCP CLASS
 // Global class variables in TCP
 ServerSocket serverSocket = new ServerSocket(8080); 
 serverSocket.accept(); // this is done somewhere in the class
 BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
 PrintWriter pw = new PrintWriter(socket.getOutputStream(), true);

    public String recvStr(){

    String msg = null;

    try {
        msg = br.readLine(); 
    }


    catch (Exception e) {
        e.printStackTrace();
    }

    return msg;
   }

public void sendStr (String msg) {

    try {

        pw.write(msg);
        pw.flush();   
    }

    catch (Exception e) {
        e.printStackTrace();
    }
}

C++ 客户端代码

 //.. main thread of execution
 struct sockaddr_in server_address, client_addr;
 memset(&server_address, '0', sizeof(server_address));
 server_address.sin_family = AF_INET;
 server_address.sin_addr.s_addr = INADDR_ANY;
 server_address.sin_port = htons(PORT);
 TCP tcp;
 char receiveBuffer[1024] = {};

 int server_fd = 0,  new_socket = 0;

tcp.RecieveChar(server_address, receiveBuffer, new_socket);

std::cout << receiveBuffer << std::endl;

char chunkSize[64] = "yeahh!\n";

tcp.SendChar(server_address, chunkSize, new_socket, server_fd);

char chunk[128] = "weeeee\n";

tcp.SendChar(server_address, chunk, new_socket, server_fd);

//... in class tcp

template<int SIZE>
void TCP::SendChar(struct sockaddr_in server_addr, const char (&data)[SIZE], int &socket_id, bool close_connection ){

    int opt = 1;
    int addrlen = sizeof(server_addr);
    int server_accept;

    // Creating socket file descriptor
    // Create one if only one does not exist
    if(socket_id == 0){
        if ((socket_id = socket(AF_INET, SOCK_STREAM, 0)) == 0)
        {
            std::cerr << "Error in creating socket" << std::endl;
            exit(EXIT_FAILURE);
        }
        // Forcefully attaching socket to the port 8080
        if (setsockopt(socket_id, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT,
                                                      &opt, sizeof(opt)))
        {
            std::cerr << "Error in setting socket" << std::endl;
            exit(EXIT_FAILURE);
        }
        // Forcefully attaching socket to the port
        if (bind(socket_id, (struct sockaddr *)&server_addr,
                                     sizeof(server_addr))<0)
        {
            std::cerr << "Error in binding socket" << std::endl;
            exit(EXIT_FAILURE);
        }
        if (listen(socket_id, 3) < 0)
        {
            std::cerr << "Error in accepting listening" << std::endl;
            exit(EXIT_FAILURE);
        }
        if ((server_accept = accept(socket_id, (struct sockaddr *)&server_addr,
                                        (socklen_t*)&addrlen))<0)
        {
            std::cerr << "Error in accepting connection" << std::endl;
            exit(EXIT_FAILURE);
        }
        socket_id = server_accept;
    }


    sendto(socket_id, data, SIZE, 0, (struct sockaddr*) &server_addr, sizeof(server_addr)); // need to change to client address?

}

void TCP::RecieveChar(struct sockaddr_in server_addr, char *received_data,
    int &socket_address_binder, bool close_connection){
    int valread;

    if(socket_address_binder == 0){
        if ((socket_address_binder = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        {
            std::cerr << "\n Socket creation error" << std::endl;
        }

        if (connect(socket_address_binder, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
        {
            std::cerr << "\nConnection Failed \n" << std::endl;
        }
    }

    valread = read( socket_address_binder , received_data, 1024);

    if(valread == 0){
        std::cerr << "Error in read" << std::endl;
    }
    std::cout << "Received: " << received_data << std::endl;
}

请注意,我已包含用于发送和接收字符/字符串数组的代码。我也可以毫无问题地在两者之间发送文件,但是当我在 Java 中接收到字符串后尝试重用套接字时,出现以下错误:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException
at java.lang.System.arraycopy(Native Method)
at java.io.BufferedOutputStream.write(BufferedOutputStream.java:128)

【问题讨论】:

  • 你在使用多线程吗?如果您将sendStr 设为synchronized 方法,问题会消失吗?
  • 不使用多线程,一个线程执行
  • @VGR :看我的回答,我知道问题出在哪里,但是您仍然可以帮助理解原因。

标签: java c++ sockets bufferedreader


【解决方案1】:

我找到了导致问题的原因,但是我不知道为什么会导致问题。问题是因为我使用的是 C char 数组,而 char 数组在数组末尾附加了一个空字符。一旦 BufferedReader 从流中接收到 Null,它只会将其他所有内容视为 null。我不知道它为什么会这样。我找到了一个解决方法,它不会一次读取一个字符,一旦我看到空字符(即 0 ASCII),我就停下来。 recvStr() 方法中的 try 子句可以替换为:

    try {
        int a = -1;
            while(true){
                a = br.read();
                if(a == 0)
                    break;
                message = message + (char)a;
            }

    }

【讨论】:

  • 不是这样。当BufferedReader 返回 -1,表示流结束时,它会当然继续这样做。它的行为与您在空字节上描述的不同。而且你不应该首先传输尾随空值。
  • 另外我没有传输空字节,因为我想,发送一个 C 字符数组包含一个空结尾。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-13
  • 2011-07-11
  • 2013-04-01
  • 1970-01-01
相关资源
最近更新 更多