網絡通信之檢測遠端連接是否斷開連接


判斷對方是否斷開連接:

一、方法層面的實現:

  1,使用輸入流的read方法:

    輸入流的read(byte[] ,int ,int) 方法,表示從當前的通道中讀取數據,具體讀取到的數據有返回的int值決定;這里的返回值和拋出的異常很重要,如果拋出IOException異常,很明顯連接已經斷開;

    返回值說明:

    針對於基於tcp/ip協議的socket連接說明:

    如果沒有設置socket的soTimeout屬性,那么該方法將是一個阻塞方法,可以通過設置socket的soTimeout屬性,讓read方法退出。

    注意:read方法如果timeout將以拋出socketTimeoutException異常;

    客戶端:

     如果對方斷開連接,客戶端的read方法將返回-1;

    服務器端:

     如果對方斷開連接,服務器端的read方法將拋出IOException異常;

  提示:

    建議使用這種方式,netty底層源碼就是使用的這種方式實現的;

 

  2,使用socket 的sendUrgentData(int) :

    注意:不建議使用此種方式,因為使用該方式有很多不可預測的情況;

    通常情況是:接收端沒有開啟socket的SO_OOBINLINE屬性,那么使用sendUrgentData(int)測試連接時,在發送17次(windows測試數據)時會發生異常;而如果接收端開啟socket的SO_OOBINLINE屬性,那么接收端就會收到發送的數據,從而導致真實數據的混亂;

    socket sendUrgentData(int)  17次異常說明;

    對於17次發送異常,在一片文章中有看到,說:如果接收端沒有開啟socket的SO_OOBINLINE的屬性(當然這也是想把該方法用於心跳檢測的必須條件),那么接收端將拋棄接收到的這個數據,也不會向發送端返回對應的ack值;但是,發送端卻會占用一個tcp/ip的發送窗口,一直等待接收端的返回,這里肯定等待不到,就會一直占用窗口;而一個tcp/ip基於平台只有8或16個窗口,於是,在第17次發送數據時拋出異常了;

    意思,作者是懂的,但真正的底層實現卻不太清楚;提供tcp/ip窗口詳解連接:

    https://technet.microsoft.com/zh-cn/library/2007.01.cableguy.aspx

 

二、協議層面的實現:

  通過自定義的心跳機制,這也是最常用的方式之一;

 

示例實現:

定義了一個抽象的通道處理類,提供遠端斷開連接的判斷;

import java.io.IOException;
import java.io.InputStream;
import java.net.SocketTimeoutException;

/**
 * 提供基於tcp/ip協議連接的斷開判斷實現
 * @author pineapple food
 *
 */
public abstract class AbstractChannelHandler {

    /*
     * 連接關閉標志
     */
    private boolean closed;
    
    /**
     * 嘗試從輸入流中讀取數據,具體讀取數據的數量,依據返回的int值作為依據;
     * @param buf 緩存區
     * @param index 起始地址
     * @param length 讀取長度
     * @return 值大於零,表示讀取到返回值表示大小的數據;等於0: 表示未讀取到數據; 等於-1:表示遠端已關閉連接;
     */
    public int tryReadMsg(byte[] buf ,int index ,int length) {
        int result = 0;
        
        try {
            result = inputStream().read(buf, index, length);
        } catch (SocketTimeoutException e) {
            result = 0;
        } catch (IOException e) {
            result = -1;
            setClosed();
        }
        
        return result;
    }
    
    protected void setClosed() {
        closed = true;
    }
    
    /**
     * 連接是否關閉
     * @return false 未關閉; true 關閉
     */
    public boolean isClosed() {
        return closed;
    }
    
    /**
     * 向遠端連接發送心跳數據;
     */
    protected void sendHeartbeat() {}
    
    /**
     * 通過協議層的心跳發送,判斷遠端連接是否關閉;
     * @return
     */
    protected boolean isClosedBySendHeartbeat() {
        return false;
    }
    
    /**
     * 設置socket 輸入流的讀取超時時間,用於設置{@link tryReadMsg(byte[] ,int ,int)} 方法的timeout時間
     * 否則將一直阻塞;
     */
    abstract protected void setSoTimeout();
    
    /**
     * 返回與該通道相關聯的輸入流;
     * @return
     */
    abstract protected InputStream inputStream() throws IOException;
}

通道處理類,繼承與上述抽象類,方便斷開連接的判斷

import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.net.SocketException;

public class ChannelHandler extends AbstractChannelHandler{
    
    private Socket socket;
    
    private InputStream is;
    
    public ChannelHandler(Socket socket) {
        this.socket = socket;
        
        setSoTimeout();
    }

    @Override
    protected InputStream inputStream() throws IOException {
        if(is == null) {
            is = socket.getInputStream();
        }
        return is;
    }

    @Override
    protected void setSoTimeout() {
        try {
            socket.setSoTimeout(1000);
        } catch (SocketException e) {
            
        }
    }

}

demo,親測可用:

    static public void test() throws IOException {
        String host = "127.0.0.1";
         
        Socket socket = new Socket();
        socket.connect(new InetSocketAddress(host, 6800));    
        
        ChannelHandler channelHandler = new ChannelHandler(socket);
        
        byte[] b = new byte[20];
        
        while(true) {
            
            int n = channelHandler.tryReadMsg(b, 0, b.length);
            
            if(n == -1 || channelHandler.isClosed()) {
                System.out.println("___________ connection is closed");
                break;
            }
            
            if(n > 0) {
                byte[] data = new byte[n];
                System.arraycopy(b, 0, data, 0, n);
                System.out.println(n + " : receive data = "+new String(data));
            }
        }
    }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM