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


 

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

簡單的SOCKET通信程序

先從一段簡單的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的博客呢?

服務端代碼:

 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 }

客戶端代碼:

 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的很多功能都是通過調用操作系統函數來實現的,比如顯示文字

JAVA比如建立Socket是通過告訴虛擬機這個指令,然后虛擬機調用操作系統的Socket()函數來實現的

 

 

 查看一下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方法,注意你的包名設置,等會編譯的時候需要你到你LINUX目錄的上一級目錄編譯 com.類名

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

 

然后編譯這個java文件成.h文件,這里注意你的JDK環境設置,我一開始用的是OPEN JDK是沒有JAVAC命令的

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

然后這個.h文件可以在我們寫的函數里導入進來那我們那個方法怎么命名,參考.h文件,這個.h中生成了一個conn()方法的C語言調用連接

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

你可以把這個方法看成conn()方法在LINUX下的名稱,等會調用conn()方法等於調用這個方法

 

注意一下,你必須指定你的classpath路徑,否則我們得java System.loadLibray是找不到得,很簡單得道理,你寫JAVA得時候你在當前目錄下得文件,你要JAVA怎么運行呢?

還有就是必須導入/include和/include/linux下的兩個頭文件,參考我上面的命令

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

牛逼吹完了,看看效果

這個nc是用來模擬連接得,可以直接通信了,哈哈哈

證明成功!

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

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM