为啥在第一个连接结束后创建第二个连接会导致管道异常?

Posted

技术标签:

【中文标题】为啥在第一个连接结束后创建第二个连接会导致管道异常?【英文标题】:Why does creating a second connection after the first one has been ended result in a broken pipe exception?为什么在第一个连接结束后创建第二个连接会导致管道异常? 【发布时间】:2019-01-29 20:21:18 【问题描述】:

在我的应用程序中,我有两个人在不同的设备上通过 wifi 直接对等连接进行连接。当它们连接时,会分配一个用户 主机”和另一个“客户”。

WifiP2pManager.ConnectionInfoListener connectionInfoListener = new WifiP2pManager.ConnectionInfoListener() 
    @Override
    public void onConnectionInfoAvailable( WifiP2pInfo wifiP2pinfo) 
         InetAddress groupOwnerAddress = wifiP2pinfo.groupOwnerAddress;

        if (wifiP2pinfo.groupFormed && wifiP2pinfo.isGroupOwner) 

            connectionStatus.setText(R.string.host);
            new ServerClass().start();


         else if (wifiP2pinfo.groupFormed) 

            connectionStatus.setText(R.string.client);
            new ClientClass(groupOwnerAddress).start();


        

    

;

主机运行 ServerClass,而客户端运行 ClientClass。它们都使用 SendRecieve 类相互发送数据,这些数据是通过处理程序接收的。这一切都很好

public class ServerClass extends Thread 
    Socket socket;
    ServerSocket serverSocket;

    @Override
    public void run() 
        super.run();
        try 
            serverSocket = new ServerSocket(8888);
            socket = serverSocket.accept();
            sendReceive = new SendReceive(socket);
            sendReceive.start();
         catch (IOException e) 
            Log.v("MainActivity", "three" + e);
        
    


private class SendReceive extends Thread 
    Socket socket;
    OutputStream outputStream;
    InputStream inputStream;


    private SendReceive(Socket skt) 
        socket = skt;
        try 
            inputStream = socket.getInputStream();
            outputStream = socket.getOutputStream();
         catch (IOException e) 
            Log.v("MainActivity", "four" + e);
        
    

    @Override
    public void run() 
        byte[] buffer = new byte[1024];
        int bytes;
        while (socket != null) 
            try 
                bytes = inputStream.read(buffer);
                //System.out.println(new String(buffer, "UTF8"));
                if (bytes > 0) 
                    handler.obtainMessage(MESSAGE_READ, bytes, -1, buffer).sendToTarget();
                
             catch (IOException e) 
                Log.v("MainActivity", "five" + e);
            
        
    

    private void write(byte[] bytes) 
        try 
            outputStream.write(bytes);
         catch (IOException e) 
            Log.v("MainActivity", "six" + e);
        
    


public class ClientClass extends Thread 
    Socket socket;
    String hostAdd;

    private ClientClass(InetAddress hostAddress) 
        hostAdd = hostAddress.getHostAddress();
        socket = new Socket();
    

    @Override
    public void run() 
        try 
            socket.connect(new InetSocketAddress(hostAdd, 8888), 500);
            sendReceive = new SendReceive(socket);
            sendReceive.start();

         catch (IOException e) 
            Log.v("MainActivity", "seven" + e);
        
    

然后当游戏结束时,玩家通过按下调用此方法的按钮断开连接

public void disconnect() 
    if (mManager != null && mChannel != null) 

        mManager.requestGroupInfo(mChannel, new WifiP2pManager.GroupInfoListener() 
            @Override
            public void onGroupInfoAvailable(WifiP2pGroup group) 
                if (group != null && mManager != null && mChannel != null
                        && group.isGroupOwner()) 
                    mManager.removeGroup(mChannel, new WifiP2pManager.ActionListener() 

                        @Override
                        public void onSuccess() 
                            Log.d("main", "removeGroup onSuccess -");
                        

                        @Override
                        public void onFailure(int reason) 
                            Log.d("main", "removeGroup onFailure -" + reason);
                        
                    );
                
            
        );

    

当用户通过寻找其他玩家然后与他们建立联系来尝试与某人一起玩另一个游戏时,就会出现问题。当他们在与他们连接的第一个人断开连接后执行此操作时,点对点连接不起作用。它引发了一个损坏的管道异常,并且无法在两个用户之间发送信息。直到应用程序关闭然后再次打开,然后用户在应用程序打开后首次搜索并连接到某人,点对点连接才能正常工作。本质上,如果我重新启动应用程序,连接似乎工作正常,出于某种原因。但是必须有一种不那么直截了当的方法来解决根本问题,我似乎无法弄清楚它是什么。我已经阅读了所有与损坏的管道错误有关的先前问题,他们都谈到一端关闭连接,而另一端仍在尝试写入它们,但这里的情况并非如此,因为之前的连接是在那时建立的结束,并生成一个全新的。有什么我缺少的东西可以让我解决这个问题吗?

