Search code examples
javagotcp

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:

e.g.

  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.


Solution

  • 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))
            }()
        }
    }