最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java語言獲取TCP流的實現(xiàn)步驟

 更新時間:2023年11月29日 08:30:42   作者:半夏之沫  
使用Wireshark分析網(wǎng)絡(luò)包時,一個很常用的功能就是選中一個TCP報文,然后查看這個TCP報文的TCP流,從而可以進(jìn)一步分析建連是否慢了,斷連是否正常等情況,那么本文就TCP流的概念以及在Java中如何獲取,做一個簡單的學(xué)習(xí),需要的朋友可以參考下

正文

一. TCP流概念

如果去搜索引擎搜索:什么是TCP流,那么大概率是很難得到一個有效的答案的,而在Wireshark中,選中一個TCP報文并單擊右鍵時,在菜單的追蹤流中可以選擇到TCP流這個功能,如下所示。

當(dāng)點擊TCP流后,Wireshark會把選中的TCP報文對應(yīng)的TCP連接的所有TCP報文過濾出來并順序展示,那么這里就知道了,TCP流就是一次TCP連接中,從連接建立,到數(shù)據(jù)傳輸,再到連接斷開整個過程中的TCP報文集合。

那么Wireshark憑什么可以從那么多TCP報文中,精確的把某一條TCP連接的TCP報文過濾出來并順序展示呢,其實就是基于TCP報文的序列號和確認(rèn)號。下面是TCP報文頭的格式。

可以看到每個TCP報文都有一個序列號SeqNum和確認(rèn)號AckNum,并且他們的含義如下。

  • 序列號:表示本次傳輸?shù)臄?shù)據(jù)的起始字節(jié)在整個TCP連接傳輸?shù)淖止?jié)流中的編號。舉個例子,某個TCP報文的SeqNum為500,然后報文長度length為100,則表示本次傳輸數(shù)據(jù)的起始字節(jié)在整個TCP流中的序列號為100,并且本次傳輸?shù)臄?shù)據(jù)的序列號范圍是500到599,根據(jù)序列號,能夠?qū)鬏數(shù)臄?shù)據(jù)有序的排列組合起來,以解決網(wǎng)絡(luò)傳輸中的數(shù)據(jù)亂序問題;
  • 確認(rèn)號:用來告訴對端本端期望下一次收到的數(shù)據(jù)的序列號,換言之,告訴對端本端已經(jīng)正常接收了序列號等于AckNum之前的所有數(shù)據(jù),根據(jù)確認(rèn)號,可以解決網(wǎng)絡(luò)傳輸中的數(shù)據(jù)丟包問題。

那么序列號和確認(rèn)號的變化有什么規(guī)則呢,規(guī)則總結(jié)如下。

  • 本次發(fā)送報文的SeqNum等于上一次發(fā)送報文的SeqNum加上上一次發(fā)送報文的length
  • 本次發(fā)送報文的AckNum等于上一次接收報文的SeqNum加上上一次接收報文的length;
  • SYN報文和FIN報文的length默認(rèn)為1,而不是0。

結(jié)合下面一張圖,可以更好的理解上面的變化規(guī)則。

二. TCP流獲取的Java實現(xiàn)

結(jié)合第一節(jié)的內(nèi)容,想要獲取某一個TCP報文所屬TCP連接的TCP流,其實就可以根據(jù)這個報文的SeqNumAckNum,向前和向后查找符合序列號和確認(rèn)號變化規(guī)則的報文,只要符合規(guī)則,那么這個報文就是屬于TCP流的。

Java語言中,要實現(xiàn)TCP流的獲取,可以先借助io.pkts工具把網(wǎng)絡(luò)包先解開,然后把每個報文封裝為我們自定義的Entityio.pkts工具包解開后的報文對象不太易用),最后就是根據(jù)序列號和確認(rèn)號的變化規(guī)則,來得到某一個報文所屬的TCP流。

現(xiàn)在進(jìn)行實操,先引入io.pkts工具的依賴,如下所示。

<dependency>
    <groupId>io.pkts</groupId>
    <artifactId>pkts-streams</artifactId>
    <version>3.0.10</version>
</dependency>
<dependency>
    <groupId>io.pkts</groupId>
    <artifactId>pkts-core</artifactId>
    <version>3.0.10</version>
</dependency>

同時自定義一個TCP報文的Entity,如下所示。

/**
 * TCP報文Entity。
 */
@Getter
@Setter
@AllArgsConstructor
public class TcpPackage {

