Java-клиент генерирует SocketException: сброс соединения при получении данных от C-сервера.Зачем? - PullRequest
0 голосов
/ 07 июня 2018

У меня есть программа сервера TCP / IP, написанная на C, и соответствующий клиент, написанный на Java:

C Server

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdbool.h>


#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <string.h>
#include <arpa/inet.h>
#include <netdb.h>

#define PORT 0

int main(int argc, char *argv[]) {


    printf("ISV_APP: Initiating TCP/IP networking.\n");

    int server_fd, new_socket, valread;
    struct sockaddr_in address;
    int opt = 1;
    int addrlen = sizeof(address);
    char buffer[1024] = {0};



    // Creating socket file descriptor.
    if((server_fd = socket(AF_INET, SOCK_STREAM,0)) == 0)
    {
        printf("TCP/IP: Initiation of server socket file descriptor failed.\n" );
        return -1;
    }
    printf("TCP/IP: Initializing server socket.\n" );

    // Forcefully attaching socket to the connection port.
    if(setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt)))
    {
        printf("TCP/IP: Error with setsockopt call occured.\n" );
        return -1;
    }
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);
    printf("TCP/IP: Socket options have been set.\n" );

    // Binding the socket.
    if(bind(server_fd, (struct sockaddr *)&address, sizeof(address)) <0){
        printf("TCP/IP: Failed to bind socket.\n" );
        return -1;
    }
    printf("TCP/IP: Socket bound to port.\n" );

    // Listen for connections
    if(listen(server_fd, 3) < 0){
        printf("TCP/IP: Failed to listen for incoming connections.\n" );
        return -1;
    }

    // Print the assigned port and address so that client knows how to connect.
    struct sockaddr_in sin;
    socklen_t len = sizeof(sin);
    if (getsockname(server_fd, (struct sockaddr *)&sin, &len) == -1){
        printf("Failed to read port number.\n" );
    }
    else{
        // Print port
        unsigned int my_port = ntohs(sin.sin_port);
        char my_port_str[6]; 
        sprintf(my_port_str, "%d", my_port);
        printf("\nINFO: Socket bound to port: " );
        printf(my_port_str);
        printf("\n");

        // Print address
        char my_ip[16];
        inet_ntop(AF_INET, &sin.sin_addr, my_ip, sizeof(my_ip));
        printf("INFO: Server IP Address: ");
        printf(my_ip);

        printf("\n");
        printf("\nServer is listening for incoming connections...\n" );

    }

    // Accept connection
    if((new_socket = accept(server_fd, (struct sockaddr*)&address, (socklen_t*)&addrlen))<0){
        printf("TCP/IP: Failed to accept incoming connection.\n" );
        return -1;
    }
    printf("TCP/IP: Connection successfully established.\n" );
    printf("\n");
    fflush(stdout);

    // Connection successful. You can send and receive data now. 


    // Get message from client.
    printf("Waiting for message from client...\n");
    valread = read(new_socket, buffer, 1024);
    printf("FROM CLIENT: %s\n", buffer);

    // Pass message to client.
    printf("Sending message to client...\n");
    char *hello = "serversayshello";
    send(new_socket, hello, strlen(hello), 0);
    printf("Hello message sent\n");


    // Close file descriptors and end program.
    close(new_socket);
    close(server_fd);
    printf("End of program.");
    exit(EXIT_SUCCESS);
}

Java-клиент

import java.io.*;
import java.net.*;

public class MobileClientPrototype {