日志猫:

I/art: After code cache collection, code=245KB, data=226KB
D/ViewRootImpl@33c15f0[MainActivity]: ViewPostImeInputStage processPointer 0
D/ViewRootImpl@33c15f0[MainActivity]: ViewPostImeInputStage processPointer 1
D/ViewRootImpl@33c15f0[MainActivity]: mHardwareRenderer.destroy()#4
dispatchDetachedFromWindow
D/InputTransport: Input channel destroyed: fd=90
D/ViewRootImpl@589584e[MainActivity]: MSG_WINDOW_FOCUS_CHANGED 1
D/ViewRootImpl@589584e[MainActivity]: mHardwareRenderer.initializeIfNeeded()#2 mSurface=isValid=true -609390592
V/InputMethodManager: Starting input: 
tba=android.view.inputmethod.EditorInfo@c3f369e nm : com.example.hosse.myapplication ic=null
I/InputMethodManager: [IMM] startInputInner - 
mService.startInputOrWindowGainedFocus
D/InputTransport: Input channel constructed: fd=91
Input channel destroyed: fd=94
E/ViewRootImpl: sendUserActionEvent() returned.
D/main: removeGroup onSuccess -
V/MainActivity: fivejava.net.SocketException: Software caused connection abort
D/AbsListView:  in onLayout changed 
D/ViewRootImpl@ca0894c[Toast]: ThreadedRenderer.create() translucent=true
D/InputTransport: Input channel constructed: fd=94
D/ViewRootImpl@ca0894c[Toast]: setView = android.widget.LinearLayout6f7cd95 V.E...... ......I. 0,0-0,0 touchMode=true
D/ViewRootImpl@ca0894c[Toast]: dispatchAttachedToWindow
D/ViewRootImpl@ca0894c[Toast]: Relayout returned: oldFrame=[0,0][0,0] newFrame=[294,1596][786,1728] result=0x27 surface=isValid=true -593956864 
surfaceGenerationChanged=true
D/mali_winsys: EGLint new_window_surface(egl_winsys_display*, void*, EGLSurface, EGLConfig, egl_winsys_surface**, egl_color_buffer_format*, EGLBoolean) returns 0x3000,  [492x132]-format:1
D/ViewRootImpl@ca0894c[Toast]: mHardwareRenderer.initialize() mSurface=isValid=true -593956864 hwInitialized=true
D/ViewRootImpl@ca0894c[Toast]: MSG_RESIZED_REPORT: frame=Rect(294, 1596 - 786, 1728) ci=Rect(0, 0 - 0, 0) vi=Rect(0, 0 - 0, 0) or=1
D/ViewRootImpl@ca0894c[Toast]: mHardwareRenderer.destroy()#4
D/ViewRootImpl@ca0894c[Toast]: dispatchDetachedFromWindow
D/InputTransport: Input channel destroyed: fd=94
D/ViewRootImpl@589584e[MainActivity]: ViewPostImeInputStage processPointer 0
D/ViewRootImpl@589584e[MainActivity]: ViewPostImeInputStage processPointer 1
I/art: Do partial code cache collection, code=249KB, data=240KB
I/art: After code cache collection, code=239KB, data=234KB
I/art: Increasing code cache capacity to 1024KB
D/AbsListView:  in onLayout changed 
D/ViewRootImpl@589584e[MainActivity]: ViewPostImeInputStage processPointer 0
D/ViewRootImpl@589584e[MainActivity]: ViewPostImeInputStage processPointer 1
D/AbsListView: onTouchUp() mTouchMode : 0
D/TextView: setTypeface with style : 0
D/TextView: setTypeface with style : 0
D/ViewRootImpl@c74834e[MainActivity]: ThreadedRenderer.create() translucent=true
D/InputTransport: Input channel constructed: fd=93
D/ViewRootImpl@c74834e[MainActivity]: setView = DecorView@30699d2[MainActivity] touchMode=true
I/Choreographer: Skipped 33 frames!  The application may be doing too much work on its main thread.
D/ViewRootImpl@c74834e[MainActivity]: dispatchAttachedToWindow
D/ViewRootImpl@c74834e[MainActivity]: Relayout returned: oldFrame=[0,0][0,0] newFrame=[32,514][1047,1477] result=0x27 surface=isValid=true -593956864 surfaceGenerationChanged=true
D/mali_winsys: EGLint new_window_surface(egl_winsys_display*, void*, EGLSurface, EGLConfig, egl_winsys_surface**, egl_color_buffer_format*, EGLBoolean) returns 0x3000,  [1207x1155]-format:1
D/ViewRootImpl@c74834e[MainActivity]: mHardwareRenderer.initialize() mSurface=isValid=true -593956864 hwInitialized=true
D/ViewRootImpl@c74834e[MainActivity]: MSG_WINDOW_FOCUS_CHANGED 1
D/ViewRootImpl@c74834e[MainActivity]: mHardwareRenderer.initializeIfNeeded()#2 mSurface=isValid=true -593956864
V/InputMethodManager: Starting input: tba=android.view.inputmethod.EditorInfo@9f1dc6f nm : com.example.hosse.myapplication ic=null
I/InputMethodManager: [IMM] startInputInner - mService.startInputOrWindowGainedFocus
D/InputTransport: Input channel constructed: fd=90
Input channel destroyed: fd=91
I/Choreographer: Skipped 33 frames!  The application may be doing too much work on its main thread.
D/ViewRootImpl@c74834e[MainActivity]: MSG_RESIZED_REPORT: frame=Rect(32, 514 - 1047, 1477) ci=Rect(0, 0 - 0, 0) vi=Rect(0, 0 - 0, 0) or=1
D/ViewRootImpl@589584e[MainActivity]: MSG_WINDOW_FOCUS_CHANGED 0
D/ViewRootImpl@c74834e[MainActivity]: ViewPostImeInputStage processPointer 0
D/ViewRootImpl@c74834e[MainActivity]: ViewPostImeInputStage processPointer 1
D/ViewRootImpl@c74834e[MainActivity]: mHardwareRenderer.destroy()#4
D/ViewRootImpl@c74834e[MainActivity]: dispatchDetachedFromWindow
D/InputTransport: Input channel destroyed: fd=93
D/ViewRootImpl@589584e[MainActivity]: MSG_WINDOW_FOCUS_CHANGED 1
D/ViewRootImpl@589584e[MainActivity]: mHardwareRenderer.initializeIfNeeded()#2 mSurface=isValid=true -609390592
V/InputMethodManager: Starting input: tba=android.view.inputmethod.EditorInfo@9fedd7c nm : com.example.hosse.myapplication ic=null
I/InputMethodManager: [IMM] startInputInner - mService.startInputOrWindowGainedFocus
D/InputTransport: Input channel constructed: fd=93
Input channel destroyed: fd=90
E/ViewRootImpl: sendUserActionEvent() returned.
D/ViewRootImpl@2e84605[Toast]: ThreadedRenderer.create() translucent=true
D/InputTransport: Input channel constructed: fd=91
D/ViewRootImpl@2e84605[Toast]: setView = android.widget.LinearLayoutf19865a V.E...... ......I. 0,0-0,0 touchMode=true
D/ViewRootImpl@2e84605[Toast]: dispatchAttachedToWindow
D/ViewRootImpl@2e84605[Toast]: Relayout returned: oldFrame=[0,0][0,0] newFrame=[310,1596][769,1728] result=0x27 surface=isValid=true -593956864 surfaceGenerationChanged=true
D/mali_winsys: EGLint new_window_surface(egl_winsys_display*, void*, EGLSurface, EGLConfig, egl_winsys_surface**, egl_color_buffer_format*, EGLBoolean) returns 0x3000,  [459x132]-format:1
D/ViewRootImpl@2e84605[Toast]: mHardwareRenderer.initialize() mSurface=isValid=true -593956864 hwInitialized=true
D/ViewRootImpl@2e84605[Toast]: MSG_RESIZED_REPORT: frame=Rect(310, 1596 - 769, 1728) ci=Rect(0, 0 - 0, 0) vi=Rect(0, 0 - 0, 0) or=1
D/ViewRootImpl@2e84605[Toast]: mHardwareRenderer.destroy()#4
D/ViewRootImpl@2e84605[Toast]: dispatchDetachedFromWindow
D/InputTransport: Input channel destroyed: fd=91
V/MainActivity: threejava.net.BindException: Address already in use
D/TextView: setTypeface with style : 0
D/ViewRootImpl@7903a0a[MainActivity]: ThreadedRenderer.create() translucent=true
D/InputTransport: Input channel constructed: fd=91
D/ViewRootImpl@7903a0a[MainActivity]: setView = DecorView@94082e2[MainActivity] touchMode=true
V/MainActivity: sixjava.net.SocketException: Broken pipe
D/ViewRootImpl@7903a0a[MainActivity]: dispatchAttachedToWindow
D/ViewRootImpl@7903a0a[MainActivity]: Relayout returned: oldFrame=[0,0][0,0] newFrame=[32,253][1047,1738] result=0x27 surface=isValid=true -593956864 surfaceGenerationChanged=true
D/ViewRootImpl@7903a0a[MainActivity]: mHardwareRenderer.initialize() mSurface=isValid=true -593956864 hwInitialized=true
D/mali_winsys: EGLint new_window_surface(egl_winsys_display*, void*, EGLSurface, EGLConfig, egl_winsys_surface**, egl_color_buffer_format*, EGLBoolean) returns 0x3000,  [1207x1677]-format:1
D/ViewRootImpl@7903a0a[MainActivity]: MSG_WINDOW_FOCUS_CHANGED 1
D/ViewRootImpl@7903a0a[MainActivity]: mHardwareRenderer.initializeIfNeeded()#2 mSurface=isValid=true -593956864
V/InputMethodManager: Starting input: tba=android.view.inputmethod.EditorInfo@17e88f1 nm : com.example.hosse.myapplication ic=null
I/InputMethodManager: [IMM] startInputInner - mService.startInputOrWindowGainedFocus
D/InputTransport: Input channel constructed: fd=95
Input channel destroyed: fd=93
D/ViewRootImpl@7903a0a[MainActivity]: MSG_RESIZED_REPORT: frame=Rect(32, 253 - 1047, 1738) ci=Rect(0, 0 - 0, 0) vi=Rect(0, 0 - 0, 0) or=1
D/ViewRootImpl@589584e[MainActivity]: MSG_WINDOW_FOCUS_CHANGED 0
V/MainActivity: sixjava.net.SocketException: Broken pipe

