美文网首页Java 杂谈
从0到1用java再造tcpip协议栈:代码实现ping应用功能

从0到1用java再造tcpip协议栈:代码实现ping应用功能

作者: 望月从良 | 来源:发表于2019-01-16 15:19 被阅读17次

上一节我们讲解了基于ICMP echo协议的ping原理,并提出下图的代码实现架构:

1.png

我们将遵照上面架构实现代码,首先为protocol后面的所有协议对象增加一个接口:

package protocol;

import java.util.HashMap;

public interface IProtocol {
    public byte[] createHeader(HashMap<String, byte[]> headerInfo);
}

package protocol;

public class ProtocolManager {
    private static ProtocolManager instance = null;
    private ProtocolManager() {}
    public static ProtocolManager getInstance() {
        if (instance == null) {
            instance = new ProtocolManager();
        }
        
        return instance;
    }
    
    public IProtocol getProtocol(String name) {
        switch (name.toLowerCase()) {
        case "icmp":
            return new ICMPProtocolLayer();
        case "ip":
            return new IPProtocolLayer();
        }
        
        return null;
    }
}

所有协议对象必须继承上面接口,处于Application处的应用对象直接调用协议对象该接口来封装发送数据包所需要的包头。接下来我们使用一个类专门用于构造协议头:

package protocol;

import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Random;

import utils.Utility;

public class ICMPEchoHeader implements IProtocol{
    private static int ICMP_EOCH_HEADER_LENGTH = 16;
    private static short ICMP_ECHO_TYPE = 8;
    private static short ICMP_ECHO_REPLY_TYPE = 0;

    @Override
    public byte[] createHeader(HashMap<String, Object> headerInfo) {
        String headerName = (String)headerInfo.get("header");
        if (headerName != "echo" && headerName != "echo_reply") {
            return null;
        }
        
        byte[] buffer = new byte[ICMP_EOCH_HEADER_LENGTH];
        ByteBuffer byteBuffer = ByteBuffer.wrap(buffer);
        
        short type = ICMP_ECHO_TYPE;
        if (headerName == "echo_reply") {
            type = ICMP_ECHO_REPLY_TYPE;
        }
        byteBuffer.putShort(type);
        short code = 0;
        byteBuffer.putShort(code);
        
        short checkSum = 0;
        byteBuffer.putShort(checkSum);
        
        short identifier = 0;
        if (headerInfo.get("identifier") == null) {
            Random ran = new Random();
            identifier = (short) ran.nextInt();
            headerInfo.put("identifier", identifier);
        }
        identifier = (short) headerInfo.get("identifier");
        byteBuffer.putShort(identifier);
        
        short sequenceNumber = 0;
        if (headerInfo.get("sequence_number") != null) {
            sequenceNumber = (short) headerInfo.get("sequence_number");
            sequenceNumber += 1;
        }
        headerInfo.put("sequence_number", sequenceNumber);
        byteBuffer.putShort(sequenceNumber);
        
        checkSum = (short) Utility.checksum(byteBuffer.array(), byteBuffer.array().length);
        byteBuffer.putShort(4, checkSum);       
        
        return byteBuffer.array();
    }

}

在ICMPProtocolLayer类中,我们依旧使用责任链模式调用相应对象来构造不同的包头:

public class ICMPProtocolLayer implements PacketReceiver, IProtocol{
....
 private ArrayList<IProtocol> protocol_header_list = new ArrayList<IProtocol>();
 public ICMPProtocolLayer() {
        //添加错误消息处理对象
        error_handler_list.add(new ICMPUnReachableMsgHandler());
        //增加icmp echo 协议包头创建对象
        protocol_header_list.add(new ICMPEchoHeader());
    }
....
  public byte[] createHeader(HashMap<String, Object> headerInfo) {
        for (int i = 0; i < protocol_header_list.size(); i++) {
            byte[] buff = protocol_header_list.get(i).createHeader(headerInfo);
            if (buff != null) {
                return buff;
            }
        }
        
        return null;
    }
}

由于发送ICMP echo数据包依然需要IP包头,因此我们先构建一个产生IP包头的类:

package protocol;

import java.nio.ByteBuffer;
import java.util.HashMap;

import utils.Utility;

public class IPProtocolLayer implements IProtocol{
    private static byte IP_VERSION = 4;
    private static int CHECKSUM_OFFSET = 10;
    
    @Override
    public byte[] createHeader(HashMap<String, Object> headerInfo) {
        byte version = IP_VERSION;
        byte internetHeaderLength = 5;
        if (headerInfo.get("internet_header_length") != null) {
            internetHeaderLength = (byte)headerInfo.get("internet_header_length");
        }
        byte[] buffer = new byte[internetHeaderLength];
        ByteBuffer byteBuffer = ByteBuffer.wrap(buffer);
        byteBuffer.put((byte) (internetHeaderLength << 4 | version));
        
        byte dscp = 0;
        if (headerInfo.get("dscp") != null) {
            dscp = (byte)headerInfo.get("dscp");
        }
        byte ecn = 0;
        if (headerInfo.get("ecn") != null) {
            ecn = (byte)headerInfo.get("ecn");
        }
        byteBuffer.put((byte)(dscp | ecn << 6));
        
        if (headerInfo.get("total_length") == null) {
            return null;
        }
        
        short totalLength = (short)headerInfo.get("total_length");
        byteBuffer.putShort(totalLength);
        
        int identification = 0;
        if (headerInfo.get("identification") != null) {
            identification = (int)headerInfo.get("identification");
        }
        byteBuffer.putInt(identification);
        
        short flagAndOffset = 0;
        if (headerInfo.get("flag") != null) {
            flagAndOffset = (short)headerInfo.get("flag");
        }
        if (headerInfo.get("fragment_offset") != null) {
            flagAndOffset |= ((short)headerInfo.get("fragment_offset")) << 3;
        }
        byteBuffer.putShort(flagAndOffset);
        
        short timeToLive = 64;
        if (headerInfo.get("time_to_live") != null) {
            timeToLive = (short)headerInfo.get("time_to_live");
        }
        byteBuffer.putShort(timeToLive);
        
        short protocol = 0;
        if (headerInfo.get("protocol") == null) {
            return null;
        }
        protocol = (short)headerInfo.get("protocol");
        byteBuffer.putShort(protocol);
        
        short checkSum = 0;
        byteBuffer.putShort(checkSum);
        
        int srcIP = 0;
        if (headerInfo.get("source_ip") == null) {
            return null;
        }
        srcIP = (int)headerInfo.get("source_ip");
        byteBuffer.putInt(srcIP);
        
        int destIP = 0;
        if (headerInfo.get("destination_ip") == null) {
            return null;
        }
        byteBuffer.putInt(destIP);
        
        if (headerInfo.get("options") != null) {
            byte[] options = (byte[])headerInfo.get("options");
            byteBuffer.put(options);
        }
        
        checkSum = (short) Utility.checksum(byteBuffer.array(), byteBuffer.array().length);
        byteBuffer.putShort(CHECKSUM_OFFSET, checkSum);
         
        return byteBuffer.array();
    }

}

接着我们构造应用程序管理对象,它将用于管理各个应用程序:

package Application;

public interface IApplication {
    public  int getPort();
    public boolean isClosed(); 
    public  void handleData(byte[] data);
}

package Application;

public interface IApplicationManager {
    public  IApplication getApplicationByPort(int port);
}

package Application;

import java.util.ArrayList;

public class ApplicationManager implements IApplicationManager{
    private ArrayList<IApplication> application_list = new ArrayList<IApplication>();
    
    @Override
    public IApplication getApplicationByPort(int port) {
        for (int i = 0; i < application_list.size(); i++) {
            IApplication app = application_list.get(i);
            if (app.getPort() == port) {
                return app;
            }
        }
        
        return null;
    }

}

在下一小节,我们会继续完善代码。

更详细的讲解和代码调试演示过程,请点击链接

更多技术信息,包括操作系统,编译器,面试算法,机器学习,人工智能,请关照我的公众号:


这里写图片描述

相关文章

网友评论

    本文标题:从0到1用java再造tcpip协议栈:代码实现ping应用功能

    本文链接:https://www.haomeiwen.com/subject/rznxdqtx.html