JAVA網絡通訊底層調用LINUX探究

 

前言:該博客花了我一個下午得心血,所有手打,路過給個贊,拒絕抄襲!!!!!!!!!!!!!!!!!!!!!!!!!

簡單的SOCKET通訊程序

先從一段簡單的JAVA程序性開始寫起,這裏咱們才用半雙工的形式,這裏的半雙工意思是客戶端能夠給服務端發送數據,發完數據就關閉,而服務端能夠一直接受數據java

咱們使用多線程方式,這個不重要數組

下面是線程類網絡

 1 public class SocketThread implements Runnable {
 2     public SocketThread(Socket socket) {
 3         this.socket = socket;
 4     }
 5 
 6     private Socket socket;
 7     @Override
 8     public void run() {
 9         InputStream inputStream = null;
10         InputStreamReader inputStreamReader = null;
11         BufferedReader bufferedReader = null;
12         try {
13             inputStream = socket.getInputStream();
14             inputStreamReader = new InputStreamReader(inputStream);
15             bufferedReader = new BufferedReader(inputStreamReader);
16             char buf[] = new char[1024];
17             int len=0;
18             while ((len=bufferedReader.read(buf))!=-1){
19                 for(int i=0;i<len;i++){
20                     System.out.print(buf[i]);
21                 }
22                 System.out.println();
23             }
24         }catch (Exception e){
25             e.printStackTrace();
26         }finally {
27             try {
28                 inputStream.close();
29                 inputStreamReader.close();
30                 bufferedReader.close();
31             } catch (IOException e) {
32                 e.printStackTrace();
33             }
34         }
35     }
36 }

說實話,這個博客園的博客醜的一B,爲何不能提交CSDN的博客呢?多線程

服務端代碼:socket

 1 public class Server {
 2     public static void main(String[] args) {
 3         try{
 4             ServerSocket serverSocket = new ServerSocket(8888);
 5             System.out.println("服務端已啓動,等待鏈接");
 6             while(true){
 7                 Socket socket = serverSocket.accept();
 8                 SocketThread socketThread = new SocketThread(socket);
 9                 new Thread(socketThread).start();
10             }
11         }catch (Exception e){
12             e.printStackTrace();
13         }
14     }
15 }

客戶端代碼:ide

 1 public class Client {
 2     public static void main(String[] args) {
 3         try {
 4             Socket socket = new Socket("localhost",8888);
 5             OutputStream outputStream = socket.getOutputStream();
 6             Scanner scan = new Scanner(System.in);
 7             while(scan.hasNext()){
 8                 String str = scan.next();
 9                 outputStream.write(str.getBytes());
10                 outputStream.flush();
11             }
12         }catch (Exception e){
13             e.printStackTrace();
14         }
15     }
16 }

分析

想要搞明白JAVA和LINUX之間的關係,首先你得明白咱們的java代碼編譯成class文件後是運行在JVM上,這個CLASS字節碼文件只有咱們JVM認識,你扔給操做系統,操做系統鐵定不懂。函數

JVM也是一個軟件,它是由C++和C編寫的,咱們姑且認爲它是C編寫的,相信你們都看過JAVA源碼,太多方法都是調用了native方法,好比咱們的bind()方法測試

 

 

 

 

 

其次,JAVA的不少功能都是經過調用操做系統函數來實現的,好比顯示文字this

JAVA好比創建Socket是經過告訴虛擬機這個指令,而後虛擬機調用操做系統的Socket()函數來實現的spa

 

 

 

 

 查看一下LINUX的SOCKET函數

簡單說一下,第一個參數是域類型,看到IPV4選它就完事了,第二個參數是協議,咱們用SOCK_STRAM表示TCP協議,第三個取0,0會把前兩個參數對應起來,寫0就完事了

return value是返回一個文件描述符,這個很難明白,你能夠把它理解爲咱們JAVA裏面的對象,JAVA當中有句話叫一切皆對象,而在LINUX中相應的咱們叫作一切皆文件