【问题讨论】:

请添加堆栈跟踪(最好是问题系统的所有Log.v 输出),并说明遇到此问题的服务器或客户端。 @greeble31 谢谢,添加了 logcat。也因为它是对等连接,它发生在两端。两个用户都充当客户端和服务器,所以连接似乎都被切断了,并且都遇到了管道中断错误。 【参考方案1】:

在您提供的示例中,来自服务器的问题是由此行上的 BindException 引起的:

serverSocket = new ServerSocket(8888);

异常处理不当,导致该函数中的其余逻辑被跳过。这会给你留下一个旧的SendRecieve,其中包含你上一场游戏留下的旧Socket。当您尝试写信给这个Socket 时,自然会遇到“破管”异常。

建议的修复

使用Exception.printStackTrace() 以便在日志文件中更好地“突出”异常,和/或使用Log.e()(我假设您没有注意到“三个”打印出来,这就是导致此问题的原因)。需要更好的异常处理逻辑;如果ServerSocket()accept() 调用发生异常,则应分别处理绑定错误或网络I/O 问题;也许通过通知用户,但至少通过更改程序状态以使游戏无法进行。

首先要避免BindException,您有两种选择:

    允许单个 ServerSocket 在您的应用程序的整个生命周期中持续存在。这需要您重新设计架构。

    在绑定之前在 ServerSocket 上设置 SO_REUSEADDR 选项,例如:

