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

时间:2018-12-10 08:05:41

标签: java go tcp

我正在编写一个与Java TCP Server通讯的简单Golang TCP Client。

我已经成功获得Golang客户端,可以向Java服务器发送消息。

但是,我的Java代码期望End of Stream(其中inputStream.read()返回-1)知道是时候停止阅读客户端消息了。

除非我首先End of Stream进行连接,否则Golang似乎不会通过connection.Write()发送Close()消息。

下面是我的Java服务器代码:

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;
}

下面是我的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))
        }()
    }
}

关于如何告诉Java我们已经完成了从Golang编写消息的任何建议?还是有更好的方法来处理Java x Golang TCP客户端-服务器连接?

这是一个相当大的问题,因为TCP连接的基本方案之一是发送和接收:

例如

  1. 客户端发送一条消息并等待结果。

  2. 服务器处理该消息并返回结果。

  3. 客户端对结果执行某些操作。

2 个答案:

答案 0 :(得分:2)

对于初学者,您可能要确保您的客户端代码可靠地关闭套接字。但这不是您应该做的全部。

由于关闭套接字,客户端进程应触发TCP FIN。由于客户端进程干净退出,套接字应该被关闭。不知道为什么这不是您的情况。你等了足够长的时间吗?

但是,为了不无限期地挂起服务器,您需要注意这种情况。考虑客户端和服务器之间存在活动TCP连接的情况。当服务器正在等待来自客户端的数据时,客户端所在的位置就会断电。因为客户端无法发送FIN,所以服务器连接不会有任何流量。除非......

,否则该TCP连接将永远挂起。

您可以在服务器端设置保持活动选项或超时选项。

保持活跃: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_TIMEOUT

答案 1 :(得分:0)

我终于按照@nos的建议打电话给CloseWrite()而不是Close()了。

不得不稍微更改我的TCP连接代码,但它的工作原理就像是一种魅力。

该解决方案非常适合我的目的,因为我只打算从客户端连接发送1条消息,并且可能在关闭前进行1条读取。

非常感谢所有帮助过的人!

下面是我更新的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))
        }()
    }
}