    /**
     * 源地址IP。
     */
    private String sourceIp;
    /**
     * 源地址端口。
     */
    private int sourcePort;
    /**
     * 目的地址IP。
     */
    private String destinationIp;
    /**
     * 目的地址端口。
     */
    private int destinationPort;
    /**
     * 報文載荷長度。
     */
    private int length;
    /**
     * ACK報文標(biāo)識。
     */
    private boolean ack;
    /**
     * FIN報文標(biāo)識,
     */
    private boolean fin;
    /**
     * SYN報文標(biāo)識。
     */
    private boolean syn;
    /**
     * RST報文標(biāo)識。
     */
    private boolean rst;
    /**
     * 序列號。
     */
    private long seqNum;
    /**
     * 確認(rèn)號。
     */
    private long ackNum;
    /**
     * 報文到達(dá)時間戳。
     */
    private long arriveTimestamp;
    /**
     * 報文體。
     */
    private String body;

}

現(xiàn)在假設(shè)已經(jīng)拿到了網(wǎng)絡(luò)包對應(yīng)的MultipartFile,下面給出基于io.pkts工具解析網(wǎng)絡(luò)包的實現(xiàn),如下所示。

public static List<TcpPackage> parseTcpPackagesFromFile(MultipartFile multipartFile) {
    List<TcpPackage> tcpPackages = new ArrayList<>();
    try (InputStream inputStream = multipartFile.getInputStream()) {
        GZIPInputStream gzipInputStream = new GZIPInputStream(inputStream);
        Pcap pcap = Pcap.openStream(gzipInputStream);
        pcap.loop(packet -> {
            if (packet.hasProtocol(Protocol.TCP)) {
                TCPPacket tcpPacket = (TCPPacket) packet.getPacket(Protocol.TCP);
                tcpPackages.add(convertTcpPacket2TcpPackage(tcpPacket));
            }
            return true;
        });
        return tcpPackages;
    } catch (Exception e) {
        String message = "從網(wǎng)絡(luò)包解析TCP報文失敗";
        log.error(message);
        throw new RuntimeException(message, e);
    }
}

上述實現(xiàn)中,假定網(wǎng)絡(luò)包是gzip的壓縮格式,所以使用了GZIPInputStream來包裝網(wǎng)絡(luò)包文件的輸入流,同時因為我們要獲取的是TCP流,所以我們只處理有TCP協(xié)議的報文,并會在convertTcpPacket2TcpPackage() 方法中完成到TcpPackage結(jié)構(gòu)的轉(zhuǎn)換,convertTcpPacket2TcpPackage() 方法實現(xiàn)如下所示。

public static TcpPackage convertTcpPacket2TcpPackage(TCPPacket tcpPacket) {
    // 報文長度=IP報文長度-IP報文頭長度-TCP報文頭長度
    IPPacket ipPacket = tcpPacket.getParentPacket();
    int length = ipPacket.getTotalIPLength() - ipPacket.getHeaderLength() - tcpPacket.getHeaderLength();
    Buffer bodyBuffer = tcpPacket.getPayload();
    String body = ObjectUtils.isNotEmpty(bodyBuffer)
            ? bodyBuffer.toString() : StringUtils.EMPTY;
    long arriveTimestamp = tcpPacket.getArrivalTime() / 1000;
    return new TcpPackage(ipPacket.getSourceIP(), tcpPacket.getSourcePort(), ipPacket.getDestinationIP(), tcpPacket.getDestinationPort(),
            length, tcpPacket.isACK(), tcpPacket.isFIN(), tcpPacket.isSYN(), tcpPacket.isRST(), tcpPacket.getSequenceNumber(),
            tcpPacket.getAcknowledgementNumber(), arriveTimestamp, body);
}

上述方法需要注意的一點就是TCP報文載荷長度的獲取,我們能夠拿到的數(shù)據(jù)是IP報文長度,IP報文頭長度和TCP報文頭長度,所以IP報文長度減去IP報文頭長度可以得到TCP報文長度,再拿TCP報文長度減去TCP報文頭長度就能得到TCP報文載荷長度。

現(xiàn)在我們已經(jīng)拿到網(wǎng)絡(luò)包里面所有TCP報文的集合了,并且這些報文是按照時間先后順序進(jìn)行正序排序的,我們隨機選中一個報文,拿到這個TCP報文以及其在集合中的索引,然后我們就可以基于下面的實現(xiàn)拿到對應(yīng)的TCP流。

