您好,登錄后才能下訂單哦!
前提概要
筆者很久之前其實就已經學習過了socket,當然也是用socket做過了聊天室,但是覺得此知識點比較一般,并無特別難的技術點,于是也并未深究。
然而近期一個項目中對socket的使用卻讓筆者感覺socket強大無比,可以實現諸多功能。
個人Socket體驗
項目主要有關智能家居,需要實現多臺手機同時對燈進行操作(開或者關),主要需要實現以下幾點:
1、進入界面時獲取所有燈的狀態。
2、一臺手機改變了燈的狀態,其他的手機上可以有所顯示。
3、硬件上改變了燈的狀態(手動開關燈),所有手機上要有所顯示。
此功能如果使用HTTP讀取的方式實現就不太合適了。一方面客戶端與服務器讀取文件的同步性難以保證,即使保證了,也需要浪費大量性能;另一方面,類似筆者的這種項目功能服務器和客戶端交互比較頻繁,對“即時性”要求也比較高,用HTTP不僅性能消耗太大,而且難以保證“即時性”。
但是使用Socket就很容易實現了,主要邏輯如下:
1、每次進入界面與服務器建立Socket連接,并得到此時燈的狀態
2、每次需要對燈進行操作的時候建立一個線程把燈的狀態傳遞給服務器,服務器接收到之后,把該狀態傳遞給每一個此時與服務器建立連接的客戶端。
此次體驗也是讓筆者想起了學長之前做的一道筆試題,題目大概如下:
將淘寶網頁和手機版同時打開賬戶,手機停留在購物車界面,此時網頁上將某一物品加入購物車,如何設計才能讓手機自動刷新購物車。
如果使用socket,相信是一個不錯的思路。
好了,接下來進入正題,展示socket聊天室demo。
效果(源碼在文章結尾)
主要思路
Android
1、進入界面客戶端與服務器建立socket,同時此時開啟一個線程一直接收服務器發送來的消息。
2、每次點擊button獲取EditText中的字符串,調用子線程把字符串發送給服務器。
服務器
1、創建一個ArrayList存儲Socket。
2、循環接收請求訪問該端口的客戶端,接收到之后,把該socket存儲到ArrayList中,并且為每一個socket開啟一個線程用于通信。
3、每個socket的線程的邏輯如下:循環接收客戶端發來的消息,接收到之后,利用之前的ArrayList,發送到每一個客戶端。如果某個客戶端返回空值或者無法發送過去,那么表示該客戶端已經斷開,就從ArrayList中移除。
代碼
(借鑒《Android瘋狂講義》)
Android
不要忘記在AndroidManifest里面加上訪問網絡的權限
MainActivity:
package com.example.double2.sockettesttwo; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; public class MainActivity extends AppCompatActivity { private EditText etMain; private Button btnMain; private TextView tvMain; private ClientThread mClientThread; //在主線程中定義Handler傳入子線程用于更新TextView private Handler mHandler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); etMain = (EditText) findViewById(R.id.et_main); btnMain = (Button) findViewById(R.id.btn_main); tvMain = (TextView) findViewById(R.id.tv_main); mHandler=new Handler() { @Override public void handleMessage(Message msg) { if (msg.what == 0) { tvMain.append("\n" + msg.obj.toString()); } } }; //點擊button時,獲取EditText中string并且調用子線程的Handler發送到服務器 btnMain.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { try { Message msg = new Message(); msg.what = 1; msg.obj = etMain.getText().toString(); mClientThread.revHandler.sendMessage(msg); etMain.setText(""); } catch (Exception e) { e.printStackTrace(); } } }); mClientThread = new ClientThread(mHandler); new Thread(mClientThread).start(); } }
ClientThread
package com.example.double2.sockettesttwo; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.util.Log; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.Socket; /** * 項目名稱:SocketTestTwo * 創建人:Double2號 * 創建時間:2016.11.20 9:16 * 修改備注: */ public class ClientThread implements Runnable { private Socket mSocket; private BufferedReader mBufferedReader = null; private OutputStream mOutputStream = null; private Handler mHandler; public Handler revHandler; public ClientThread(Handler handler) { mHandler = handler; } @Override public void run() { try { mSocket = new Socket("10.3.20.159", 30003); Log.d("xjj","connect success"); mBufferedReader = new BufferedReader(new InputStreamReader(mSocket.getInputStream())); mOutputStream = mSocket.getOutputStream(); new Thread(){ @Override public void run() { super.run(); try { String content = null; while ((content = mBufferedReader.readLine()) != null) { Log.d("xjj",content); Message msg = new Message(); msg.what = 0; msg.obj = content; mHandler.sendMessage(msg); } }catch (IOException e){ e.printStackTrace(); } } }.start(); //由于子線程中沒有默認初始化Looper,要在子線程中創建Handler,需要自己寫 Looper.prepare(); revHandler = new Handler() { @Override public void handleMessage(Message msg) { if (msg.what == 1) { try { mOutputStream.write((msg.obj.toString() + "\r\n").getBytes("utf-8")); } catch (IOException e) { e.printStackTrace(); } } } }; Looper.loop(); } catch (IOException e) { e.printStackTrace(); Log.d("xjj",""); } } }
activity_main
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="@dimen/activity_vertical_margin" android:orientation="vertical" > <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <EditText android:id="@+id/et_main" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1"/> <Button android:id="@+id/btn_main" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/send"/> </LinearLayout> <TextView android:id="@+id/tv_main" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>
服務器:
MySever
import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; public class MySever { public static ArrayList<Socket> socketList = new ArrayList<Socket>(); public static String content=""; public static void main(String[] args) throws IOException { //建立ServerSocket ServerSocket ss = new ServerSocket(30003); //不斷接收此端口的socket,并存儲到socketList中 //并且為每一個socket開啟一個線程,用于接收信息 while (true) { Socket s = ss.accept(); System.out.println("connect success!"); socketList.add(s); new Thread(new ServerThread(s)).start(); } } }
SeverThread
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.net.Socket; import java.net.SocketException; import java.util.Iterator; public class ServerThread implements Runnable { private Socket mSocket = null; private BufferedReader mBufferedReader = null; // 構造函數中接收socket并且初始化BufferedReader public ServerThread(Socket socket) throws UnsupportedEncodingException, IOException { mSocket = socket; mBufferedReader = new BufferedReader( new InputStreamReader(mSocket.getInputStream(), "utf-8")); } @Override public void run() { // TODO Auto-generated method stub try { String content = null; //循環接收來自此客戶端的消息 //如果接收不到了,表面已經斷開,就將此客戶端從socketList中移除 while ((content = mBufferedReader.readLine()) != null) { System.out.println(content); //向連接中的每個客戶端發送數據 //如果異常,說明斷開,就將該條目從socketList中移除 for (Iterator<Socket> it = MySever.socketList.iterator(); it.hasNext();) { Socket s = it.next(); try { OutputStream os = s.getOutputStream(); os.write((content + "\n").getBytes("utf-8")); } catch (SocketException e) { e.printStackTrace(); it.remove(); } } } } catch (IOException e) { e.printStackTrace(); MySever.socketList.remove(mSocket); } } }
源碼地址:android socket聊天室
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。