Java客户端抛出SocketException:从C服务器接收数据时重置连接。为什么呢?

时间:2018-06-07 09:52:32

标签: java c sockets java-io

我有一个用C编写的TCP / IP服务器程序和一个用Java编写的相应客户端:

C服务器

#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

Screenshot 1

但是,如果有一些较长的字符串,客户端会在收到服务器的响应后立即抛出SocketException: Connection reset

在这里,我正在尝试将 hello从客户端发送到服务器。这就是:

Screenshot 2

值得注意的是,服务器显然没有收到完整的消息,因为它只打印 hello from

错误

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)

传输似乎与“hello”,“123456”,“foo”,“bar”,“g g”等短字符串一起使用。

一些不起作用的字符串是: “你好”(大写字母H),“你好客户”,“foobarfoobarfoobar”。

除此之外,我还没有想出一个模式。

什么导致客户端在传输这些较长的邮件时抛出异常?

2 个答案:

答案 0 :(得分:0)

您不确定‹���������������HTTP/1.1 200 OK Host: localhost:8000 Date: Thu, 07 Jun 2018 09:46:54 +0000 Connection: close Content-Type: application/javascript Content-Length: 32861 var mr=function(a,b,c){ ... // the rest of js code 是否会在单次调用中返回所有内容(除非您查看已读取的数据的内容或read()的返回值),很多时候它会返回部分数据,您必须再次read()来读取剩余数据

套接字通信的推荐方法是预先确定预期的数据,你可以决定使用固定长度数据并继续调用read()直到总字节数(加上read()调用的返回值,当大于零表示成功读取的字节数)读取等于您预定的固定长度,或者您可以决定期望新行指示数据结束并继续读取循环直到您收到read()

答案 1 :(得分:0)

“连接重置”有两个常见原因:

  1. 写入已被对等方关闭的连接。
  2. 关闭连接而不读取对等方发送的所有数据。
  3. 还有其他人,但这些是最常见的。两者都是应用程序协议错误。

    在你的情况下,线索是它适用于短消息但不适用于长消息,原因是虽然Java代码正在发送由行终止符分隔的消息,但是你只执行一个{{1}在C代码中,并假设这将读取整个消息。在Posix或TCP / IP RFC中无法保证这一点。

    如果您期望由行终止符分隔的消息,则必须继续阅读,直到获得它为止。这就是Java的read()方法所做的,而这正是您需要在C代码中模拟的。