public static List<TcpPackage> getTcpStream(List<TcpPackage> tcpPackages, int index) {
    LinkedList<TcpPackage> tcpStream = new LinkedList<>();
    TcpPackage beginTcpPackage = tcpPackages.get(index);
    long currentSeqNum = beginTcpPackage.getSeqNum();
    long currentAckNum = beginTcpPackage.getAckNum();
    // 從index位置向前查找
    for (int i = index - 1; i >=0; i--) {
        TcpPackage previousTcpPackage = tcpPackages.get(i);
        long previousSeqNum = previousTcpPackage.getSeqNum();
        long previousAckNum = previousTcpPackage.getAckNum();
        if (isPreviousTcpPackageSatisfied(currentSeqNum, currentAckNum, previousSeqNum, previousAckNum)) {
            tcpStream.addFirst(previousTcpPackage);
            currentSeqNum = previousSeqNum;
            currentAckNum = previousAckNum;
        }
    }
    // index位置的報文也要放到tcp流中
    tcpStream.add(beginTcpPackage);
    currentSeqNum = beginTcpPackage.getSeqNum();
    currentAckNum = beginTcpPackage.getAckNum();
    // 從index位置向后查找
    for (int i = index + 1; i < tcpPackages.size(); i++) {
        TcpPackage nextTcpPackage = tcpPackages.get(i);
        long nextSeqNum = nextTcpPackage.getSeqNum();
        long nextAckNum = nextTcpPackage.getAckNum();
        if (isNextTcpPackageSatisfied(currentSeqNum, currentAckNum, nextSeqNum, nextAckNum)) {
            tcpStream.add(nextTcpPackage);
            currentSeqNum = nextSeqNum;
            currentAckNum = nextAckNum;
        }
    }
    return tcpStream;
}

上述方法中,向前查找時判斷TCP報文是否屬于TCP流是基于isPreviousTcpPackageSatisfied() 方法,向后查找時判斷TCP報文是否屬于TCP流是基于isNextTcpPackageSatisfied() 方法,而這兩個方法其實就是把序列號和確認(rèn)號的變化規(guī)則翻譯成了代碼,如下所示。

public static boolean isPreviousTcpPackageSatisfied(long currentSeqNum, long currentAckNum,
                                                    long previousSeqNum, long previousAckNum) {
    boolean condition1 = currentSeqNum == previousSeqNum && currentSeqNum != 0;
    boolean condition2 = currentAckNum == previousAckNum && currentAckNum != 0;
    boolean condition3 = currentSeqNum == previousAckNum;
    boolean condition4 = currentAckNum - 1 == previousSeqNum;
    return condition1 || condition2 || condition3 || condition4;
}

public static boolean isNextTcpPackageSatisfied(long currentSeqNum, long currentAckNum,
                                                long nextSeqNum, long nextAckNum) {
    boolean condition1 = currentSeqNum == nextSeqNum && currentSeqNum != 0;
    boolean condition2 = currentAckNum == nextAckNum && currentAckNum != 0;
    boolean condition3 = currentAckNum == nextSeqNum;
    boolean condition4 = currentSeqNum + 1 == nextAckNum;
    return condition1 || condition2 || condition3 || condition4;
}

至此,使用Java語言如何從網(wǎng)絡(luò)包中獲得TCP流就介紹完畢。

總結(jié)

TCP流就是一次TCP連接中,從連接建立,到數(shù)據(jù)傳輸,再到連接斷開整個過程中的TCP報文集合,而獲取TCP流是基于TCP報文序列號和確認(rèn)號的變化規(guī)則,規(guī)則如下。

  • 本次發(fā)送報文的SeqNum等于上一次發(fā)送報文的SeqNum加上上一次發(fā)送報文的length
  • 本次發(fā)送報文的AckNum等于上一次接收報文的SeqNum加上上一次接收報文的length;
  • SYN報文和FIN報文的length默認(rèn)為1,而不是0。

使用Java語言解析網(wǎng)絡(luò)包并得到TCP流,步驟總結(jié)如下。

  • 使用io.pkts工具解開網(wǎng)絡(luò)包;
  • 將網(wǎng)絡(luò)包中的TCP報文轉(zhuǎn)換為自定義的可讀性更強的數(shù)據(jù)結(jié)構(gòu);
  • 選中一個TCP報文;
  • 根據(jù)序列號和確認(rèn)號變化獲取TCP流。

