0

In my application I have two people on different devices connect through wifi direct peer to peer connection. When they are connected, one user is assigned host" and the other "client".

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();


        }

    }

};

The host runs the ServerClass , while the client runs the ClientClass. Both of them use the SendRecieve class to send each other data, which is received through a handler. This all works fine

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);
        }
    }
}

and then when the game is ended the players disconnect by pressing a button that calls this method

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);
                        }
                    });
                }
            }
        });

    }
}

A problem arises When the user then tries to play another game with someone by looking for other players and then connecting with them. When they do this after disconnecting from the first person they connected with, the peer to peer connection doesn't work. It throws a broken pipe exception and information is not able to be sent between the two users. It is not until the app is closed then opened again, then the user searches for and connects to someone for the first time since the app was opened that the peer to peer connection works normally. essentially if I restart the app the connection seems to work fine, for some reason. But there must be a less blunt way of fixing the underlying issue, and I can't seem to figure out what it is. I've read all previous questions having to do with broken pipe errors and they all talk about one end closing the connection while the other is still trying to write to them, but that isn't the case here since the previous connection is made then ended, and a entirely new one is generated. Is there something I'm missing that will allow me to solve this issue?

logcat:

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.LinearLayout{6f7cd95 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.LinearLayout{f19865a 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
Solaire
  • 21
  • 7
  • Please add the stack trace (preferably all the `Log.v` output from the problem system), and mention whether it is the server or client that experiences this problem. – greeble31 Jan 29 '19 at 23:16
  • @greeble31 thanks, added the logcat. also since it's peer to peer connection it happens on both ends. Both users are acting as both clients and servers, so the connection all together seems to be severed, and both experience a broken pipe error. – Solaire Jan 30 '19 at 03:02

1 Answers1

0

In the example you provided, from the server, the problem is caused by a BindException on this line:

serverSocket = new ServerSocket(8888);

The exception is handled improperly, causing the rest of the logic in that function to be skipped. That leaves you with an old SendRecieve that contains the old Socket that was left over from your last game. When you try to write to this Socket, naturally you get a "broken pipe" exception.

SUGGESTED FIXES

Use Exception.printStackTrace() so that exceptions "stand out" better in the log file, and/or use Log.e() (I assume that you didn't notice the "three" printing out, which is what led to this problem). Better exception handling logic is necessary; if an exception occurs on either the ServerSocket() or the accept() calls, you should handle the binding error or the network I/O problem, respectively; perhaps by notifying the user, but at least by changing the program state so that play cannot advance.

To avoid the BindException in the first place, you have two choices:

  1. Allow a single ServerSocket to persist for the life of your app. This will require some re-architecting on your part.

  2. Set the SO_REUSEADDR option on your ServerSocket before binding, e.g.:

...

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

...

When using option 2, take care to close the old ServerSocket once the server's game has finished.

Just because you set SO_REUSEADDR does not mean you can't still get a BindException! Some other app, which you don't control, might have that port open.

TIPS TO AVOID THIS IN THE FUTURE

When you run into a problem like this, it's a good idea to carefully re-check all your assumptions; in this case, assumptions about which lines were executing, and how "new" your Socket was. Some more logging may help. You may want to put a logger at the end of each try block - something like "Server socket created!" or "connection accepted!". This helps give you confidence that things are executing the way you expect them to. Also, since you knew the "broken pipe" error involved the Socket lifecycle, you could have dumped the value of socket.toString() in each SendReceive(). If the strings didn't change, you'd know your Socket was still the old one. You wouldn't know why, necessarily, but you'd still have an important clue.

EDIT

Another good technique: null your references after you're done with them. Especially in the case of Sockets; after you close() them, they're of very little use. That would've helped avoid this issue as well.

greeble31
  • 4,894
  • 2
  • 16
  • 30