简体   繁体   English

Golang TCP客户端不会向Java TCP Server发送“流结束”

[英]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. 我正在编写一个与Java TCP Server通讯的简单Golang TCP Client。

I have successfully gotten my Golang Client to send a message to my Java Server. 我已成功获取Golang客户端,以将消息发送到Java服务器。

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. 但是,我的Java代码期望End of StreaminputStream.read()返回-1 )知道是时候停止读取Client消息了。

It looks like Golang does not send an End of Stream message via connection.Write() unless I Close() the connection first. 除非我先Close() connection.Write()否则Golang似乎不会通过connection.Write()发送End of Stream消息。

Below is my Java Server code: 以下是我的Java Server代码:

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: 以下是我的Golang客户代码:

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? 关于如何告诉Java我们已经完成了从Golang编写消息的任何建议? Or is there a better way to handle Java x Golang TCP Client-Server connections? 还是有更好的方法来处理Java x Golang TCP Client-Server连接?

This is quite a problem because one of the basic scenarios for TCP connections is Send-and-Receive: 这是一个很大的问题,因为TCP连接的基本方案之一是发送和接收:

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. 由于关闭套接字,客户端进程应触发TCP FIN。 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. 考虑客户端和服务器之间存在活动TCP连接的情况。 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. 因为客户端无法发送FIN,所以服务器连接不会有任何流量。 That TCP connection will stay hung forever unless.... 除非...,否则该TCP连接将永远挂起。

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 保持活动: 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 超时: 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() . 我终于按照@nos的建议调用CloseWrite()而不是Close()来使其工作。

Had to change my TCP Connection code a bit, but it works like a charm. 不得不更改我的TCP连接代码,但它的工作原理很吸引人。

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. 此解决方案非常适合我的目的,因为我仅打算执行1从客户端连接发送,并且可能在关闭之前进行1读取。

Many thanks to everyone who helped! 非常感谢大家的帮助!

Below is my updated Golang Client code: 以下是我更新的Golang客户端代码:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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