1. 程式人生 > >使用java自造TCP/IP協議棧:使用JPCAP實現資料發包

使用java自造TCP/IP協議棧:使用JPCAP實現資料發包

從本節開始,我們打算使用java把tcp/ip網路協議棧重新實現一遍。這是一個不小的野心,自然也是一個不小的工程,好在前面順利完成了作業系統,編譯器兩門課程的實現,這給了我極大的信心。整個網際網路系統分為三大支柱,分別是作業系統,編譯器,和網路協議,我們完成了前面三者,還剩第三者一直孤懸在那,直到今天我終於下定決心,嘗試著把tcp/ip協議棧也重新實現一遍。

對於網路協議棧而言,絕大多數人都是”只見過豬跑,但沒吃過豬肉“,這絕對是一種巨大的遺憾。網路協議棧構思值巧妙,架構之穩定,非常值得任何期望提升自身技術能力,設計能力,全域性思考能力的技術工程師好好把握,把一樣東西從無到有,從0到1重新構建出來,絕對是把握其精髓的最佳方式。

我們要開發的協議棧採取四層架構:

螢幕快照 2018-11-27 下午5.38.41.png

協議棧設計的最巧妙之處在於,上層協議只需要關注自己的資料處理,剩下的問題交由下層協議處理,這種方式將一個相當複雜的系統分而治之,一個複雜度極高的系統級怪獸就被庖丁血牛似的給肢解掉了,在後面的編碼實現中,我們會感受到此種設計模式的巨大威力。

在上面的架構圖中,所有的網路從最頂層開始封包,直到最底部的網絡卡,資料會轉換為電訊號被網絡卡傳送給指定伺服器或路由器,因此要想再造整個協議棧,我們需要一個功能就是直接操作網絡卡進行資料包的傳送和接收,由此我們使用java開發時,需要有辦法直接操作網絡卡硬體,能幫我們完成該任務的,是常有的java抓包發包元件jpcap。

它是一個jar包,我把它傳送到課堂附件,或者大家也可以自行下載。下載到本地後,在eclipse中新建一個專案叫TCPImplementation,當然你也可以任意取自己喜歡的名字,然後在專案目錄上右鍵單擊,選擇property屬性,點選"java build path",然後選擇"Libraires",然後點選”add externel jars",然後從下載的jpcap目錄下,進入lib目錄,然後選擇jpcap.jar,接著進入bin目錄,選取jpcap.bin,完成後配置如下圖:

螢幕快照 2018-11-27 下午5.59.53.png

然後進入jpcap目錄下的src/main/c目錄,如果像我一樣使用macos,那麼就開啟makefil,找到ifeq else部分程式碼,然後在該部分的末尾新增如下程式碼:

ifeq ($(PLATFORM), Darwin)
    JNI_INCLUDE2 = /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.sdk/System/Library/Frameworks/JavaVM.framework/versions/A/Headers/ 
    COMPILE_OPTION = -bundle -framework JavaVM
    SUFFIX = .jnilib
endif

新增完後,在控制檯執行make命令進行編譯,如果使用windows系統,請同學自己百度一下相關配置,編譯好後在目錄下會有libjpcap.jnilib,將該檔案拷貝到目錄/Library/Java/Extensions/,如果不是使用macos的同學需要百度一下相應目錄或者是jpcap的編譯配置方法,最後執行命令sudo chomod 777 /dev/bpf*,如此所有準備工作就完成了。

然後在java工程下新建一個檔案ProtocolEntry.java,然後新增如下程式碼:

import java.io.IOException;

import jpcap.JpcapCaptor;
import jpcap.NetworkInterface;
import jpcap.NetworkInterfaceAddress;
import jpcap.packet.Packet;

public class ProtocolEntry implements PacketReceiver {
	
	public void receivePacket(Packet packet) {
		System.out.println(packet);
		System.out.println("Receive a packet");
	}

	public static void main(String[] args) throws IOException {
		//獲取網絡卡列表
	    NetworkInterface[] devices = JpcapCaptor.getDeviceList();
	    JpcapCaptor captor = null;
	    for (int i = 0; i < devices.length; i++) {
	    	//顯示網絡卡名字
	        System.out.println(i+": "+devices[i].name + "(" + devices[i].description + ")");
	        
	        System.out.println(" datalink: " + devices[i].datalink_name + "(" + devices[i].datalink_description + ")");
	        
	        System.out.println(" Mac Address: ");
	        for (byte b : devices[i].mac_address) {
	            System.out.print(Integer.toHexString(b & 0xff) + ":");
	        }
	        
	        System.out.println();
	        
	        for (NetworkInterfaceAddress a : devices[i].addresses) {
	            System.out.println(" address:" + a.address + " " + a.subnet + " " + a.broadcast);
	        }
	        
	        captor = JpcapCaptor.openDevice(devices[i], 65536, false, 20);
	        if (captor != null) {
	            System.out.println("Open captor on device" + i);
	            break;
	        }
	    }
	    
	   
	}
}

上面程式碼執行後,結果如下圖:

螢幕快照 2018-11-27 下午6.09.42.png

通過jpcap,我們成功訪問了網絡卡硬體,其中"Open captor on device0“,表示我們可以使用名為device0這個網絡卡來實現資料包的傳送和接收,如果你有多塊可用網絡卡,那麼這些網絡卡都可以成功open,後面可以選擇其中某一個來進行資料包的傳送,至此萬里長征,我們成功的走出了第一步!

歡迎關注公眾號,讓我們一起學習,交流,成長:
文章公眾號.jpg