摘要:什么是網絡上的兩個程序通過一個雙向的通訊連接實現數據的交換,這個雙向鏈路的一端稱為一個。通常用來實現客戶方和服務方的連接。
什么是Socket
網絡上的兩個程序通過一個雙向的通訊連接實現數據的交換,這個雙向鏈路的一端稱為一個Socket。Socket通常用來實現客戶方和服務方的連接。Socket是TCP/IP協議的一個十分流行的編程界面,一個Socket由一個IP地址和一個端口號唯一確定
但是,Socket所支持的協議種類不僅TCP/IP一種,因此兩者之間是沒有必然聯系的。在Java環境下,Socket編程主要是指基于TCP/IP協議的網絡編程
PS:雖然湊字數這種技能早就點滿了,但關于更多Socket及TCP/IP相關概念,還請各位看官自行/先行了解,這里不再多做贅述
本次Demo預覽 工具準備Eclipse(若你沒有Eclipse也沒事兒,后邊告訴你用命令行編譯運行!)
AndroidStudio(若你本身就是用Eclipse開發安卓程序,那Eclipse就夠了)
服務端OK,話不多說,開干
首先在Eclipse新建一個Java項目,就叫SocketDemo吧
接下來咱們要監聽是否有客戶端發送連接請求,如果有,則連接并處理
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)讀取消息,所以每條消息最后必須加換行符 ,否則讀取不到 mWriter.write(msg + " "); 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就是這么簡單!
接下來你會發現,客戶端特么更簡單!
客戶端(Android)第一步新建一個安卓項目,也叫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 { //服務端是按行讀取消息,所以每條消息最后必須加換行符 mWriter.write(msg + " "); 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 + " "); 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"); } }
客戶端(安卓端)的我就不放了!
結語更多內容歡迎訪問我的主頁或我的博客
如果我的文章確實有幫助到你,請不要忘了點一下文末的"?"讓他變成"?"
作為新手難免很多地方理解不到位,文中若有錯誤請直(bu)接(yao)指(ma)出(wo)
寫作不易!
致于題目叫"與自己聊次天",我想解釋一
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/67010.html
摘要:什么是網絡上的兩個程序通過一個雙向的通訊連接實現數據的交換,這個雙向鏈路的一端稱為一個。通常用來實現客戶方和服務方的連接。 什么是Socket 網絡上的兩個程序通過一個雙向的通訊連接實現數據的交換,這個雙向鏈路的一端稱為一個Socket。Socket通常用來實現客戶方和服務方的連接。Socket是TCP/IP協議的一個十分流行的編程界面,一個Socket由一個IP地址和一個端口號唯一確...
摘要:在大型的工程中,自己定義的變量函數,類名與其他人定義的相沖突等問題。使用標準輸出控制臺和標準輸入鍵盤時,必須包含頭文件以及標準命名空間。缺省參數概念缺省參數是聲明或定義函數時為函數的參數指定一個默認值。 目錄 前言 1.命名空間 1.1命名空間定義 1.2 命名空間使用 2. C++的輸入和...
摘要:容器運行時,會打印一條信息消息并退出。因此,更好地做法是將需要使用的用戶加入用戶組。涉及到了兩條指令,和。執行命令指令是用來執行命令行命令的。 Docker 是個劃時代的開源項目,它徹底釋放了計算虛擬化的威力,極大提高了應用的運行效率,降低了云計算資源供應的成本!使用 Docker,可以讓應用的部署、測試和分發都變得前所未有的高效和輕松! 無論是應用開發者、運維人員、還是其他信息技術從...
摘要:容器運行時,會打印一條信息消息并退出。因此,更好地做法是將需要使用的用戶加入用戶組。涉及到了兩條指令,和。執行命令指令是用來執行命令行命令的。 Docker 是個劃時代的開源項目,它徹底釋放了計算虛擬化的威力,極大提高了應用的運行效率,降低了云計算資源供應的成本!使用 Docker,可以讓應用的部署、測試和分發都變得前所未有的高效和輕松! 無論是應用開發者、運維人員、還是其他信息技術從...
摘要:被移除的方法和屬性被改變為復數形式的方法名以下這些方法名被更改為了復數形式。在中,使用單數的方法名將會得到一個警告。在中,被它的駝峰命名版本所取代。在中,中會刪去端口號,而在中,端口號會被保留。 前言 Express 5.0 仍處于alpha版中,但是我們還是想先來初窺一下新的express版本中將會有哪些改變,以及如何將你的應用從Express 4 遷移至 Express 5。 Ex...
閱讀 3583·2021-11-15 11:36
閱讀 1076·2021-11-11 16:55
閱讀 715·2021-10-20 13:47
閱讀 3035·2021-09-29 09:35
閱讀 3465·2021-09-08 10:45
閱讀 2564·2019-08-30 15:44
閱讀 863·2019-08-30 11:10
閱讀 1441·2019-08-29 13:43