前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >从0到1用java再造tcpip协议栈:代码实现ping应用功能1

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

作者头像
望月从良
发布2022-01-17 16:26:16
3710
发布2022-01-17 16:26:16
举报
文章被收录于专栏:Coding迪斯尼Coding迪斯尼

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

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

代码语言:javascript
复制
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处的应用对象直接调用协议对象该接口来封装发送数据包所需要的包头。接下来我们使用一个类专门用于构造协议头:

代码语言:javascript
复制
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类中,我们依旧使用责任链模式调用相应对象来构造不同的包头:

代码语言:javascript
复制
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包头的类:

代码语言:javascript
复制
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();
   }}

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

代码语言:javascript
复制
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;
   }}

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

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-01-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Coding迪斯尼 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档