IP数据包头解析:Java和C语言实现

IP数据包头解析:Java和C语言实现

本代码演示了如何解析IP数据包头,并将其信息输出。代码分别使用Java和C语言实现,并详细解释了每个字段的含义和解析方法。

Java代码

import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        System.out.println('请输入一串IP数据包头:');
        System.out.println();

        Scanner sc = new Scanner(System.in);
        String packet = sc.nextLine().replace(' ', '');
        sc.close();

        // 版本号:取字符串的第一个字符,并将其转换为十进制数。
        int version = Integer.parseInt(packet.substring(0, 1), 16);
        // 包头长度:取字符串的第二个字符(表示包头长度的四分之一),并将其乘以4得到包头长度的字节数。
        int headerLength = Integer.parseInt(packet.substring(1, 2), 16) * 4;

        StringBuilder tmp = new StringBuilder(Integer.toBinaryString(Integer.parseInt(packet.substring(2, 4), 16)));
        int len = tmp.length();
        for (int i = 0; i < 8 - len; i++) {
            tmp.insert(0, '0');
        }

        // 优先权:取字符串的第三个字符的二进制表示的前3位,并将其转换为十进制数。
        int priority = Integer.parseInt(tmp.substring(0, 3), 2);
        // TOS:根据优先权的值确定TOS的具体含义。
        String TOS;
        if (tmp.charAt(3) == '1') {
            TOS = '最小延迟';
        } else if (tmp.charAt(4) == '1') {
            TOS = '最大吞吐量';
        } else if (tmp.charAt(5) == '1') {
            TOS = '最高可靠性';
        } else if (tmp.charAt(6) == '1') {
            TOS = '低成本';
        } else {
            TOS = '一般服务';
        }

        // IP数据报文总长:取字符串的第四到第七个字符,并将其转换为十进制数。
        int packetLength = Integer.parseInt(packet.substring(4, 8), 16);
        // 标志位:取字符串的第八到第十一个字符,并将其转换为十进制数。
        int identification = Integer.parseInt(packet.substring(8, 12), 16);

        tmp = new StringBuilder(Integer.toBinaryString(Integer.parseInt(packet.substring(12, 16), 16)));
        len = tmp.length();
        for (int i = 0; i < 16 - len; i++) {
            tmp.insert(0, '0');
        }
        // 是否分片:根据标志位的第二位判断是否分片。
        String section = tmp.charAt(1) == '0' ? '可以分片' : '不能分片';
        // 包属于:根据标志位的第三位判断包属于哪个分片
        String property = tmp.charAt(2) == '0' ? '最后一个分片的包' : '分片中段的包';
        // 分段偏移:取标志位的第四位到最后一位。
        String fragmentOffset = tmp.substring(3, 16);

        // TTL值:取字符串的第十七和第十八个字符,并将其转换为十进制数。
        int ttl = Integer.parseInt(packet.substring(16, 18), 16);
        // 协议号:取字符串的第十九和第二十个字符,并将其转换为十进制数。然后根据协议号的值,确定协议的名称。
        int protocolCode = Integer.parseInt(packet.substring(18, 20), 16);
        String protocol = '未知';
        for (ProtocolType val : ProtocolType.values()) {
            if (val.getValue() == protocolCode) {
                protocol = val.name();
            }
        }

        tmp = new StringBuilder(Integer.toBinaryString(Integer.parseInt(packet.substring(20, 24), 16)));
        len = tmp.length();
        for (int i = 0; i < 16 - len; i++) {
            tmp.insert(0, '0');
        }
        // 头部校验和:取字符串的第二十一到第二十四个字符
        String checkSum = tmp.toString();
        // 原始地址:将字符串的第二十五到第三十二个字符按照每两个字符转换为十进制数,得到四个数字
        int[] sourceAddress = {
                Integer.parseInt(packet.substring(24, 26), 16),
                Integer.parseInt(packet.substring(26, 28), 16),
                Integer.parseInt(packet.substring(28, 30), 16),
                Integer.parseInt(packet.substring(30, 32), 16)
        };
        // 目标地址:将字符串的第三十三到第四十个字符按照每两个字符转换为十进制数,得到四个数字
        int[] targetAddress = {
                Integer.parseInt(packet.substring(32, 34), 16),
                Integer.parseInt(packet.substring(34, 36), 16),
                Integer.parseInt(packet.substring(36, 38), 16),
                Integer.parseInt(packet.substring(38, 40), 16)
        };

        // 输出包信息
        println('版本号:');
        println(String.valueOf(version));
        System.out.println();

        println('包头长度:');
        println(String.valueOf(headerLength));
        System.out.println();

        println('优先权:');
        println(String.valueOf(priority));
        System.out.println();

        println('TOS:');
        println(TOS);
        System.out.println();

        println('包总长:');
        println(String.valueOf(packetLength));
        System.out.println();

        println('标志位:');
        println(String.valueOf(identification));
        System.out.println();

        println('分片:');
        println(section);
        System.out.println();

        println('包属性:');
        println(property);
        System.out.println();

        println('分段偏移:');
        println(fragmentOffset);
        System.out.println();

        println('TTL:');
        println(String.valueOf(ttl));
        System.out.println();

        println('协议号:');
        println(protocolCode + '(' + protocol + ')');
        System.out.println();

        println('头部校验和:');
        println(checkSum);
        System.out.println();

        println('原始地址:');
        println(sourceAddress[0] + '.' + sourceAddress[1] + '.' + sourceAddress[2] + '.' + sourceAddress[3]);
        System.out.println();

        println('目标地址:');
        println(targetAddress[0] + '.' + targetAddress[1] + '.' + targetAddress[2] + '.' + targetAddress[3]);
        System.out.println();
    }

    static void println(String msg) {
        System.out.printf('%'-15s', msg);
    }

    enum ProtocolType {
        ICMP(1),
        IGMP(2),
        TCP(6),
        UDP(17);

        private final int value;

        ProtocolType(int value) {
            this.value = value;
        }

        public int getValue() {
            return value;
        }
    }
}

