【问题标题】:Send and receive over the same socket通过同一个套接字发送和接收
【发布时间】:2012-10-05 16:43:43
【问题描述】:

我对 Java 套接字很陌生,但在使用同一个套接字发送和接收数据时遇到问题。

服务器在 Android 设备上:

    ServerSocket listenSocket = null;
    OutputStream dataOutStream = null;
    Socket socket = null;
    InputStream dataInputStream = null;

    // Listen
    System.out.println("Start listening");
    try {
        listenSocket = new ServerSocket(4370);
        socket = listenSocket.accept();
        System.out.println("Connection accepted");
        dataInputStream = socket.getInputStream();
        dataOutStream = socket.getOutputStream();
        while (dataInputStream.read() != -1);
    } catch (IOException e) {
        e.printStackTrace();
        close(listenSocket, socket);
        return;
    }

    // Answer
    System.out.println("Answering...");
    byte[] answer = {(byte) 0x82, (byte) 0xf8, 0, 0};
    try {
        dataOutStream.write(answer);
    } catch (IOException e) {
        e.printStackTrace();
        close(listenSocket, socket);
        return;
    }

    close(listenSocket, socket);
    System.out.println("Finished");

客户端在装有 Java 6 的 Linux 机器上运行:

    Socket socket = new Socket("192.168.1.33", 4370);
    OutputStream dataOutputStream = socket.getOutputStream();
    InputStream dataInputStream = socket.getInputStream();
    byte[] bufferOut = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    dataOutputStream.write(bufferOut);
    System.out.println("Sent");

    while (dataInputStream.read() != -1);       
    socket.close();
    System.out.println("Finished");

这里的问题是服务器卡在while (dataInputStream.read() != -1); 行。看起来客户端永远不会关闭发送。

如果我在客户端部分执行dataOutputStream.close()(当然是在写入之后),那么它确实可以工作,但是客户端在while (dataInputStream.read() != -1); 上死了,说套接字已关闭。

我想保持整个套接字打开,以便在同一个套接字上进行更多数据交换,直到发送关闭命令。

我显然在这里做错了什么,有什么见解吗?提前致谢!

【问题讨论】:

    标签: java android networking


    【解决方案1】:

    你既不关闭套接字也不关闭输出流。你在客户端做一个shutdownOutput():

    ...
    dataOutputStream.write(bufferOut);
    System.out.println("Sent");
    socket.shutdownOutput();
    ...
    

    这称为半关闭,因为它只关闭一个方向而使另一个方向保持打开状态。

    【讨论】:

    • 如果调用 dataOutputStream.close() 会发生什么?它还会接近一半吗?
    • @sgp15 这已经在上面的问题中了:“如果我执行 dataOutputStream.close() ... 那么客户端在 while (dataInputStream.read() != -1);套接字已关闭。
    • 会试试这个,但实际上我想保持整个套接字打开,以便在同一个套接字上进行更多数据交换,直到发送关闭命令。有什么想法吗?
    • 测试stream.read() != -1 表示EOF。如果您不想(一半)关闭流,则必须测试其他一些结束标记,正如@Pyranja 已经建议的那样。
    【解决方案2】:

    edit:Olaf 展示了一种内置方式来实现问题中的请求-响应场景。

    关闭从套接字获得的流也会关闭套接字(see also)。另一方面,从套接字获得的 InputStream 不可能查看是否所有数据都已发送,只要流/套接字是打开(可能会有更多位!)。除非使用 Olafs 示例,否则 2-way 通信不能以这种方式工作。这允许等待服务器响应。

    您可以做的是自己定义一个结束信号(例如字符串“END”),并且任何一方都会监听直到读取该结束信号,然后再写入。但这伴随着你肯定会遇到的各种其他问题(如果发送的文本包含结束信号怎么办?如果合作伙伴在发送结束之前死了怎么办?超时?...)。 see also

    我会尝试查看在 Java 中得到很好支持的 SOAP 或 RESTful 服务(但我不知道在 Android 上的支持情况如何)。通常有许多库可以为您解决这些问题,并使您从低级网络中解脱出来。使用现有解决方案几乎总能带来回报。

    【讨论】:

    • 如果可以预先确定消息的长度,也可以发送前几个字节(例如 4 个字节)来指示后续有效负载的长度。
    • 感谢您的回答,但除了 TCP 之外我不能使用任何东西。这是针对已经通过 TCP 实现的协议。
    • 从技术上讲 SOAP 和 REST 确实使用 TCP... (: 但是使用包含以下数据长度的标头作为@sgp15 建议也是一种可行的选择。可以简化错误处理。一般来说你有如果你想在套接字连接上使用,以某种方式定义一个清楚地标记交换消息边界的通信协议。
    • 完全正确。我完全忘记了 TCP 之上的自定义协议有开始结束标记。谢谢!
    【解决方案3】:

    尝试通过dataOutStream.flush() 刷新输出流

    【讨论】:

    • 在客户端上,我正在做outStream.write(outPacket); 然后读取,但服务器没有正确接收数据包。写完后脸红对我有用!
    猜你喜欢
    • 2015-07-12
    • 1970-01-01
    • 2021-07-08
    • 1970-01-01
    • 2012-02-12
    • 2013-09-11
    • 1970-01-01
    • 2015-11-22
    • 1970-01-01
    相关资源
    最近更新 更多