以上就是Java語言獲取TCP流的實現(xiàn)步驟的詳細(xì)內(nèi)容,更多關(guān)于Java獲取TCP流的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Spring使用@Filter注解創(chuàng)建自定義過濾器

    Spring使用@Filter注解創(chuàng)建自定義過濾器

    Spring 中鮮為人知但非常有用的注解之一是 @Filter,它支持自定義過濾器,下面我們就來深入研究一下如何使用 Spring 的 @Filter 注解來創(chuàng)建自定義過濾器吧
    2023-11-11
  • java讀取zip/jar包中文件的幾種方式

    java讀取zip/jar包中文件的幾種方式

    這篇文章主要給大家介紹了關(guān)于java讀取zip/jar包中文件的幾種方式,在我們?nèi)粘J褂弥袎嚎s文件是非常常用的,文中通過示例代碼將java讀取zip/jar包中文件的方法介紹的非常詳細(xì),需要的朋友可以參考下
    2023-07-07
  • Java整合騰訊云短信發(fā)送實例代碼

    Java整合騰訊云短信發(fā)送實例代碼

    大家好,本篇文章主要講的是Java整合騰訊云短信發(fā)送實例代碼,感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下,方便下次瀏覽
    2021-12-12
  • 存儲過程創(chuàng)建及springboot代碼調(diào)用存儲過程方式

    存儲過程創(chuàng)建及springboot代碼調(diào)用存儲過程方式

    文章介紹了如何在Navicat中創(chuàng)建存儲過程,并在Spring Boot項目中調(diào)用存儲過程,存儲過程創(chuàng)建步驟包括選擇函數(shù)類型、自定義函數(shù)名、添加參數(shù)等,在Spring Boot中調(diào)用存儲過程時,可以通過JdbcTemplate或MyBatis等工具進(jìn)行
    2024-11-11
  • 利用Mybatis?Plus實現(xiàn)一個SQL攔截器

    利用Mybatis?Plus實現(xiàn)一個SQL攔截器

    SQL攔截器是一種用于攔截和修改Mybatis執(zhí)行的SQL語句的工具,通過使用SQL攔截器,開發(fā)人員可以在執(zhí)行SQL語句之前或之后對其進(jìn)行修改或記錄,本文就來借助一下Mybatis-Plus實現(xiàn)一個SQL攔截器吧
    2023-05-05
  • 輕松學(xué)會使用JavaMail?API發(fā)送郵件

    輕松學(xué)會使用JavaMail?API發(fā)送郵件

    想要輕松學(xué)會使用JavaMail?API發(fā)送郵件嗎?本指南將帶你快速掌握這一技能,讓你能夠輕松發(fā)送電子郵件,無論是個人還是工作需求,跟著我們的步驟,很快你就可以在Java應(yīng)用程序中自如地處理郵件通信了!
    2023-12-12
  • 一文詳解Java中的類加載機制

    一文詳解Java中的類加載機制

    Java虛擬機把描述類的數(shù)據(jù)從Class文件加載到內(nèi)存,并對數(shù)據(jù)進(jìn)行校驗、轉(zhuǎn)換解析和初始化,最終形成可以被虛擬機直接使用的Java類型,這個過程被稱作虛擬機的類加載機制。本文將詳解Java的類加載機制,需要的可以參考一下
    2022-05-05
  • 數(shù)組重排序(如何將所有奇數(shù)都放在所有偶數(shù)前面)的深入分析

    數(shù)組重排序(如何將所有奇數(shù)都放在所有偶數(shù)前面)的深入分析

    本篇文章是對數(shù)組重排序(如何將所有奇數(shù)都放在所有偶數(shù)前面)的方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-06-06
  • Spring Boot集成sa-token的項目實踐

    Spring Boot集成sa-token的項目實踐

    本文主要介紹了Spring Boot集成sa-token的項目實踐,實現(xiàn)了基本的登錄和權(quán)限控制功能,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-05-05
  • Java高效讀取CSV文件的多種方法與分步實例

    Java高效讀取CSV文件的多種方法與分步實例

    在當(dāng)今數(shù)據(jù)驅(qū)動的世界中,CSV文件作為一種輕量級,通用的數(shù)據(jù)交換格式,本文將深入探討多種Java讀取CSV文件的方法,幫助開發(fā)者在不同場景下選擇最合適的解決方案,希望對大家有所幫助
    2025-10-10

最新評論

旌德县| 黑龙江省| 镇沅| 安岳县| 留坝县| 大名县| 雷波县| 新郑市| 额敏县| 甘洛县| 江陵县| 延长县| 屏边| 屯昌县| 富裕县| 调兵山市| 舟曲县| 津市市| 佛学| 运城市| 策勒县| 两当县| 磴口县| 靖州| 凤山市| 乌鲁木齐县| 镇赉县| 温州市| 雷州市| 庆元县| 达孜县| 蓬溪县| 古丈县| 建湖县| 万全县| 耿马| 樟树市| 铁岭市| 隆子县| 安图县| 无棣县|