C语言代码

#include <stdio.h>

#define MAX_LENGTH 100

void printLine(char *msg) {
    printf('%'-15s', msg);
}

typedef enum {
    ICMP = 1,
    IGMP = 2,
    TCP = 6,
    UDP = 17
} ProtocolType;

int main() {
    printf('请输入一串IP数据包头:

');

    char packet[MAX_LENGTH];
    fgets(packet, MAX_LENGTH, stdin);

    // 版本号:取字符串的第一个字符,并将其转换为十进制数。
    int version = (int)packet[0] - '0';
    // 包头长度:取字符串的第二个字符(表示包头长度的四分之一),并将其乘以4得到包头长度的字节数。
    int headerLength = (int)packet[1] - '0' * 4;

    char tmp[9];
    sprintf(tmp, '%02X', packet[2]);
    // 优先权:取字符串的第三个字符的二进制表示的前3位,并将其转换为十进制数。
    int priority = (int)tmp[0] - '0';
    // TOS:根据优先权的值确定TOS的具体含义。
    char *TOS;
    if (tmp[1] == '1') {
        TOS = '最小延迟';
    } else if (tmp[2] == '1') {
        TOS = '最大吞吐量';
    } else if (tmp[3] == '1') {
        TOS = '最高可靠性';
    } else if (tmp[4] == '1') {
        TOS = '低成本';
    } else {
        TOS = '一般服务';
    }

    // IP数据报文总长:取字符串的第四到第七个字符,并将其转换为十进制数。
    int packetLength = 0;
    sscanf(&packet[4], '%04X', &packetLength);
    // 标志位:取字符串的第八到第十一个字符,并将其转换为十进制数。
    int identification = 0;
    sscanf(&packet[8], '%04X', &identification);

    sprintf(tmp, '%02X', packet[12]);
    // 是否分片:根据标志位的第二位判断是否分片。
    char *section = (tmp[1] == '0') ? '可以分片' : '不能分片';
    // 包属于:根据标志位的第三位判断包属于哪个分片
    char *property = (tmp[2] == '0') ? '最后一个分片的包' : '分片中段的包';
    // 分段偏移:取标志位的第四位到最后一位。
    char fragmentOffset[14];
    sprintf(fragmentOffset, '%s', &tmp[3]);

    // TTL值:取字符串的第十七和第十八个字符,并将其转换为十进制数。
    int ttl = 0;
    sscanf(&packet[16], '%02X', &ttl);
    // 协议号:取字符串的第十九和第二十个字符,并将其转换为十进制数。然后根据协议号的值,确定协议的名称。
    int protocolCode = 0;
    sscanf(&packet[18], '%02X', &protocolCode);
    char *protocol = '未知';
    switch (protocolCode) {
        case ICMP:
            protocol = 'ICMP';
            break;
        case IGMP:
            protocol = 'IGMP';
            break;
        case TCP:
            protocol = 'TCP';
            break;
        case UDP:
            protocol = 'UDP';
            break;
    }

    sprintf(tmp, '%02X', packet[20]);
    // 头部校验和:取字符串的第二十一到第二十四个字符
    char checkSum[17];
    sprintf(checkSum, '%s', tmp);
    // 原始地址:将字符串的第二十五到第三十二个字符按照每两个字符转换为十进制数,得到四个数字
    int sourceAddress[4];
    sscanf(&packet[24], '%02X%02X%02X%02X', &sourceAddress[0], &sourceAddress[1], &sourceAddress[2], &sourceAddress[3]);
    // 目标地址:将字符串的第三十三到第四十个字符按照每两个字符转换为十进制数,得到四个数字
    int targetAddress[4];
    sscanf(&packet[32], '%02X%02X%02X%02X', &targetAddress[0], &targetAddress[1], &targetAddress[2], &targetAddress[3]);

    // 输出包信息
    printLine('版本号:');
    printf('%d

', version);

    printLine('包头长度:');
    printf('%d

', headerLength);

    printLine('优先权:');
    printf('%d

', priority);

    printLine('TOS:');
    printf('%s

', TOS);

    printLine('包总长:');
    printf('%d

', packetLength);

    printLine('标志位:');
    printf('%d

', identification);

    printLine('分片:');
    printf('%s

', section);

    printLine('包属性:');
    printf('%s

', property);

    printLine('分段偏移:');
    printf('%s

', fragmentOffset);

    printLine('TTL:');
    printf('%d

', ttl);

    printLine('协议号:');
    printf('%d(%s)

', protocolCode, protocol);

    printLine('头部校验和:');
    printf('%s

', checkSum);

    printLine('原始地址:');
    printf('%d.%d.%d.%d

', sourceAddress[0], sourceAddress[1], sourceAddress[2], sourceAddress[3]);

    printLine('目标地址:');
    printf('%d.%d.%d.%d

', targetAddress[0], targetAddress[1], targetAddress[2], targetAddress[3]);

    return 0;
}

代码解析

1. 版本号和包头长度

  • 版本号:IP数据包头的第一个字节表示版本号,取值为4,即IPv4。代码中使用 packet[0] 获取第一个字节,并将其转换为十进制数。
  • 包头长度:IP数据包头的第二个字节表示包头长度的四分之一,取值范围为5~15,表示包头长度为20~60字节。代码中使用 packet[1] 获取第二个字节,并将其乘以4得到包头长度的字节数。

2. 优先权和TOS

  • 优先权:IP数据包头的第三个字节表示优先权和TOS,其中前3位表示优先权。代码中使用 sprintf 将第三个字节转换为十六进制字符串,然后使用 tmp[0] 获取第一个字符,并将其转换为十进制数。
  • TOS:IP数据包头的第三个字节表示优先权和TOS,其中后5位表示TOS。代码中使用 tmp[1]~tmp[5] 获取后5位,并根据其值判断TOS的具体含义。

3. 包总长和标志位

  • 包总长:IP数据包头的第四到第七个字节表示包总长,取值范围为20~65535字节。代码中使用 sscanf 将第四到第七个字节转换为十进制数。
  • 标志位:IP数据包头的第八到第十一个字节表示标志位,取值范围为0~65535。代码中使用 sscanf 将第八到第十一个字节转换为十进制数。

4. 是否分片和包属性

  • 是否分片:IP数据包头的第十二个字节表示标志位,其中第2位表示是否分片。代码中使用 tmp[1] 获取第二位,并根据其值判断是否分片。
  • 包属性:IP数据包头的第十二个字节表示标志位,其中第3位表示包属性。代码中使用 tmp[2] 获取第三位,并根据其值判断包属性。

5. 分段偏移

  • 分段偏移:IP数据包头的第十二个字节表示标志位,其中第4~16位表示分段偏移。代码中使用 tmp[3]~tmp[15] 获取分段偏移,并将其转换为字符串。

6. TTL值和协议号

  • TTL值:IP数据包头的第十七和第十八个字节表示TTL值,取值范围为0~255。代码中使用 sscanf 将第十七和第十八个字节转换为十进制数。
  • 协议号:IP数据包头的第十九和第二十个字节表示协议号。代码中使用 sscanf 将第十九和第二十个字节转换为十进制数,然后根据协议号的值,确定协议的名称。

7. 头部校验和

  • 头部校验和:IP数据包头的第二十一到第二十四个字节表示头部校验和。代码中使用 sprintf 将第二十一到第二十四个字节转换为字符串。

8. 原始地址和目标地址

  • 原始地址:IP数据包头的第二十五到第三十二个字节表示原始地址。代码中使用 sscanf 将第二十五到第三十二个字节转换为四个十进制数。
  • 目标地址:IP数据包头的第三十三到第四十个字节表示目标地址。代码中使用 sscanf 将第三十三到第四十个字节转换为四个十进制数。

总结

本代码演示了如何解析IP数据包头,并将其信息输出。代码分别使用Java和C语言实现,并详细解释了每个字段的含义和解析方法。通过学习本代码,可以更好地理解IP协议的结构和工作原理。

标签: 常规


原文地址: https://cveoy.top/t/topic/fkPd 著作权归作者所有。请勿转载和采集!