先從一段簡單的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是用來模擬鏈接得,能夠直接通訊了,哈哈哈
證實成功!
總結:好累啊,不再想這麼認真寫實驗了= =。寫了一個下午,我要去划水