通过同一个套接字发送和接收数据

3

我是一个新手,对Java sockets并不熟悉,我在使用相同的socket发送和接收数据时遇到了问题。

服务器位于一个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); 处崩溃,提示套接字已关闭。我希望保持整个套接字开放以进行更多的数据交换,直到发送关闭命令为止。显然我在这里做错了些什么,有什么见解吗?谢谢!
3个回答

12

你既没有关闭套接字也没有关闭输出流。在客户端中,你使用了shutdownOutput()方法:

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

这被称为半关闭,因为它只关闭一个方向并将另一个方向保持开放。


如果调用dataOutputStream.close()会发生什么?它仍然会是半关闭状态吗? - sgp15
@sgp15,这已经在上面的问题中提到了:“如果我执行dataOutputStream.close()...然后客户端在while (dataInputStream.read() != -1)时死亡,说套接字已关闭。” - Olaf Dietsche
会尝试这样做,但实际上我想保持整个套接字开放以进行更多的数据交换,直到发送关闭命令。有什么想法吗? - m0skit0
2
测试 stream.read() != -1 是否为 EOF。如果您不想(半)关闭流,则必须测试其他结束标记,正如 @Pyranja 已经建议的那样。 - Olaf Dietsche
开发者向您致敬! - Prince Bansal

2

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

关闭从套接字获取的流也会关闭套接字(见此处)。另一方面,只要流/套接字保持打开状态(可能还有更多的位数在路上!),即使从套接字获取的InputStream已发送所有数据,它也无法看到。除非使用Olaf的例子,否则不可能通过这种方式进行双向通信。这允许等待服务器响应。

您可以自己定义一个结束信号(例如字符串“END”),然后每一方侦听直到读取该结束信号,然后再写入。但这会带来各种其他问题,您肯定会遇到这些问题(如果发送的文本包含结束信号怎么办?如果合作伙伴在发送结束之前死亡怎么办?超时?...)。 见此处

我会尝试查看在Java中广受支持的SOAP或RESTful服务(但我不知道Android上的表现如何)。通常,有许多库可以为您解决这些问题,并使您免于低级网络。使用现有的解决方案几乎总是值得的。


如果消息的长度可以预先确定,那么也可以发送前几个字节(例如4个字节)来指示随后的有效载荷的长度。 - sgp15
谢谢您的回答,但我不能使用除TCP之外的任何东西。这是针对已经实现的TCP协议。 - m0skit0
从技术角度来说,SOAP 和 REST 确实使用 TCP 协议...(: 但是像 @sgp15 建议的使用包含后续数据长度的头部也是一个可行的替代方案。这样可以简化错误处理。总的来说,如果你想在套接字连接中使用通信协议,就必须以某种方式定义一个通信协议,清楚地标记交换消息的边界。 - Pyranja
完全正确。我完全忘记了TCP上的自定义协议具有起始-结束标记。谢谢! - m0skit0

0
尝试通过执行 dataOutStream.flush() 来刷新 OutputStream。

在客户端,我正在执行 outStream.write(outPacket); 然后读取数据,但是服务器没有正确地接收数据包。写完数据后刷新缓存对我有用! - wrapperapps

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接