...

serverSocket = new ServerSocket();
serverSocket.setReuseAddress(true);
serverSocket.bind(8888);

...

使用选项 2 时,请注意在服务器游戏结束后关闭旧的 ServerSocket

仅仅因为您设置了 SO_REUSEADDR 并不意味着您仍然无法获得BindException!您无法控制的其他一些应用程序可能会打开该端口。

今后避免这种情况的提示

当您遇到这样的问题时,最好仔细重新检查您的所有假设;在这种情况下,假设正在执行哪些行,以及您的 Socket 有多“新”。更多的日志记录可能会有所帮助。您可能希望在每个 try 块的末尾放置一个记录器 - 类似于“已创建服务器套接字!”或“连接已接受!”。这有助于让您确信事情正在按照您期望的方式执行。此外,由于您知道“管道损坏”错误涉及 Socket 生命周期,您可以在每个 SendReceive() 中转储 socket.toString() 的值。如果字符串没有改变,你会知道你的Socket 仍然是旧的。你不一定知道为什么,但你仍然有一个重要的线索。

编辑

另一个好技巧:null 完成后的引用。尤其是Sockets的情况;在你close()他们之后,他们就没什么用了。这也有助于避免这个问题。

【讨论】:

以上是关于为啥在第一个连接结束后创建第二个连接会导致管道异常?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的模型在第二个 epoch 过拟合?

左外连接 - 如何在第二个表中返回一个布尔值?

本地连接为啥会有两个自动配置ipv4

为啥我的信号处理程序(引发异常)不会多次触发?

连接第二个 BLE 设备后 BLE 响应时间变慢

为啥带有 2 个视频的 ffmpeg concat 会丢失第二个音频?