?
前言:該博客花了我一個下午得心血,全部手打,路過給個贊,拒絕抄襲!!!!!!!!!!!!!!!!!!!!!!!!!
簡單的SOCKET通信程序
先從一段簡單的JAVA程序性開始寫起,這里我們才用半雙工的形式,這里的半雙工意思是客戶端可以給服務(wù)端發(fā)送數(shù)據(jù),發(fā)完數(shù)據(jù)就關(guān)閉,而服務(wù)端可以一直接受數(shù)據(jù)
我們使用多線程方式,這個不重要
下面是線程類
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的博客呢?
服務(wù)端代碼:
1 public class Server { 2 public static void main(String[] args) { 3 try{ 4
ServerSocket serverSocket =new ServerSocket(8888); 5
System.out.println("服務(wù)端已啟動,等待連接"); 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之間的關(guān)系,首先你得明白我們的java代碼編譯成class文件后是運行在JVM上,這個CLASS字節(jié)碼文件只有我們JVM認(rèn)識,你扔給操作系統(tǒng),操作系統(tǒng)鐵定不懂。
JVM也是一個軟件,它是由C++和C編寫的,我們姑且認(rèn)為它是C編寫的,相信大家都看過JAVA源碼,太多方法都是調(diào)用了native方法,比如我們的bind()方法
?
?
?
?
其次,JAVA的很多功能都是通過調(diào)用操作系統(tǒng)函數(shù)來實現(xiàn)的,比如顯示文字
JAVA比如建立Socket是通過告訴虛擬機(jī)這個指令,然后虛擬機(jī)調(diào)用操作系統(tǒng)的Socket()函數(shù)來實現(xiàn)的
?
?
?查看一下LINUX的SOCKET函數(shù)
簡單說一下,第一個參數(shù)是域類型,看到IPV4選它就完事了,第二個參數(shù)是協(xié)議,我們用SOCK_STRAM表示TCP協(xié)議,第三個取0,0會把前兩個參數(shù)對應(yīng)起來,寫0就完事了
return
value是返回一個文件描述符,這個很難明白,你可以把它理解為我們JAVA里面的對象,JAVA當(dāng)中有句話叫一切皆對象,而在LINUX中相應(yīng)的我們叫做一切皆文件
只要我們拿到這個文件描述符,就能操作相應(yīng)的Socket,牛逼吧= =
好的,下面講一下驗證思路
我們寫一個JAVA方法,這個方法也調(diào)用NATVIE方法,而這個方法就不是JVM提供的C了,而是我們自己寫的一個C文件,用這個C文件的函數(shù)調(diào)用操作系統(tǒng)內(nèi)核函數(shù)來建立Socket連接,這里的StartServer()
方法就相當(dāng)于我們之前的ServerSocket socket = new
ServerSocket();如果能調(diào)用成功,是不是說明JAVA就是通過調(diào)用LINUX函數(shù)實現(xiàn)的呢?
?
正篇開始
先放代碼
在我們JAVA中有socket.bind(),在C語言中怎么寫呢?也是一樣的,調(diào)用bind()函數(shù)即可,我們也來瞧瞧
?
第一個字段就是把我們剛剛的文件描述符傳過來,第二個就比較復(fù)雜了我的哥,在C語言中我們的參數(shù)非常復(fù)雜,傳入?yún)?shù),傳出參數(shù)和傳入傳出參數(shù)
啥意思?其實很簡單,傳入?yún)?shù)就是傳進(jìn)來修改不會影響外面,類似局部變量,。。。傳入傳出就是指針啥的嘛,我們這里就是結(jié)構(gòu)體指針,但是如果加了一個const就不一樣
const 意思是我們不能修改它即只能傳入,不能傳出
這個sockaddr 結(jié)構(gòu)體里面有兩個參數(shù),一個是IP,一個是端口號PORT,就是IP+端口,最后是你結(jié)構(gòu)體的長度
看正篇最開始的代碼,為什么我這里寫的是sockaddr_in呢?參數(shù)不是sockaddr嘛?
你可以把這個理解為JAVA當(dāng)中的類型,人家要一個INT結(jié)果你傳一個字符串
這個其實是在UNIX開發(fā)之初,我們的IP協(xié)議還沒有很穩(wěn)定的時候是用sockaddr來描述的,但是隨著計算機(jī)的發(fā)展,IP協(xié)議越來越復(fù)雜,所以它開發(fā)了另一個函數(shù)叫Sockaddr_in來表示我們的IP和端口號,所以這個方法已經(jīng)過時了,但是為什么沒改掉呢?這個很簡單啊,和JAVA一樣它在很多地方都有使用,你不能隨便改,就像JAVA一樣我們都是聲明接口,傳入實現(xiàn)類等等保障系統(tǒng)的可擴(kuò)展性。那怎么辦?其實很簡單,我們傳一個參數(shù)進(jìn)來然后把它強(qiáng)轉(zhuǎn)成這個類型不就好了
這個htons是啥?這個叫本地轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)數(shù),短整型,說白了,你這個8080人家LINUX
不認(rèn)識,你需要通過轉(zhuǎn)換。比如你傳一個192.168.。。。人家LINUX不認(rèn)識,它需要把它轉(zhuǎn)換成123456的整形才認(rèn)識,這就是提供轉(zhuǎn)換的函數(shù)
INADDR_ANY是啥,你也可以換成localhost,這里表示當(dāng)前本機(jī)任何一個可用的IP地址
?
這個listen是監(jiān)聽你當(dāng)前Socket當(dāng)中在同一時刻能夠接受的連接數(shù),它默認(rèn)值是128,無所謂,他不是關(guān)鍵
?
這是啥?傳出來的是C語言類型的數(shù)據(jù),不是我們能看懂的IP等,我們需要轉(zhuǎn)換一下,就轉(zhuǎn)到這個數(shù)組中
這個就是讀數(shù)據(jù)啊,讀到這個字符串?dāng)?shù)據(jù)里,以及它的長度
關(guān)鍵
這里涉及JNI調(diào)用,不懂得自己去看吧。關(guān)于什么是JNI我就不解釋了
首先來段簡單代碼,這個conn()會調(diào)用到我們自己寫得C方法,注意你的包名設(shè)置,等會編譯的時候需要你到你LINUX目錄的上一級目錄編譯 com.類名
把這個JAVA文件拷貝到LINUX系統(tǒng)里
?
然后編譯這個java文件成.h文件,這里注意你的JDK環(huán)境設(shè)置,我一開始用的是OPEN JDK是沒有JAVAC命令的
關(guān)鍵來了,我們查看我們編譯好的.h文件
然后這個.h文件可以在我們寫的函數(shù)里導(dǎo)入進(jìn)來那我們那個方法怎么命名,參考.h文件,這個.h中生成了一個conn()方法的C語言調(diào)用連接
這個JNIEXPORT。。。就是我們得調(diào)用函數(shù)名稱,替換C文件得方法名即可
你可以把這個方法看成conn()方法在LINUX下的名稱,等會調(diào)用conn()方法等于調(diào)用這個方法
?
注意一下,你必須指定你的classpath路徑,否則我們得java
System.loadLibray是找不到得,很簡單得道理,你寫JAVA得時候你在當(dāng)前目錄下得文件,你要JAVA怎么運行呢?
還有就是必須導(dǎo)入/include和/include/linux下的兩個頭文件,參考我上面的命令
哈哈哈,可以看到我們運行JAVA已經(jīng)調(diào)用到LINUX下得內(nèi)核函數(shù)了,下面來測試一哈
牛逼吹完了,看看效果
這個nc是用來模擬連接得,可以直接通信了,哈哈哈
證明成功!
總結(jié):好累啊,再也不想這么認(rèn)真寫實驗了= =。寫了一個下午,我要去劃水
?
?
?
熱門工具 換一換