在原先代码设计中,我们为了方便,喜欢在一个模块中组织数据包的协议头,然后将要发送的数据融合在一起,并调用网卡将数据发送出去,这种偷懒的做法将多种逻辑融合在一起。这种做法一旦遇到复杂的数据发送需求时,系统逻辑的复杂性会呈现出爆炸性的增长,最后超出我们的控制范围。
为了实现体系的层次化,将各种功能剥离成单独模块,实现系统的可理解性,我将体系结构改动为以下模式:
从上图看,所有的应用实例,也就是调用网络协议,实现数据收发功能的应用都继承IApplication接口和继承Application类,其内容如下:
package Application;
import java.util.HashMap;
public interface IApplication {
public int getPort();
public boolean isClosed();
public void handleData(HashMap<String, Object> 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<String, Object> 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<IApplication> application_list = new ArrayList<IApplication>();
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<String, Object> headerInfo);
public HashMap<String, Object> 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<String , byte[] > ipToMacTable = null;
private static HashMap<String, byte[]> 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<String, byte[]>();
dataWaitToSend = new HashMap<String, byte[]>();
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 {
/*
* 发送数据前先检查给定ip的mac地址是否存在,如果没有则先让ARP协议获取mac地址
*/
byte[] mac = ipToMacTable.get(Arrays.toString(ip));
if (mac == null) {
HashMap<String, Object> headerInfo = new HashMap<String, Object>();
headerInfo.put("sender_ip", ip);
byte[] arpRequest = arpLayer.createHeader(headerInfo);
if (arpRequest == null) {
throw new Exception("Get mac adress hea