Android基礎入門教程——7.6.2 基於TCP協議的Socket通訊(1)
Android基礎入門教程——7.6.2 基於TCP協議的Socket通訊(1)
標籤(空格分隔): Android基礎入門教程
本節引言:
上一節的概念課枯燥無味是吧,不過總有點收穫是吧,本節開始我們來研究基於TCP協議的Socket
通訊,先來瞭解下Socket的概念,以及Socket通訊的模型,實現Socket的步驟,以及作為Socket服務
端與用戶端的兩位各做要做什麼事情!好的,我們由淺入深來扣這個Socket吧!
1.什麼是Socket?
2.Socket通訊模型:
Socket通訊實現步驟解析:
Step 1:建立ServerSocket和Socket
Step 2:開啟串連到的Socket的輸入/輸出流
Step 3:按照協議對Socket進行讀/寫操作
Step 4:關閉輸入輸出資料流,以及Socket
好的,我們接下來寫一個簡單的例子,開啟服務端後,用戶端點擊按鈕然後連結服務端,
並向服務端發送一串字串,表示通過Socket連結上伺服器~
3.Socket服務端的編寫:
服務端要做的事有這些:
Step 1:建立ServerSocket對象,綁定監聽的連接埠
Step 2:調用accept()方法監聽用戶端的請求
Step 3:串連建立後,通過輸入資料流讀取用戶端發送的請求資訊
Step 4:通過輸出資料流向用戶端發送響應資訊
Step 5:關閉相關資源
代碼實現:
直接在Eclipse下建立一個Java項目,然後把Java代碼貼進去即可!
public class SocketServer { public static void main(String[] args) throws IOException { //1.建立一個伺服器端Socket,即ServerSocket,指定綁定的連接埠,並監聽此連接埠 ServerSocket serverSocket = new ServerSocket(12345); InetAddress address = InetAddress.getLocalHost(); String ip = address.getHostAddress(); Socket socket = null; //2.調用accept()等待用戶端串連 System.out.println(~~~服務端已就緒,等待用戶端接入~,服務端ip地址: + ip); socket = serverSocket.accept(); //3.串連後擷取輸入資料流,讀取用戶端資訊 InputStream is=null; InputStreamReader isr=null; BufferedReader br=null; OutputStream os=null; PrintWriter pw=null; is = socket.getInputStream(); //擷取輸入資料流 isr = new InputStreamReader(is,UTF-8); br = new BufferedReader(isr); String info = null; while((info=br.readLine())!=null){//迴圈讀取用戶端的資訊 System.out.println(用戶端發送過來的資訊 + info); } socket.shutdownInput();//關閉輸入資料流 socket.close(); }}
然後我們把代碼run起來,控制台會列印:
好的,接下來到Android用戶端了!
4.Socket用戶端的編寫:
用戶端要做的事有這些:
Step 1:建立Socket對象,指明需要連結的伺服器的地址和端號
Step 2:連結建立後,通過輸出資料流向伺服器發送請求資訊
Step 3:通過輸出資料流擷取伺服器響應的資訊
Step 4:關閉相關資源
代碼實現:
MainActivity.java:
public class MainActivity extends AppCompatActivity implements View.OnClickListener { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button btn_accept = (Button) findViewById(R.id.btn_accept); btn_accept.setOnClickListener(this); } @Override public void onClick(View v) { new Thread() { @Override public void run() { try { acceptServer(); } catch (IOException e) { e.printStackTrace(); } } }.start(); } private void acceptServer() throws IOException { //1.建立用戶端Socket,指定伺服器位址和連接埠 Socket socket = new Socket(172.16.2.54, 12345); //2.擷取輸出資料流,向伺服器端發送資訊 OutputStream os = socket.getOutputStream();//位元組輸出資料流 PrintWriter pw = new PrintWriter(os);//將輸出資料流封裝為列印流 //擷取用戶端的IP地址 InetAddress address = InetAddress.getLocalHost(); String ip = address.getHostAddress(); pw.write(用戶端:~ + ip + ~ 接入伺服器!!); pw.flush(); socket.shutdownOutput();//關閉輸出資料流 socket.close(); }}
因為Android不允許在主線程(UI線程)中做網路操作,所以這裡需要我們自己
另開一個線程來串連Socket!
運行結果:
點擊按鈕後,服務端控制台列印:
5.增強版案例:小豬簡易聊天室
只是點擊個按鈕,然後伺服器返回一串資訊,肯定是很無趣的是吧,接下來我們來
搭建一個超簡單的聊天室,我們需要用到線程池,儲存Socket連結的集合,我們還需要
位元組寫一個線程,具體的我們在代碼中來體會!
實現的:
先把我們的服務端跑起來:
接著把我們的程式分別跑到兩台模擬器上:
接下來我們來寫代碼:
首先是服務端,就是將讀寫socket的操作放到自訂線程當中,建立ServerSocket後,迴圈
調用accept方法,當有新用戶端接入,將socket加入集合當中,同時線上程池建立一個線程!
另外,在讀取資訊的方法中,對輸入字串進行判斷,如果為bye字串,將socket從集合中
移除,然後close掉!
Server.java:
public class Server { //定義相關的參數,連接埠,儲存Socket串連的集合,ServerSocket對象 //以及線程池 private static final int PORT = 12345; private List mList = new ArrayList(); private ServerSocket server = null; private ExecutorService myExecutorService = null; public static void main(String[] args) { new Server(); } public Server() { try { server = new ServerSocket(PORT); //建立線程池 myExecutorService = Executors.newCachedThreadPool(); System.out.println(服務端運行中...); Socket client = null; while(true) { client = server.accept(); mList.add(client); myExecutorService.execute(new Service(client)); } }catch(Exception e){e.printStackTrace();} } class Service implements Runnable { private Socket socket; private BufferedReader in = null; private String msg = ; public Service(Socket socket) { this.socket = socket; try { in = new BufferedReader(new InputStreamReader(socket.getInputStream())); msg = 使用者: +this.socket.getInetAddress() + ~加入了聊天室 +當前線上人數: +mList.size(); this.sendmsg(); }catch(IOException e){e.printStackTrace();} } @Override public void run() { try{ while(true) { if((msg = in.readLine()) != null) { if(msg.equals(bye)) { System.out.println(~~~~~~~~~~~~~); mList.remove(socket); in.close(); msg = 使用者: + socket.getInetAddress() + 退出: +當前線上人數:+mList.size(); socket.close(); this.sendmsg(); break; }else{ msg = socket.getInetAddress() + 說: + msg; this.sendmsg(); } } } }catch(Exception e){e.printStackTrace();} } //為串連上服務端的每個用戶端發送資訊 public void sendmsg() { System.out.println(msg); int num = mList.size(); for(int index = 0;index < num;index++) { Socket mSocket = mList.get(index); PrintWriter pout = null; try { pout = new PrintWriter(new BufferedWriter( new OutputStreamWriter(mSocket.getOutputStream(),UTF-8)),true); pout.println(msg); }catch (IOException e) {e.printStackTrace();} } } }}
接著到用戶端,用戶端的痛點在於要另外開闢線程的問題,因為Android不允許直接在
主線程中做網路操作,而且不允許在主線程外的線程操作UI,這裡的做法是自己建立
一個線程,以及通過Hanlder來更新UI,實際開發不建議直接這樣做!!!
布局檔案:activity_main.xml:
MainActivity.java:
public class MainActivity extends AppCompatActivity implements Runnable { //定義相關變數,完成初始化 private TextView txtshow; private EditText editsend; private Button btnsend; private static final String HOST = 172.16.2.54; private static final int PORT = 12345; private Socket socket = null; private BufferedReader in = null; private PrintWriter out = null; private String content = ; private StringBuilder sb = null; //定義一個handler對象,用來重新整理介面 public Handler handler = new Handler() { public void handleMessage(Message msg) { if (msg.what == 0x123) { sb.append(content); txtshow.setText(sb.toString()); } } ; }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); sb = new StringBuilder(); txtshow = (TextView) findViewById(R.id.txtshow); editsend = (EditText) findViewById(R.id.editsend); btnsend = (Button) findViewById(R.id.btnsend); //當程式一開始啟動並執行時候就執行個體化Socket對象,與服務端進行串連,擷取輸入輸出資料流 //因為4.0以後不能再主線程中進行網路操作,所以需要另外開闢一個線程 new Thread() { public void run() { try { socket = new Socket(HOST, PORT); in = new BufferedReader(new InputStreamReader(socket.getInputStream(), UTF-8)); out = new PrintWriter(new BufferedWriter(new OutputStreamWriter( socket.getOutputStream())), true); } catch (IOException e) { e.printStackTrace(); } } }.start(); //為發送按鈕設定點擊事件 btnsend.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String msg = editsend.getText().toString(); if (socket.isConnected()) { if (!socket.isOutputShutdown()) { out.println(msg); } } } }); new Thread(MainActivity.this).start(); } //重寫run方法,在該方法中輸入資料流的讀取 @Override public void run() { try { while (true) { if (socket.isConnected()) { if (!socket.isInputShutdown()) { if ((content = in.readLine()) != null) { content += ; handler.sendEmptyMessage(0x123); } } } } } catch (Exception e) { e.printStackTrace(); } }}
本節小結:
好的,本節給大家講解了基於TCP的Socket通訊,文中介紹了Socket通訊的模型,實現了
一個簡單的Socket通訊例子,以及寫了一個增強版的執行個體:小豬聊天室,相信會對剛涉及
Socket編程的你帶來便利~,謝謝~