    public static void main(String[] args) throws IOException{

        // Used to get user input. 
        BufferedReader inFromUser = new BufferedReader(new InputStreamReader(System.in));

        System.out.print("Please enter IP Address:");
        String host = inFromUser.readLine();

        System.out.print("Please enter port number:");
        int port = Integer.parseInt(inFromUser.readLine());

        // Initialize Socket
        Socket clientSocket = new Socket(host, port);

        // Streams for communication with server
        DataOutputStream outToServer = new DataOutputStream(clientSocket.getOutputStream());
        BufferedReader inFromServer = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));

        // Ask user for string message which will be sent to server
        String sentence;
        System.out.print("\nPlease enter message:\n");
        sentence = inFromUser.readLine();

        // Send message to server
        outToServer.writeBytes(sentence + '\n');
        System.out.print("Message sent!\n\n");

        // Wait for response message from server        
        String responseSentence;
        System.out.print("Awaiting response...\n");
        responseSentence = inFromServer.readLine();
        // Print response
        System.out.println("RESPONSE FROM SERVER:\n" + responseSentence);

        // Close socket and end program
        System.out.print("\nTerminating connection...");
        clientSocket.close();
        System.out.print("\nEnd of program.");
    }
}

Сценарий

Я запускаю серверные и клиентские программы на локальном компьютере.С клиентом я ввожу 127.0.0.1 в качестве IP-адреса и номера порта, напечатанного сервером.Когда соединение установлено, вы можете ввести сообщение с клиентом, которое будет отправлено на сервер.После этого сервер отвечает serversayshello .

Он отлично работает с некоторыми короткими строками, например, я могу передать hello :

Снимок экрана 1

Однако с некоторыми более длинными строками клиент выбрасывает SocketException: Connection reset, как только получает ответ от сервера.

Здесь я пытаюсь отправить привет от клиента на сервер.Вот что происходит:

Снимок экрана 2

Стоит также отметить, что сервер, по-видимому, не получает полное сообщение, так как он только напечатал привет от.

Ошибка

Exception in thread "main" java.net.SocketException: Connection reset
    at java.net.SocketInputStream.read(SocketInputStream.java:210)
    at java.net.SocketInputStream.read(SocketInputStream.java:141)
    at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
    at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
    at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
    at java.io.InputStreamReader.read(InputStreamReader.java:184)
    at java.io.BufferedReader.fill(BufferedReader.java:161)
    at java.io.BufferedReader.readLine(BufferedReader.java:324)
    at java.io.BufferedReader.readLine(BufferedReader.java:389)
    at MobileClientPrototype.main(MobileClientPrototype.java:38)

Кажется, что передача работает с короткими строками, такими как "привет", "123456", "foo", "bar", "GG" и так далее.

Некоторые строки, которые не работают: «Hello» (с заглавной буквы H), «hello from client», «foobarfoobarfoobar».

Кроме этого, я не понялшаблон пока нет.

Что заставляет клиента выдавать исключение, когда он передает эти более длинные сообщения?

Ответы [ 2 ]

0 голосов
/ 07 июня 2018

Существует две распространенные причины «сброса соединения»:

  1. Запись в соединение, которое уже было закрыто узлом.
  2. Закрытие соединения без чтения всех данныхкоторый был послан сверстником.

Есть и другие, но они наиболее распространены.Оба являются ошибками протокола приложения.

В вашем случае подсказка в том, что он работает для коротких сообщений, но не для длинных сообщений, и причина в том, что, хотя код Java отправляет сообщение, разделенное разделителем строки, вывыполняем только один read() в коде C и предполагаем, что это прочитает все сообщение.Это не гарантируется нигде в Posix или в RFC TCP / IP.

Если вы ожидаете сообщение, ограниченное ограничителем строки, вам придется продолжать чтение, пока вы его не получите.Это то, что делает Java readLine() метод, и это то, что вам нужно эмулировать в вашем коде C.

0 голосов
/ 07 июня 2018

Вы не будете знать наверняка, если read() возвращает все в одном вызове (если вы не посмотрите на содержимое данных, которые читаются, или на возвращаемое значение read()), много раз он вернет частичные данные, и вы будетепридется read() снова, чтобы прочитать оставшиеся данные

Рекомендуемый способ связи через сокеты - это заранее решить, какие данные ожидать, вы можете решить иметь данные фиксированной длины и продолжать вызывать read() до общего количества байтов (сложите возвращаемое значение из вызова read (), когда больше нуля указывает количество успешно прочитанных байтов) read равен фиксированной длине, которую вы определили, или вы можете решить, что новая строка будет указывать конец данных и продолжать чтение в цикле доВы получаете '\n'

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...