java.net.SocketException: sendto failed: EPIPE (Broken pipe) 当发送字符检查套接字连接时

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/33361031/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 21:33:28  来源:igfitidea点击:

java.net.SocketException: sendto failed: EPIPE (Broken pipe) When sending character to check socket connection

javaandroidmultithreadingsockets

提问by Mr Neo

I develop a system include: socket server on Android Mobile and socket client on PC. And to check the connection with client, I had sent a " "character every 1 second from server.

我开发的系统包括:Android Mobile 上的socket 服务器和PC 上的socket 客户端。为了检查与客户端的连接,我" "每 1 秒从服务器发送一个字符。

But sometimes, I got the exception:

但有时,我得到了例外:

java.net.SocketException: sendto failed: EPIPE (Broken pipe)    
    at libcore.io.IoBridge.maybeThrowAfterSendto(IoBridge.java:506)    
    at libcore.io.IoBridge.sendto(IoBridge.java:475)    
    at java.net.PlainSocketImpl.write(PlainSocketImpl.java:507)    
    at java.net.PlainSocketImpl.access0(PlainSocketImpl.java:46)    
    at java.net.PlainSocketImpl$PlainSocketOutputStream.write(PlainSocketImpl.java:269)    
    at java.io.OutputStream.write(OutputStream.java:82)    
    at com.foxconn.cnsbgit.mobileterminal.MainActivity$ServerThread.run(MainActivity.jaa:527)    
    at java.lang.Thread.run(Thread.java:856)    
Caused by: libcore.io.ErrnoException: sendto failed: EPIPE (Broken pipe)    
    at libcore.io.Posix.sendtoBytes(Native Method)    
    at libcore.io.Posix.sendto(Posix.java:151)    
    at libcore.io.BlockGuardOs.sendto(BlockGuardOs.java:177)    
    at libcore.io.IoBridge.sendto(IoBridge.java:473)    
    ... 6 more

The sending Threadto check connection is following:

发送Thread检查连接如下:

while (true) {
    if (client != null) {
         try {
              client.getOutputStream().write(" ".getBytes()); // Get exception when sending character
              Thread.sleep(1000);
              mHandler.post(new Runnable() {
                  @Override
                  public void run() {
                      txtConnectionStatus.setText(R.string.smoConnectOK);
                  }
              });
         } catch (Exception e) {
              e.printStackTrace(); // Get exception at here
              mHandler.post(new Runnable() {
                  @Override
                  public void run() {
                      if !txtConnectionStatus.getText().toString().contains("FAIL")) {
                           txtConnectionStatus.setText(R.string.connectionFailString);
                      }
                  }
              });
              try {
                   Thread.sleep(5000);
              } catch (InterruptedException e1) {
                   e1.printStackTrace();
              }
          }
     }
}

Updated:Then I had input and sent data to client. Is connection lost when both heartbeat and data send at the same time? :

更新:然后我输入并将数据发送到客户端。当心跳和数据同时发送时,连接是否丢失?:

public class SendDataThread implements Runnable {

    @Override
    public void run() {
        try {
            if (client != null) {
                sendDataStream = client.getOutputStream();
                sendDataStream.write(dataSend); //dataSend is a byte array
                sendDataStream.flush();

                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        edtCommand.selectAll();
                    }
                });
            }
        } catch (final Exception e) {
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    txtRec.setText(e.toString());
                }
            });
        }
    }
}

回答by Valamburi M

When an Android is kept idle, the device locks and then it goes to deep sleep mode. In deep sleep mode the Android system, closes the existing network connections like TCP or UDP. If your app is connected to a server, it loses connection to the server and tries to reconnect based on the reconnect attempt methods configured for the client. But if your app is the server, all the client will lose connection to the server, and you have to start the socket again in server and try to connect once again from the clients.

当 Android 处于空闲状态时,设备会锁定,然后进入深度睡眠模式。在深度睡眠模式下,Android 系统会关闭现有的网络连接,如 TCP 或 UDP。如果您的应用程序连接到服务器,它会失去与服务器的连接并尝试根据为客户端配置的重新连接尝试方法重新连接。但是如果您的应用程序是服务器,则所有客户端都将失去与服务器的连接,您必须在服务器中再次启动套接字并尝试从客户端再次连接。

Keeping a TCP connection open for extended periods may not be a good option for a mobile device, because TCP connections don't interact well with computers that go to sleep. The problem scenario would be this: your Android user puts his Android device to sleep while your app is running, and then the remote user's program (or whatever is at the other end of the TCP connection) sends some data over the TCP stream. The remote user's program never gets any ACKs back from the Android device, because of course the Android device is asleep, so the remote device's TCP stack assumes that the TCP packets it sent must have been lost, and it responds by increasing its timeout period, decreasing its TCP window size (aka number-of-TCP-packets-allowed-in-flight-at-once), and resending the TCP packets. But the Android device is still asleep, and thus the same thing happens again. The upshot is that a few minutes later, the remote end of the TCP connection has slowed down to the point where even if the Android device was to wake up, the TCP connection will likely be too slow to be usable -- at which point your program will need to close the bogged-down TCP connection and start up a fresh one anyway, so why bother trying to keep it open?

对于移动设备来说,长时间保持 TCP 连接打开可能不是一个好的选择,因为 TCP 连接不能与进入睡眠状态的计算机很好地交互。问题场景是这样的:当您的应用程序运行时,您的 Android 用户将他的 Android 设备置于睡眠状态,然后远程用户的程序(或 TCP 连接另一端的任何程序)通过 TCP 流发送一些数据。远程用户的程序永远不会从 Android 设备收到任何 ACK,因为 Android 设备当然处于睡眠状态,因此远程设备的 TCP 堆栈假定它发送的 TCP 数据包肯定已经丢失,并通过增加其超时时间来响应,减少它的 TCP 窗口大小(又名 TCP 数据包数量),并重新发送 TCP 数据包。但是安卓设备还在休眠,因此同样的事情再次发生。结果是几分钟后,TCP 连接的远程端的速度减慢到即使 Android 设备唤醒,TCP 连接也可能太慢而无法使用——此时您的程序将需要关闭陷入困境的 TCP 连接并无论如何都要启动一个新的连接,那么为什么还要尝试保持它打开呢?

Solution

解决方案

Acquire a PARTIAL_WAKE_LOCK, and trap when the screen goes off. Then disable and reenable the wifi. This works because the filter only turns on when the screen goes off, so starting wifi with the screen off will keep it working until the screen goes off again.

获取 PARTIAL_WAKE_LOCK,并在屏幕熄灭时捕获。然后禁用并重新启用wifi。这是有效的,因为过滤器仅在屏幕关闭时才会打开,因此在屏幕关闭的情况下启动 wifi 将使其继续工作,直到屏幕再次关闭。

回答by Steffen Ullrich

But sometimes, I got the exception:

但有时,我得到了例外:

java.net.SocketException: sendto failed: EPIPE (Broken pipe)

java.net.SocketException: sendto failed: EPIPE (Broken pipe)

EPIPE is triggered if you write to a socket but the peer has closed the socket already. Thus this exception indicates that you can no longer communicate through this socket.

如果您写入套接字但对等方已关闭套接字,则会触发 EPIPE。因此,此异常表明您无法再通过此套接字进行通信。

回答by user207421

Your system is working exactly as designed. You sent the heartbeat, it fails, you've detected a dead connection. That's the purpose of the code.

您的系统完全按照设计工作。你发送了心跳,它失败了,你检测到一个死连接。这就是代码的目的。