天天看點

ServerSocketChannel監聽問題

今天遇到一個問題,就是推出了監聽界面之後,再進來界面還能監聽用戶端連接配接服務端。

做法:

1.關閉界面的時候要把服務端線程停止。

2.先關閉跟用戶端連接配接的socket

3.關閉服務端的socket.

4.關閉select選擇器.

做完這些,以後再進這個界面就可以重新監聽了。

這樣可以修複下面這個問題了 W/System.err:     at com.example.tcplibrary.tcpserver.TcpServer.a(Unknown Source)

 W/System.err:     at com.example.tcplibrary.tcpserver.TcpServer.run(Unknown Source)

有這個問題是因為端口資源被暫用了。

如果發現,服務端起來了,但是用戶端連接配接不上,有可能是之前沒清掉用戶端跟服務端的連接配接。得清掉之後才能自動連接配接上。

下面是一個測試的小代碼:

socket類的代碼:

package gateway.demo.com.gatewaydemo.tcpserver;

import android.util.Log;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * Created by caifeng on 2018/7/11.
 */

public class TcpServer implements Runnable {
    private static final String TAG = "TcpServer";
    private static TcpServer mTcpServer = null;
    private Selector mSelector;
    private SocketChannel mSocketChannel;
    private ServerSocketChannel server;
    private boolean work;

    public static TcpServer getInstance() {
        if (mTcpServer == null) {
            synchronized (TcpServer.class) {
                if (mTcpServer == null) {
                    mTcpServer = new TcpServer();
                }
            }
        }
        return mTcpServer;
    }

    private TcpServer() {

    }

    @Override
    public void run() {
        buildServerSocketChannel(); //建立服務端通道
        iterationSelector(); //疊代選擇器

    }

    /**
     * 建立ServerSocketChannel
     *
     * @throws IOException
     */
    private void buildServerSocketChannel() {
        try {
            mSelector = Selector.open();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            server = ServerSocketChannel.open();
        } catch (IOException e) {
            e.printStackTrace();
        }
        Log.d("TcpServer", "打開成功");
        try {
            server.configureBlocking(false);
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            server.socket().bind(new InetSocketAddress(9190), 1024);
        } catch (IOException e) {
            e.printStackTrace();
        }
        Log.d("TcpServer", "綁定成功");
        try {
            server.register(mSelector, SelectionKey.OP_ACCEPT);
        } catch (ClosedChannelException e) {
            e.printStackTrace();
        }
        Log.d("TcpServer", "注冊成功");
    }

    /**
     * 循環周遊SelectionKey
     */
    private void iterationSelector() {
        while (work) {
            try {
                mSelector.select(1000);
            } catch (IOException e) {
                e.printStackTrace();
            }
            Set<SelectionKey> selectionKeys = mSelector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            SelectionKey selectionKey = null;

            while (iterator.hasNext()) {
                selectionKey = iterator.next();
                iterator.remove();
                if (selectionKey.isAcceptable()) {
                    ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectionKey.channel();
                    try {
                        mSocketChannel = serverSocketChannel.accept();
                        mSocketChannel.configureBlocking(false);
                        mSocketChannel.register(mSelector, SelectionKey.OP_READ);
                        Log.d(TAG, "連接配接成功");
                    } catch (IOException e) {
                        e.printStackTrace();
                        selectionKey.cancel();
                        if (selectionKey.channel() != null) {
                            try {
                                selectionKey.channel().close();
                            } catch (IOException e1) {
                                e1.printStackTrace();
                            }
                        }
                    }
                }

                if (selectionKey.isReadable()) {
                    mSocketChannel = (SocketChannel) selectionKey.channel();
                    //擷取t2時間
                    ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                    String content = "";
                    try {
                        int readBytes = mSocketChannel.read(byteBuffer);
                        // 寫完就把狀态關注去掉,否則會一直觸發寫事件(改變自身關注事件)
                        selectionKey.interestOps(SelectionKey.OP_READ);
                    } catch (IOException i) {
                        //如果捕獲到該SelectionKey對應的Channel時出現了異常,即表明該Channel對于的Client出現了問題
                        //是以從Selector中取消該SelectionKey的注冊
                        selectionKey.cancel();
                        if (selectionKey.channel() != null) {
                            try {
                                selectionKey.channel().close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
        }
        closeSocket();
    }

    private void closeSocket() {
        if (server != null) {
            try {
                server.socket().close();
                Log.d(TAG, "關閉成功1");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        try {
            if (mSocketChannel != null) {
                mSocketChannel.close();
                Log.d(TAG, "關閉成功2");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            if (mSelector != null) {
                mSelector.close();
                Log.d(TAG, "關閉成功3");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public boolean isWork() {
        return work;
    }

    public void setWork(boolean work) {
        this.work = work;
    }
}
      

Activity的代碼:

package gateway.demo.com.gatewaydemo.activity;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import gateway.demo.com.gatewaydemo.R;
import gateway.demo.com.gatewaydemo.tcpserver.TcpServer;

public class MainActivity extends AppCompatActivity {
    private TcpServer mTcpServer = TcpServer.getInstance();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTcpServer.setWork(true);
        new Thread(mTcpServer).start();
    }

    @Override
    protected void onDestroy() {
        mTcpServer.setWork(false);
        super.onDestroy();
    }
}
      

我是推出activity停止的線程,具體需求你們可以自己設計。

希望能幫助有這個問題的人。