在原先代碼設計中,我們為了友善,喜歡在一個子產品中組織資料包的協定頭,然後将要發送的資料融合在一起,并調用網卡将資料發送出去,這種偷懶的做法将多種邏輯融合在一起。這種做法一旦遇到複雜的資料發送需求時,系統邏輯的複雜性會呈現出爆炸性的增長,最後超出我們的控制範圍。
為了實作體系的階層化,将各種功能剝離成單獨子產品,實作系統的可了解性,我将體系結構改動為以下模式:
從上圖看,所有的應用執行個體,也就是調用網絡協定,實作資料收發功能的應用都繼承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