简体   繁体   中英

Golang TCP Client Does Not Send `End of Stream` to Java TCP Server

I'm writing a simple Golang TCP Client that talks with a Java TCP Server.

I have successfully gotten my Golang Client to send a message to my Java Server.

However, my Java code is expecting an End of Stream (where inputStream.read() returns -1 ) to know that it's time to stop reading Client messages.

It looks like Golang does not send an End of Stream message via connection.Write() unless I Close() the connection first.

Below is my Java Server code:

package com.mycompany.app;

import com.google.gson.*;
import com.google.common.primitives.Bytes;
import java.io.*;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.net.ServerSocket;
import java.net.SocketException;

public class MyApp {
    public static void main(String[] args) {
        int port = 9000;

        // Start listening for messages.
        try (ServerSocket serverSocket = new ServerSocket(port)) {
            System.out.println("Listening on port " + port + "...");

            // Never stop listening for messages.
            while (true) {
                try {
                    // Accept a client connection.
                    Socket socket = serverSocket.accept();

                    // Read message from the client.
                    DataInputStream inputStream = new DataInputStream(socket.getInputStream());

                    List<Byte> list = new ArrayList<Byte>();
                    int input = inputStream.read();

                    while (input != -1) {
                        list.add((byte) input);
                        input = inputStream.read();
                    }

                    byte[] jsonBytes = Bytes.toArray(list);

                    if (jsonBytes.length > 0) {
                        String jsonString = new String(jsonBytes);

                        System.out.println("Received: " + jsonString);

                        // Unmarshal from JSON.
                        Person person = new Gson().fromJson(jsonString, Person.class);

                        System.out.println("Person: " + person.Name + " " + person.Age);
                    }
                } catch (EOFException e) {
                    // A client has disconnected. Do nothing.
                } catch (SocketException e) {
                    // A client connection has been terminated unexpectedly. Do nothing.
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class Person {
    String Name;
    int Age;
}

Below is my Golang Client code:

package main

import (
    "bufio"
    "encoding/json"
    "fmt"
    "net"
    "os"
    "strings"
)

type Person struct {
    Name string
    Age  int
}

func main() {
    reader := bufio.NewReader(os.Stdin)

    for {
        func() {
            // Dial the Java Server.
            conn, err := net.Dial("tcp", "localhost:9000")
            if err != nil {
                fmt.Println(err)
            }

            defer conn.Close()

            fmt.Print("Enter Name: ")
            strName, err := reader.ReadString('\n')
            strName = strings.TrimSpace(strName)

            var person Person
            person.Name = strName
            person.Age = 18

            jsonBytes, err := json.Marshal(person)
            if err != nil {
                fmt.Println(err)
                return
            }

            // Write our message to the connection.
            _, err = conn.Write(jsonBytes)
            if err != nil {
                fmt.Println(err)
                return
            }

            fmt.Println("Sent: " + string(jsonBytes))
        }()
    }
}

Any advice on how to tell Java that we're done writing messages from Golang? Or is there a better way to handle Java x Golang TCP Client-Server connections?

This is quite a problem because one of the basic scenarios for TCP connections is Send-and-Receive:

eg

  1. Client sends a message and waits for the result.

  2. Server processes the message and returns the result.

  3. Client does something with the result.

For starters, you probably want to make sure your client code reliably closes the socket. But that's not all you should do.

The client process should trigger a TCP FIN as a result of closing the socket. And the socket should get closed as a result of the client process cleanly exiting. Not sure why this isn't the case for you. Are you waiting long enough?

However, so that your server is not indefinitely hung, you need to guard for this condition. Consider the case where there's an active TCP connection between a client and your server. And while the server is awaiting data from the client, the power goes out where the client is at. Because there's no way for the client to send a FIN, the server connection isn't going to have any traffic. That TCP connection will stay hung forever unless....

You either set a keep-alive option or a timeout option on the server side.

Keep-alive: https://docs.oracle.com/javase/7/docs/api/java/net/SocketOptions.html#SO_KEEPALIVE

Timeout: https://docs.oracle.com/javase/7/docs/api/java/net/SocketOptions.html#SO_TIMEOUT

I finally got it to work following @nos' advice to call CloseWrite() instead of Close() .

Had to change my TCP Connection code a bit, but it works like a charm.

This solution is perfect for my purposes because I only plan to do 1 Send from a Client connection and possibly 1 Read before I Close it.

Many thanks to everyone who helped!

Below is my updated Golang Client code:

package main

import (
    "bufio"
    "encoding/json"
    "fmt"
    "net"
    "os"
    "strings"
)

// Person struct.
type Person struct {
    Name string
    Age  int
}

func main() {
    reader := bufio.NewReader(os.Stdin)

    for {
        func() {
            // Dial the Java Server.
            tcpAddr, err := net.ResolveTCPAddr("tcp", "localhost:9000")
            if err != nil {
                fmt.Println(err)
                return
            }

            conn, err := net.DialTCP("tcp", nil, tcpAddr)
            if err != nil {
                fmt.Println(err)
                return
            }

            defer conn.Close()

            fmt.Print("Enter Name: ")
            strName, err := reader.ReadString('\n')
            strName = strings.TrimSpace(strName)

            var person Person
            person.Name = strName
            person.Age = 18

            jsonBytes, err := json.Marshal(person)
            if err != nil {
                fmt.Println(err)
                return
            }

            // Write our message to the connection.
            _, err = conn.Write(jsonBytes)
            if err != nil {
                fmt.Println(err)
                return
            }

            // Tell the server that we're done writing.
            err = conn.CloseWrite()
            if err != nil {
                fmt.Println(err)
                return
            }

            // Read Message From Server code goes here.

            fmt.Println("Sent: " + string(jsonBytes))
        }()
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM