天天看點

tcpip協定_從0到1用java再造tcpip協定棧:架構重建,完整實作ping應用

在原先代碼設計中,我們為了友善,喜歡在一個子產品中組織資料包的協定頭,然後将要發送的資料融合在一起,并調用網卡将資料發送出去,這種偷懶的做法将多種邏輯融合在一起。這種做法一旦遇到複雜的資料發送需求時,系統邏輯的複雜性會呈現出爆炸性的增長,最後超出我們的控制範圍。

為了實作體系的階層化,将各種功能剝離成單獨子產品,實作系統的可了解性,我将體系結構改動為以下模式:

tcpip協定_從0到1用java再造tcpip協定棧:架構重建,完整實作ping應用

從上圖看,所有的應用執行個體,也就是調用網絡協定,實作資料收發功能的應用都繼承IApplication接口和繼承Application類,其内容如下:

package Application;import java.util.HashMap;public interface IApplication { public int getPort(); public boolean isClosed();  public void handleData(HashMap data);}package Application;import java.util.HashMap;public class Application implements IApplication{ protected int port = 0; private boolean closed = false;  public Application() { ApplicationManager manager = ApplicationManager.getInstance(); manager.addApplication(this); }  @Override public int getPort() { return port; } @Override public void handleData(HashMap data) { // TODO Auto-generated method stub  } @Override public boolean isClosed() {  return closed; }}
           

所有應用對象都要導出getPort()接口,每個port對應唯一一個應用對象,如果資料包到達後,協定會根據port尋找應該接受資料的應用對象。應用對象全部接受ApplicationManager的管理,當網絡協定部分有資料需要送出給對應的應用時,需要通過ApplicationManager查詢相應應用對象,它的代碼如下:

package Application;import java.util.ArrayList;public class ApplicationManager { private static ArrayList application_list = new ArrayList(); private static ApplicationManager instance = null;  private ApplicationManager() {  }  public static ApplicationManager getInstance() { if (instance == null) { instance = new ApplicationManager(); }  return instance; }  public static void addApplication(IApplication app) { application_list.add(app); } 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; }}
           

實作網絡協定的子產品單獨形成一個獨立部分,實作具體網絡協定的對象都繼承統一的接口IProtocol:

package protocol;import java.util.HashMap;import jpcap.packet.Packet;public interface IProtocol { public byte[] createHeader(HashMap headerInfo); public HashMap handlePacket(Packet packet);}
           

所有協定對象都接受ProtocolManager的統一管理,當應用對象需要調用某個協定對象建立標頭時,需要經過ProtocolManager擷取相應對象,同時它是唯一一個從網卡接收資料的對象,當網卡把資料包傳遞給它後,它通過解析網絡包的以太太標頭,決定把資料包轉交給對應的網絡協定對象解析,它的代碼如下:

package protocol;

import java.util.Arrays;

import java.util.HashMap;

import Application.ApplicationManager;

import Application.IApplication;

import datalinklayer.DataLinkLayer;

import jpcap.PacketReceiver;

import jpcap.packet.EthernetPacket;

import jpcap.packet.IPPacket;

import jpcap.packet.Packet;

public class ProtocolManager implements PacketReceiver{

private static ProtocolManager instance = null;

private static ARPProtocolLayer arpLayer = null;

private static DataLinkLayer dataLinkInstance = null;

private static HashMap ipToMacTable = null;

private static HashMap dataWaitToSend = null;

private static byte[] broadcast=new byte[]{(byte)255,(byte)255,(byte)255,(byte)255,(byte)255,(byte)255};

private ProtocolManager() {}

public static ProtocolManager getInstance() {

if (instance == null) {

instance = new ProtocolManager();

dataLinkInstance = DataLinkLayer.getInstance();

ipToMacTable = new HashMap();

dataWaitToSend = new HashMap();

dataLinkInstance.registerPacketReceiver(instance);

arpLayer = new ARPProtocolLayer();

}

return instance;

}

public IProtocol getProtocol(String name) {

switch (name.toLowerCase()) {

case "icmp":

return new ICMPProtocolLayer();

case "ip":

return new IPProtocolLayer();

}

return null;

}

public void sendData(byte[] data, byte[] ip) throws Exception {

byte[] mac = ipToMacTable.get(Arrays.toString(ip));

if (mac == null) {

HashMap headerInfo = new HashMap();

headerInfo.put("sender_ip