網絡上的兩個程序經過一個雙向的通信鏈接實現數據的交換,這個雙向鏈路的一端稱爲一個Socket。Socket一般用來實現客戶方和服務方的鏈接。Socket是TCP/IP協議的一個十分流行的編程界面,一個Socket由一個IP地址和一個端口號惟一肯定java
可是,Socket所支持的協議種類不只TCP/IP一種,所以二者之間是沒有必然聯繫的。在Java環境下,Socket編程主要是指基於TCP/IP協議的網絡編程git
PS:雖然湊字數這種技能早就點滿了,但關於更多Socket及TCP/IP相關概念,還請各位看官自行/先行了解,這裏再也不多作贅述github
Eclipse(若你沒有Eclipse也沒事兒,後邊告訴你用命令行編譯運行!)編程
AndroidStudio(若你自己就是用Eclipse開發安卓程序,那Eclipse就夠了)網絡
OK,話很少說,開幹socket
首先在Eclipse新建一個Java項目,就叫SocketDemo吧ide
接下來我們要監聽是否有客戶端發送鏈接請求,若是有,則鏈接並處理工具
SocketDemo.java:
佈局
public class SocketDemo { /** * 端口號 注意:0~1023爲系統所保留端口號,選擇端口號時應大於1023,具體隨便你取 */ public static int PORT = 2345; public static void main(String[] args) { try { //serverSocket用於監聽是否有客戶端發送鏈接請求 ServerSocket serverSocket = new ServerSocket(PORT); System.out.println("服務啓動..."); //serverSocket.accept():若是有客戶端發送鏈接請求, //則返回一個socket供處理與客戶端的鏈接,不然一直阻塞監聽 Socket socket = serverSocket.accept(); System.out.println("與客戶端鏈接成功..."); //這個MySocket是啥呢?是一個對socket的封裝,方便操做 MySocket mySocket = new MySocket(socket); //因爲MySocket繼承於Thread,因此須要start()一下 //致於爲啥要繼承於Thread來封裝socket,請看下方 MySocket類 mySocket.start(); } catch (IOException e) { e.printStackTrace(); } } }
註釋中的兩個問題,很好理解,很少說,直接看看MySocket是怎麼寫的吧:測試
MySocket.java
public class MySocket extends Thread { Socket mSocket; BufferedWriter mWriter; BufferedReader mReader; public MySocket(Socket socket) { this.mSocket = socket; try { mWriter = new BufferedWriter(new OutputStreamWriter( socket.getOutputStream(), "utf-8")); mReader = new BufferedReader(new InputStreamReader( socket.getInputStream(), "utf-8")); } catch (IOException e) { e.printStackTrace(); } } /** * 向客戶端發送消息 * msg 發送消息內容 **/ public void send(String msg) { try { // 客戶端按行(readLine)讀取消息,因此每條消息最後必須加換行符 \n,不然讀取不到 mWriter.write(msg + "\n"); mWriter.flush(); } catch (IOException e) { e.printStackTrace(); } } /** * * 不斷讀取來自客戶端的消息,一旦收到消息,則自動回覆 **/ @Override public void run() { super.run(); try { String line; //服務端按行讀取消息 //不斷按行讀取,得到來自客戶端的消息 while ((line = mReader.readLine()) != null) { System.out.println("客戶端消息:" + line); //收到客戶端消息後,自動回覆 send("已經收到你發送的\"" + line + "\""); } mReader.close(); } catch (IOException e) { e.printStackTrace(); } System.out.println("end"); } }
看完MySocket以後豁然開朗,原來將讀取客戶端消息的操做是阻塞的,要放在子線程來作,因此繼承於Thread會方便一點
那麼至此,服務端的程序已經寫完了
什麼?你問怎麼這麼簡單?!緣由有兩個:
這只是一個基礎的Socket服務端程序,不用考慮那麼多其餘狀況,天然幾行代碼就搞定了
沒錯,Socket就是這麼簡單!
接下來你會發現,客戶端特麼更簡單!
第一步新建一個安卓項目,也叫SocketDemo吧,畢竟,湊字數這個技能我比較熟練
簡單一點,佈局中就一個按鈕(id=btn_send),用來發送消息,初窺嘛,簡單就是王道,佈局代碼就不上了
接下來看看MainActivity的代碼:
不行,在看MainActivity以前還有一些話要交代清楚:
若是你將安卓程序跑在電腦的虛擬機上,則你訪問的IP地址爲:10.0.2.2(虛擬機只能經過這個IP訪問電腦)
若是你將安卓程序跑在真機上,那麼你須要在CMD中輸入ipconfig獲取到IPv4地址,而且確保手機和電腦在同一個網絡下(鏈接了同一個WIFI)
public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); connectServer(); findViewById(R.id.btn_send).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { sendMsg("2333"); } }); } private Socket mSocket; private BufferedWriter mWriter; private BufferedReader mReader; //這個IP上面解釋過了噢,要理解一下 private static String IP = "10.0.2.2"; //切記端口號必定要和服務端保持一致! private static int PORT = 2345; private void connectServer() { new Thread(new Runnable() { @Override public void run() { try { mSocket = new Socket(IP, PORT); mWriter = new BufferedWriter(new OutputStreamWriter( mSocket.getOutputStream(), "utf-8")); mReader = new BufferedReader(new InputStreamReader( mSocket.getInputStream(), "utf-8")); Log.i(TAG, "鏈接服務端成功"); } catch (IOException e) { Log.i(TAG, "鏈接服務端失敗"); e.printStackTrace(); return; } try { String line; while ((line = mReader.readLine()) != null) { Log.i(TAG, "服務端消息: " + line); } } catch (IOException e) { e.printStackTrace(); Log.i(TAG, "服務端:已中止服務"); } } }).start(); } private void sendMsg(String msg) { // 若是mSocket爲null有可能兩種狀況: // 1.還在嘗試鏈接服務端 // 2.鏈接失敗 if (mSocket == null){ Toast.makeText(this,"鏈接未完成或鏈接失敗,沒法發送消息!",Toast.LENGTH_SHORT).show(); return; } try { //服務端是按行讀取消息,因此每條消息最後必須加換行符 \n mWriter.write(msg + "\n"); mWriter.flush(); } catch (IOException e) { Toast.makeText(this,"發送失敗:服務端已關閉服務!",Toast.LENGTH_SHORT).show(); e.printStackTrace(); } } }
這就寫完了客戶端??對,這就寫完了...那你別問爲啥Socket咋就這麼點內容,Socket原本也不是啥難點~
而且,這只是一個很是很是基礎的Demo
OK,到這裏就能夠來跑一下程序試一試了
若是你有Eclipse,那麼直接在Eclipse內跑起來就好了!
若是很不巧,你沒有Eclipse
新建本文章服務端部分的SocketDemo.java
和MySocket.java
兩個文件,而且放在同一個文件夾下,上面代碼沒有寫出import包,不能直接copy進文件內用,文末我會放出全部源代碼,到文末copy一下放在兩個文件內就好了(固然你得確保你有JDK環境!雖然做爲安卓狗,這是必要的,但仍是提醒一下!)
打開CMD,切換進入上述兩個文件所在的目錄
執行
javac *.java java SocketDemo
就將程序跑起來了(ctrl+c退出程序)
注意事項:
在Eclipse內運行的程序,切記:若是修改內容後要從新啓動程序,請先將正在運行的程序關閉,不然將一直佔用端口!沒法再以此端口再次啓用一次程序!
若是用CMD運行的程序,提示編碼錯誤,請將全部中文替換成英文,或者將兩個.java文件內容轉換成GBK編碼(建議換成英文!英文好的哥們兒,上!)
直接跑安卓程序就好了!
在Eclipse跑服務端的圖已經在文首放出,這裏放一個CMD下跑服務端的圖片:
注:不知爲何發送消息的時候,命令行及LogCat不會即時顯示出內容,在我ctrl+c退出程序以後纔會一次全出來,如有知道的朋友,還望指教!萬分感謝!
想一下,服務端程序只響應一個客戶端,若是又有客戶端發出鏈接請求,那豈不是沒法響應了!
再想一下以爲不對,也就是我本身測試,哪來的第二個客戶端發出鏈接請求
再再想一下,若是你改了一下安卓端的代碼,又一次點了運行,那誰來響應你?!這樣的話,由於修改安卓端代碼,又得去把服務端的程序停了,再啓動一下,多麻煩!
好吧,既然分析了確實有這個麻煩,那就把它解決掉:
public class SocketDemo { public static int PORT = 2745; public static void main(String[] args) { try { ServerSocket serverSocket = new ServerSocket(PORT); System.out.println("服務啓動..."); //寫一個死循環,若是有一個客戶端鏈接成功,那麼繼續讓serverSocket.accept()阻塞住 //等待下一個客戶端請求,這樣不論有多少個客戶端請求過來,均可以響應到, //結束調試的時候再關閉服務端程序 while (true) { Socket socket = serverSocket.accept(); System.out.println("客戶端鏈接成功..."); MySocket mySocket = new MySocket(socket); mySocket.start(); } } catch (IOException e) { e.printStackTrace(); } } }
so easy~不解釋了~
至此整個SocketDemo就完成了,對Socket的第一步已經邁出了,那麼趕忙理解好,而後再深刻Socket吧!
SocketDemo.java:
import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; public class SocketDemo { public static int PORT = 2745; public static void main(String[] args) { try { ServerSocket serverSocket = new ServerSocket(PORT); System.out.println("服務啓動..."); while (true) { Socket socket = serverSocket.accept(); System.out.println("客戶端鏈接成功..."); MySocket mySocket = new MySocket(socket); mySocket.start(); } } catch (IOException e) { e.printStackTrace(); } } }
MySocket.java:
import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.Socket; public class MySocket extends Thread { Socket mSocket; BufferedWriter mWriter; BufferedReader mReader; public MySocket(Socket socket) { this.mSocket = socket; try { mWriter = new BufferedWriter(new OutputStreamWriter( socket.getOutputStream(), "utf-8")); mReader = new BufferedReader(new InputStreamReader( socket.getInputStream(), "utf-8")); } catch (IOException e) { e.printStackTrace(); } } public void send(String msg) { try { mWriter.write(msg + "\n"); mWriter.flush(); } catch (IOException e) { e.printStackTrace(); } } @Override public void run() { super.run(); try { String line; while ((line = mReader.readLine()) != null) { System.out.println("客戶端消息:" + line); send("收到:\"" + line + "\""); } mReader.close(); } catch (IOException e) { e.printStackTrace(); } System.out.println("end"); } }
客戶端(安卓端)的我就不放了!