套接字客户端:没有得到 DataInputStream.readUTF() 的完整响应

Socket client: don't get the full response with DataInputStream.readUTF()

我想编写一个套接字客户端来向服务器发送请求并获得响应。它有效,但不正确。 这是我的代码:

public String send(final String data) {
        Socket client = null;
        String response = null;

        try {
            client = new Socket(this.host, this.port);

            final OutputStream outToServer = client.getOutputStream();
            final DataOutputStream out = new DataOutputStream(outToServer);
            out.writeUTF(data);

            final InputStream inFromServer = client.getInputStream();
            final DataInputStream in = new DataInputStream(inFromServer);
            response = in.readUTF();
        } catch (final IOException e) {
            this.log.error(e);
            this.log.error("Sending message to server " + this.host + ":" + this.port + " fail", e);
        } finally {
            if (client != null) {
                try {
                    client.close();
                } catch (final IOException e) {
                    this.log.error("Can't close socket connection to " + this.host + ":" + this.port, e);
                }
            }
        }
        if (StringUtils.isBlank(response)) return null;
        return response;
    }

问题是:我没有得到 in.readUTF() 的完整回复。我总是收到与发送数据长度相同的响应(变量 data)。我已经用其他 GUI 客户端进行了测试并得到了完整的响应。所以不是服务器的问题。 有人知道我做错了什么吗?

更新

感谢 EJP 和 Andrey Lebedenko。我想,我的问题是函数 writeUTFreadUTF。所以我在 try 块中编辑了我的代码:

        Charset charset = Charset.forName("ISO-8859-1");
        final OutputStream outToServer = client.getOutputStream();
        final DataOutputStream out = new DataOutputStream(outToServer);
        out.write(data.getBytes(charset));

        final InputStream inFromServer = client.getInputStream();
        final BufferedReader in = new BufferedReader(new InputStreamReader(inFromServer, charset));
        response = in.readLine();

现在有效了。

在不知道服务器部分做什么的情况下,很难(如果可能的话)追查根本原因。因此,真诚地希望它能对我有所帮助,我只是分享了这段代码,我用它测试了上面的片段。

package tcpsendreceive;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.logging.Level;
import java.util.logging.Logger;

public class SendReceive {
    private static final Logger log = Logger.getLogger(SendReceive.class.toString());

    private final String host;
    private final int port;

    private Server server;

    class Server extends Thread
    {
        private final ServerSocket serverSocket;
        public Server(ServerSocket s)
        {
            serverSocket = s;
        }

        @Override
        public void run()
        {
            Socket connection;
            SendReceive.this.log.log(Level.INFO, "Server: DoubleEcho Server running on "+this.serverSocket.getLocalPort());
            try{
                do {
                    connection = this.serverSocket.accept();
                    SendReceive.this.log.log(Level.INFO, "Server: new connection from "+connection.getRemoteSocketAddress());
                    int b;
                    do {
                        DataInputStream in = new DataInputStream(connection.getInputStream());
                        String s = in.readUTF();

                        DataOutputStream out = new DataOutputStream(connection.getOutputStream());
                        out.writeUTF(s+","+s); // echo it back TWICE

                        out.flush();
                        connection.shutdownOutput();
                        connection.close();
                    } while(!connection.isClosed());
                }
                while(true);
            }
            catch(IOException ioe)
            {
                SendReceive.this.log.log(Level.SEVERE, "IOException in server! - STOP", ioe);
            }
            finally {
                try{
                    this.serverSocket.close();
                } catch (Exception e) {
                    SendReceive.this.log.log(Level.SEVERE, "IOException closing server! - FATAL", e);
                }
                try{
                    if(!connection.isClosed())
                      connection.close();
                } catch (Exception e) {
                    SendReceive.this.log.log(Level.SEVERE, "IOException closing server! - FATAL", e);
                }
            }
        }
    }

    public SendReceive(String host, int port)
    {
        this.host = host;
        this.port = port;
        try{
            this.server = new Server(new ServerSocket(this.port));
            this.server.start();
        }
        catch(IOException ioe)
        {
            this.log.log(Level.SEVERE, "IOException while creating server! - STOP", ioe);
        }
    }

    public String send(final String data) {
        Socket client = null;
        String response = null;

        try {
            client = new Socket(this.host, this.port);

            final OutputStream outToServer = client.getOutputStream();
            final DataOutputStream out = new DataOutputStream(outToServer);
            out.writeUTF(data);

            final InputStream inFromServer = client.getInputStream();
            final DataInputStream in = new DataInputStream(inFromServer);
            response = in.readUTF();
        } catch (final IOException e) {
            this.log.log(Level.SEVERE, "Sending message to server " + this.host + ":" + this.port + " fail", e);
        } finally {
            if (client != null) {
                try {
                    client.close();
                } catch (final IOException e) {
                    this.log.log(Level.SEVERE, "Can't close socket connection to " + this.host + ":" + this.port, e);
                }
            }
        }
        if(response == null || response.isEmpty()) 
            return null;

        return response;
    }    
}

测试演示

package tcpsendreceive;

public class Main {
    public static void main(String[]  args)
    {    
        String data = "AAABBB";
        SendReceive sr = new SendReceive("localhost", 5000);
        String res = sr.send(data);
        System.out.println("Sent: "+data);
        System.out.println("Received: "+res);
    }
}

结果(关键部分):

Sent: AAABBB
Received: AAABBB,AAABBB

希望对您有所帮助。

编辑 1. 更正了包装器刷新而不是套接字流刷新。仍然相信在关闭套接字之前刷新流包装器是一种很好的做法。

  1. 客户端套接字在 IOException 块中关闭(因为它们不会在 ServerSocket 关闭时自动关闭)。谢谢@EJP

P.s。我应该承认我对如此关注不起眼的测试服务器代码感到有点惊讶,尤其是与我们在 SO 上看到的其他脏测试相比。受宠若惊。 :)

如果它与 Telnet 一起工作,根据你的评论,服务器没有使用 readUTF(),所以你的 writeUTF() 已经是错误的,因此服务器不太可能使用 writeUTF(),这也会使您的 readUTF() 错误。您不能随意使用这些方法:它们只能在它们之间交换数据。

我敢打赌你的 GUI 客户端也不会使用它们。