<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                #### 2.4.6 使用Socket 在本節中,我們**通過Socket來實現進程間的通信**。 Socket也稱為“**套接字**”,是網絡通信中的概念,它分為**流式套接字和用戶數據報套接字兩種**,分別**對應于網絡的傳輸控制層中的TCP和UDP協議**。 * **TCP協議是面向連接的協議,提供穩定的雙向通信功能**,TCP連接的建立**需要經過“三次握手”才能完成**,為了提供**穩定的數據傳輸功能**,其**本身提供了超時重傳機制**,因此**具有很高的穩定性**; * 而**UDP是無連接的,提供不穩定的單向通信功能**,當然**UDP也可以實現雙向通信功能**。 * 在**性能上,UDP具有更好的效率,其缺點是不保證數據一定能夠正確傳輸,尤其是在網絡擁塞的情況下**。 關于TCP和UDP的介紹就這么多,更詳細的資料請查看相關網絡資料。 接下來我們**演示一個跨進程的聊天程序**,**兩個進程可以通過Socket來實現信息的傳輸,Socket本身可以支持傳輸任意字節流,這里為了簡單起見,僅僅傳輸文本信息,很顯然,這是一種IPC方式**。 使用Socket來進行通信,有兩點需要注意,首先需要聲明權限: ``` <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> ``` 其次要**注意不能在主線程中訪問網絡,因為這會導致我們的程序無法在Android 4.0及其以上的設備中運行**,會拋出如下異常:`android.os.NetworkOnMainThreadException`。而且**進行網絡操作很可能是耗時的,如果放在主線程中會影響程序的響應效率,從這方面來說,也不應該在主線程中訪問網絡**。 下面就開始設計我們的聊天室程序了,比較簡單,**首先在遠程Service建立一個TCP服務,然后在主界面中連接TCP服務,連接上了以后,就可以給服務端發消息。對于我們發送的每一條文本消息,服務端都會隨機地回應我們一句話。為了更好地展示Socket的工作機制,在服務端我們做了處理,使其能夠和多個客戶端同時建立連接并響應**。 先看一下**服務端的設計**,**當Service啟動時,會在線程中建立TCP服務,這里監聽的是8688端口,然后就可以等待客戶端的連接請求**。 * 當**有客戶端連接時**,就會**生成一個新的Socket,通過每次新創建的Socket就可以分別和不同的客戶端通信了**。服務端每收到一次客戶端的消息就會隨機回復一句話給客戶端。 * 當**客戶端斷開連接時**,**服務端這邊也會相應的關閉對應Socket并結束通話線程**,這點是如何做到的呢?方法有很多,這里是**通過判斷服務端輸入流的返回值來確定的,當客戶端斷開連接后,服務端這邊的輸入流會返回null,這個時候我們就知道客戶端退出了**。 服務端的代碼如下所示。 ~~~ package com.ryg.chapter_2.socket; public class TCPServerService extends Service { private boolean mIsServiceDestoryed = false; private String[] mDefinedMessages = new String[] { "你好啊,哈哈", "請問你叫什么名字呀?", "今天北京天氣不錯啊,shy", "你知道嗎?我可是可以和多個人同時聊天的哦", "給你講個笑話吧:據說愛笑的人運氣不會太差,不知道真假。" }; @Override public void onCreate() { new Thread(new TcpServer()).start(); super.onCreate(); } @Override public IBinder onBind(Intent intent) { return null; } @Override public void onDestroy() { mIsServiceDestoryed = true; super.onDestroy(); } private class TcpServer implements Runnable { @SuppressWarnings("resource") @Override public void run() { ServerSocket serverSocket = null; try { //監聽本地8688端口 serverSocket = new ServerSocket(8688); } catch (IOException e) { System.err.println("establish tcp server failed, port:8688"); e.printStackTrace(); return; } while (!mIsServiceDestoryed) { try { // 接受客戶端請求 final Socket client = serverSocket.accept(); System.out.println("accept"); //這里建議不要顯示創建線程,盡量使用線程池。 //線程資源必須通過線程池提供,不允許在應用中自行顯式創建線程。 // 說明:使用線程池的好處是減少在創建和銷毀線程上所花的時間以及系統資源的開銷,解決資源不足的問題。 // 如果不使用線程池,有可能造成系統創建大量同類線程而導致消耗完內存或者“過度切換”的問題。 new Thread() { @Override public void run() { try { responseClient(client); } catch (IOException e) { e.printStackTrace(); } }; }.start(); } catch (IOException e) { e.printStackTrace(); } } } } private void responseClient(Socket client) throws IOException { // 用于接收客戶端消息 BufferedReader in = new BufferedReader(new InputStreamReader( client.getInputStream())); // 用于向客戶端發送消息 PrintWriter out = new PrintWriter(new BufferedWriter( new OutputStreamWriter(client.getOutputStream())), true); out.println("歡迎來到聊天室!"); while (!mIsServiceDestoryed) { String str = in.readLine(); System.out.println("msg from client:" + str); if (str == null) { //客戶端斷開連接 break; } int i = new Random().nextInt(mDefinedMessages.length); String msg = mDefinedMessages[i]; out.println(msg); System.out.println("send :" + msg); } System.out.println("client quit."); // 關閉流 MyUtils.close(out); MyUtils.close(in); client.close(); } } ~~~ 接著看一下客戶端,**客戶端Activity啟動**時,會**在onCreate中開啟一個線程去連接服務端Socket,至于為什么用線程在前面已經做了介紹(不要在主線程中訪問網絡)。為了確定能夠連接成功,這里采用了超時重連的策略,每次連接失敗后都會重新建立嘗試建立連接。當然為了降低重試機制的開銷,我們加入了休眠機制,即每次重試的時間間隔為1000毫秒**。 Socket socket = null; //超時重連機制,只有當socket不為空,才是連接成功 while (socket == null) { try { socket = new Socket("localhost", 8688); mClientSocket = socket; mPrintWriter = new PrintWriter(new BufferedWriter( new OutputStreamWriter(socket.getOutputStream())), true); mHandler.sendEmptyMessage(MESSAGE_SOCKET_CONNECTED); System.out.println("connect server success."); } catch (IOException e) { SystemClock.sleep(1000); System.out.println("connect tcp server failed, retry..."); } } **服務端連接成功以后,就可以和服務端進行通信了**。 下面的代碼**在線程中通過while循環不斷地去讀取服務端發送過來的消息,同時當Activity退出時,就退出循環并終止線程**。 ~~~ // 接收服務器端的消息 BufferedReader br = new BufferedReader(new InputStreamReader( socket.getInputStream())); while (!TCPClientActivity.this.isFinishing()) { String msg = br.readLine(); System.out.println("receive :" + msg); if (msg != null) { String time = formatDateTime(System.currentTimeMillis()); final String showedMsg = "server " + time + ":" + msg + "\n"; mHandler.obtainMessage(MESSAGE_RECEIVE_NEW_MSG, showedMsg) .sendToTarget(); } } ~~~ 同時,**當Activity退出時,還要關閉當前的Socket**,如下所示。 ~~~ @Override protected void onDestroy() { if (mClientSocket != null) { try { mClientSocket.shutdownInput(); mClientSocket.close(); } catch (IOException e) { e.printStackTrace(); } } super.onDestroy(); } ~~~ 接著是發送消息的過程,這個就很簡單了,這里不再詳細說明。客戶端的完整代碼如下: ~~~ package com.ryg.chapter_2.socket; public class TCPClientActivity extends Activity implements OnClickListener { private static final int MESSAGE_RECEIVE_NEW_MSG = 1; private static final int MESSAGE_SOCKET_CONNECTED = 2; private Button mSendButton; private TextView mMessageTextView; private EditText mMessageEditText; private PrintWriter mPrintWriter; private Socket mClientSocket; @SuppressLint("HandlerLeak") private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case MESSAGE_RECEIVE_NEW_MSG: { mMessageTextView.setText(mMessageTextView.getText() + (String) msg.obj); break; } case MESSAGE_SOCKET_CONNECTED: { mSendButton.setEnabled(true); break; } default: break; } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_tcpclient); mMessageTextView = (TextView) findViewById(R.id.msg_container); mSendButton = (Button) findViewById(R.id.send); mSendButton.setOnClickListener(this); mMessageEditText = (EditText) findViewById(R.id.msg); Intent service = new Intent(this, TCPServerService.class); startService(service); new Thread() { @Override public void run() { connectTCPServer(); } }.start(); } @Override protected void onDestroy() { if (mClientSocket != null) { try { mClientSocket.shutdownInput(); mClientSocket.close(); } catch (IOException e) { e.printStackTrace(); } } super.onDestroy(); } @Override public void onClick(View v) { if (v == mSendButton) { final String msg = mMessageEditText.getText().toString(); if (!TextUtils.isEmpty(msg) && mPrintWriter != null) { mPrintWriter.println(msg); mMessageEditText.setText(""); String time = formatDateTime(System.currentTimeMillis()); final String showedMsg = "self " + time + ":" + msg + "\n"; mMessageTextView.setText(mMessageTextView.getText() + showedMsg); } } } @SuppressLint("SimpleDateFormat") private String formatDateTime(long time) { return new SimpleDateFormat("(HH:mm:ss)").format(new Date(time)); } private void connectTCPServer() { Socket socket = null; //超時重連機制,只有當socket不為空,才是連接成功 while (socket == null) { try { socket = new Socket("localhost", 8688); mClientSocket = socket; mPrintWriter = new PrintWriter(new BufferedWriter( new OutputStreamWriter(socket.getOutputStream())), true); mHandler.sendEmptyMessage(MESSAGE_SOCKET_CONNECTED); System.out.println("connect server success"); } catch (IOException e) { SystemClock.sleep(1000); System.out.println("connect tcp server failed, retry..."); } } try { // 接收服務器端的消息 BufferedReader br = new BufferedReader(new InputStreamReader( socket.getInputStream())); while (!TCPClientActivity.this.isFinishing()) { String msg = br.readLine(); System.out.println("receive :" + msg); if (msg != null) { String time = formatDateTime(System.currentTimeMillis()); final String showedMsg = "server " + time + ":" + msg + "\n"; mHandler.obtainMessage(MESSAGE_RECEIVE_NEW_MSG, showedMsg) .sendToTarget(); } } System.out.println("quit..."); MyUtils.close(mPrintWriter); MyUtils.close(br); socket.close(); } catch (IOException e) { e.printStackTrace(); } } } ~~~ 上述就是**通過Socket來進行進程間通信的實例,除了采用TCP套接字,還可以采用UDP套接字**。 另外,上面的例子僅僅是一個示例,實際上**通過Socket不僅僅能實現進程間的通信,還可以實現設備間的通信,當然前提是這些設備之間的IP地址互相可見**,這其中又涉及許多復雜的概念,這里就不一一介紹了。下面看一下上述例子的運行效果,如圖2-9所示。 :-: ![](https://img.kancloud.cn/71/de/71de98cf363dcd296beafbb2fb35a36d_626x599.png) 圖2-9 Socket通信示例
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看