今天遇到一個問題,就是推出了監聽界面之後,再進來界面還能監聽用戶端連接配接服務端。
做法:
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停止的線程,具體需求你們可以自己設計。
希望能幫助有這個問題的人。