只要咱們拿到這個文件描述符,就能操做相應的Socket,牛逼吧= =

好的,下面講一下驗證思路

咱們寫一個JAVA方法,這個方法也調用NATVIE方法,而這個方法就不是JVM提供的C了,而是咱們本身寫的一個C文件,用這個C文件的函數調用操做系統內核函數來創建Socket鏈接,這裏的StartServer()

方法就至關於咱們以前的ServerSocket socket = new ServerSocket();若是能調用成功,是否是說明JAVA就是經過調用LINUX函數實現的呢?

 

正篇開始

先放代碼

在咱們JAVA中有socket.bind(),在C語言中怎麼寫呢?也是同樣的,調用bind()函數便可,咱們也來瞧瞧

 

第一個字段就是把咱們剛剛的文件描述符傳過來,第二個就比較複雜了個人哥,在C語言中咱們的參數很是複雜,傳入參數,傳出參數和傳入傳出參數

啥意思?其實很簡單,傳入參數就是傳進來修改不會影響外面,相似局部變量,。。。傳入傳出就是指針啥的嘛,咱們這裏就是結構體指針,可是若是加了一個const就不同

const 意思是咱們不能修改它即只能傳入,不能傳出

這個sockaddr 結構體裏面有兩個參數,一個是IP,一個是端口號PORT,就是IP+端口,最後是你結構體的長度

爲何我這裏寫的是sockaddr_in呢?參數不是sockaddr嘛?你能夠把這個理解爲JAVA當中的類型,人家要一個INT結果你傳一個字符串

這個實際上是在UNIX開發之初,咱們的IP協議尚未很穩定的時候是用sockaddr來描述的,可是隨着計算機的發展,IP協議愈來愈複雜,因此它開發了另外一個函數叫Sockaddr_in來表示咱們的IP和端口號,因此這個方法已通過時了,可是爲何沒改掉呢?這個很簡單啊,和JAVA同樣它在不少地方都有使用,你不能隨便改,就像JAVA同樣咱們都是聲明接口,傳入實現類等等保障系統的可擴展性。那怎麼辦?其實很簡單,咱們傳一個參數進來而後把它強轉成這個類型不就行了

這個htons是啥?這個叫本地轉換成網絡字節數,短整型,說白了,你這個8080人家LINUX不認識,你須要經過轉換。好比你傳一個192.168.。。。人家LINUX不認識,它須要把它轉換成123456的整形才認識,這就是提供轉換的函數

INADDR_ANY是啥,你也能夠換成localhost,這裏表示當前本機任何一個可用的IP地址

 

這個listen是監聽你當前Socket當中在同一時刻可以接受的鏈接數,它默認值是128,無所謂,他不是關鍵

 

這是啥?傳出來的是C語言類型的數據,不是咱們能看懂的IP等,咱們須要轉換一下,就轉到這個數組中

這個就是讀數據啊,讀到這個字符串數據裏,以及它的長度

關鍵

這裏涉及JNI調用,不懂得本身去看吧。關於什麼是JNI我就不解釋了

首先來段簡單代碼,這個conn()會調用到咱們本身寫得C方法

把這個JAVA文件拷貝到LINUX系統裏

 

而後編譯這個java文件成.h文件

關鍵來了,咱們查看咱們編譯好的.h文件

而後這個.h文件能夠在咱們寫的函數裏導入進來那咱們那個方法怎麼命名,參考.h文件,這個.h中生成了一個conn()方法的C語言調用鏈接

這個JNIEXPORT。。。就是咱們得調用函數名稱,替換C文件得方法名便可

 

哈哈哈,能夠看到咱們運行JAVA已經調用到LINUX下得內核函數了,下面來測試一哈

牛逼吹完了,看看效果

這個nc是用來模擬鏈接得,能夠直接通訊了,哈哈哈

證實成功!

總結:好累啊,不再想這麼認真寫實驗了= =。寫了一個下午,我要去划水

相關文章
相關標籤/搜索