国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統(tǒng) > Android > 正文

Android使用多線程進(jìn)行網(wǎng)絡(luò)聊天室通信

2019-12-12 01:54:08
字體:
供稿:網(wǎng)友

TCP/IP通信協(xié)議是一種可靠的網(wǎng)絡(luò)協(xié)議,它在通信的兩端各建立一個(gè)Socket,從而在通信的兩端之間形成網(wǎng)絡(luò)虛擬鏈路。一旦建立了虛擬的網(wǎng)絡(luò)鏈路,兩端的程序就可以通過虛擬鏈路進(jìn)行通信了。Java對基于TCP協(xié)議的網(wǎng)絡(luò)通信提供了良好的封裝,Java使用Socket對象來代表兩端通信接口,并通過Socket產(chǎn)生IO流來進(jìn)行網(wǎng)絡(luò)通信。

下面的程序Demo是實(shí)現(xiàn)一個(gè)簡單的C/S聊天室的應(yīng)用,每個(gè)客戶端該包含兩條線程:一條負(fù)責(zé)生成主界面,響應(yīng)用戶動(dòng)作,并將用戶輸入的數(shù)據(jù)寫入Socket對應(yīng)的輸出流中;另一條負(fù)責(zé)讀取Socket對應(yīng)的輸入流中的數(shù)據(jù)(從服務(wù)器發(fā)送過來的數(shù)據(jù)),并負(fù)責(zé)將這些數(shù)據(jù)在程序界面上顯示出來。
客戶端程序是一個(gè)Android應(yīng)用,因此需要?jiǎng)?chuàng)建一個(gè)Android項(xiàng)目,這個(gè)Android應(yīng)用的界面中包含兩個(gè)文本框:一個(gè)用于接收用戶的輸入;另一個(gè)用于顯示聊天信息。界面中還有一個(gè)按鈕,當(dāng)用戶單擊該按鈕時(shí),程序向服務(wù)器發(fā)送聊天信息。
layout/activity_main.xml界面布局代碼如下:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <LinearLayout  android:layout_width="match_parent"  android:layout_height="wrap_content"  android:orientation="horizontal">  <!-- 定義一個(gè)文本框,它用于接收用戶的輸入 -->  <EditText   android:id="@+id/input"   android:layout_width="280dp"   android:layout_height="wrap_content" />  <Button   android:id="@+id/send"   android:layout_width="match_parent"   android:layout_height="wrap_content"   android:paddingLeft="8dp"   android:text="發(fā)送" /> </LinearLayout> <!-- 定義一個(gè)文本框,它用于顯示來自服務(wù)器的信息 --> <TextView  android:id="@+id/show"  android:layout_width="match_parent"  android:layout_height="match_parent"  android:background="#ffff"  android:gravity="top"  android:textColor="#f000"  android:textSize="18sp" /></LinearLayout>

客戶端的Activity負(fù)責(zé)生成程序界面,并為程序的按鈕單擊事件綁定事件監(jiān)聽器,當(dāng)用戶單擊按鈕時(shí)向服務(wù)器發(fā)送信息。
MainActivity.java邏輯代碼如下:

package com.fukaimei.multithreadclient;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.support.v7.app.AppCompatActivity;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.EditText;import android.widget.TextView;public class MainActivity extends AppCompatActivity { // 定義界面上的兩個(gè)文本框 EditText input; TextView show; // 定義界面上的一個(gè)按鈕 Button send; Handler handler; // 定義與服務(wù)器通信的子線程 ClientThread clientThread; @Override public void onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState);  setContentView(R.layout.activity_main);  input = (EditText) findViewById(R.id.input);  send = (Button) findViewById(R.id.send);  show = (TextView) findViewById(R.id.show);  handler = new Handler() // ②  {   @Override   public void handleMessage(Message msg) {    // 如果消息來自于子線程    if (msg.what == 0x123) {     // 將讀取的內(nèi)容追加顯示在文本框中     show.append("/n" + msg.obj.toString());    }   }  };  clientThread = new ClientThread(handler);  // 客戶端啟動(dòng)ClientThread線程創(chuàng)建網(wǎng)絡(luò)連接、讀取來自服務(wù)器的數(shù)據(jù)  new Thread(clientThread).start(); // ①  send.setOnClickListener(new OnClickListener() {   @Override   public void onClick(View v) {    try {     // 當(dāng)用戶按下發(fā)送按鈕后,將用戶輸入的數(shù)據(jù)封裝成Message     // 然后發(fā)送給子線程的Handler     Message msg = new Message();     msg.what = 0x234;     msg.obj = input.getText().toString();     clientThread.revHandler.sendMessage(msg);     // 清空input文本框     input.setText("");    } catch (Exception e) {     e.printStackTrace();    }   }  }); }}

當(dāng)用戶單擊該程序界面中的“發(fā)送”按鈕后,程序?qū)?huì)把input輸入框中的內(nèi)容發(fā)送給clientThread的revHandler對象,clientThread負(fù)責(zé)將用戶輸入的內(nèi)容發(fā)送給服務(wù)器。

ClientThread子線程負(fù)責(zé)建立與遠(yuǎn)程服務(wù)器的連接,并負(fù)責(zé)與遠(yuǎn)程服務(wù)器通信,讀到數(shù)據(jù)之后便通過Handler對象發(fā)送一條消息;當(dāng)ClientThread子線程收到UI線程發(fā)送過來的消息后,還負(fù)責(zé)將用戶輸入的內(nèi)容發(fā)送給遠(yuǎn)程服務(wù)器。

ClientThread.java邏輯代碼如下:

package com.fukaimei.multithreadclient;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;import java.net.SocketTimeoutException;public class ClientThread implements Runnable { private static final String TAG = "ClientThread"; private Socket s; // 定義向UI線程發(fā)送消息的Handler對象 private Handler handler; // 定義接收UI線程的消息的Handler對象 public Handler revHandler; // 該線程所處理的Socket所對應(yīng)的輸入流 BufferedReader br = null; OutputStream os = null; public ClientThread(Handler handler) {  this.handler = handler; } public void run() {  try {   s = new Socket("172.xx.xx.xxx", 30000);   br = new BufferedReader(new InputStreamReader(     s.getInputStream()));   os = s.getOutputStream();   // 啟動(dòng)一條子線程來讀取服務(wù)器響應(yīng)的數(shù)據(jù)   new Thread() {    @Override    public void run() {     String content = null;     // 不斷讀取Socket輸入流中的內(nèi)容     try {      while ((content = br.readLine()) != null) {       // 每當(dāng)讀到來自服務(wù)器的數(shù)據(jù)之后,發(fā)送消息通知程序       // 界面顯示該數(shù)據(jù)       Message msg = new Message();       msg.what = 0x123;       msg.obj = content;       handler.sendMessage(msg);      }     } catch (IOException e) {      e.printStackTrace();     }    }   }.start();   // 為當(dāng)前線程初始化Looper   Looper.prepare();   // 創(chuàng)建revHandler對象   revHandler = new Handler() {    @Override    public void handleMessage(Message msg) {     // 接收到UI線程中用戶輸入的數(shù)據(jù)     if (msg.what == 0x234) {      // 將用戶在文本框內(nèi)輸入的內(nèi)容寫入網(wǎng)絡(luò)      try {       os.write((msg.obj.toString() + "/r/n")         .getBytes("utf-8"));      } catch (Exception e) {       e.printStackTrace();      }     }    }   };   // 啟動(dòng)Looper   Looper.loop();  } catch (SocketTimeoutException e1) {   Log.d(TAG, "網(wǎng)絡(luò)連接超時(shí)!");  } catch (Exception e) {   e.printStackTrace();  } }}

上面線程的功能也非常簡單,它只是不斷地獲取Socket輸入流中的內(nèi)容,當(dāng)讀到Socket輸入流中的內(nèi)容后,便通過Handler對象發(fā)送一條消息,消息負(fù)責(zé)攜帶讀到的數(shù)據(jù)。除此之外,該子線程還負(fù)責(zé)讀取UI線程發(fā)送的消息,接收到消息之后,該子線程負(fù)責(zé)中攜帶的數(shù)據(jù)發(fā)送給遠(yuǎn)程服務(wù)器。

服務(wù)器端應(yīng)該包含多條線程,每個(gè)Socket對應(yīng)一條線程,該線程負(fù)責(zé)讀取Socket對應(yīng)輸入流,并將讀到的數(shù)據(jù)向每個(gè)Socket輸出流發(fā)送一遍,因此需要在服務(wù)器端使用List來保存所有的Socket。
下面是服務(wù)器端的代碼。程序?yàn)榉?wù)器提供了兩個(gè)類:一個(gè)是創(chuàng)建ServerSocket監(jiān)聽的主類;另一個(gè)是負(fù)責(zé)處理每個(gè)Socket通信的線程類。

/MultiThreadServer/src/MyServer.java邏輯代碼如下:

import java.io.IOException;import java.net.ServerSocket;import java.net.Socket;import java.util.ArrayList;public class MyServer { // 定義保存所有Socket的ArrayList public static ArrayList<Socket> socketList = new ArrayList<Socket>(); public static void main(String[] args) throws IOException {  ServerSocket ss = new ServerSocket(30000);  while (true) {   // 此行代碼會(huì)阻塞,將一直等待別人的連接   Socket s = ss.accept();   socketList.add(s);   // 每當(dāng)客戶端連接后啟動(dòng)一條ServerThread線程為該客戶端服務(wù)   new Thread(new ServerThread(s)).start();  } }}

上面的程序是服務(wù)器端只負(fù)責(zé)接收客戶端Socket的連接請求,每當(dāng)客戶端Socket連接到該ServerSocket之后,程序?qū)?yīng)Socket加入socketList集合中保存,并為該Socket啟動(dòng)一條線程,該程序負(fù)責(zé)處理該Socket所有的通信任務(wù)。服務(wù)器端線程類的代碼如下。

/MultiThreadServer/src/ServerThread.java邏輯代碼如下:

import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.io.OutputStream;import java.net.Socket;import java.util.Iterator;// 負(fù)責(zé)處理每條線程通信的線程類public class ServerThread implements Runnable { // 定義當(dāng)前線程所處理的Socket Socket s = null; // 該線程所處理的Socket所對應(yīng)的輸入流 BufferedReader br = null; public ServerThread(Socket s) throws IOException {  this.s = s;  // 初始化該Socket對應(yīng)的輸入流  br = new BufferedReader(new InputStreamReader(s.getInputStream(), "utf-8")); } @Override public void run() {  String content = null;  // 采用循環(huán)不斷從Socket中讀取客戶端發(fā)送過來的數(shù)據(jù)  while ((content = readFromClient()) != null) {   // 遍歷socketList中的每個(gè)Socket   // 將讀取的內(nèi)容向每個(gè)Socket發(fā)送一次   for (Iterator<Socket> it = MyServer.socketList.iterator(); it.hasNext();) {    Socket s = it.next();    try {     OutputStream os = s.getOutputStream();     os.write((content + "/n").getBytes("utf-8"));    } catch (Exception e) {     e.printStackTrace();     // 刪除該Socket     it.remove();     System.out.println(MyServer.socketList);    }   }  } } // 定義讀取客戶端數(shù)據(jù)的方法 private String readFromClient() {  try {   return br.readLine();  } catch (IOException e) { // 如果捕獲到異常,表明該Socket對應(yīng)的客戶端已經(jīng)關(guān)閉   e.printStackTrace();   // 刪除該Socket   MyServer.socketList.remove(s);  }  return null; }}

上面的服務(wù)器端線程類不斷讀取客戶端數(shù)據(jù),程序使用readFromClient()方法來讀取客戶端數(shù)據(jù),如果在讀數(shù)據(jù)過程中捕獲到IOException異常,則表明該Socket對應(yīng)的客戶端Socket出現(xiàn)問題,程序就將該Socket從socketList中刪除。
當(dāng)服務(wù)器線程讀到客戶端數(shù)據(jù)之后,程序遍歷socketList集合,并將該數(shù)據(jù)向socketList集合中的每個(gè)Socket發(fā)送一次――該服務(wù)器線程將把從Socket中讀到的數(shù)據(jù)向socketList中的每個(gè)Socket轉(zhuǎn)發(fā)一次。

先運(yùn)行上面程序的MyServer類,該類運(yùn)行后只是作為服務(wù)器,看不到任何輸出。接著可以運(yùn)行Android客戶端――相當(dāng)于啟動(dòng)聊天界面登錄該服務(wù)器,接下來在任何一個(gè)Android客戶端輸入一些內(nèi)容后單擊“發(fā)送”按鈕,將可以看到所有客戶端(包含自己)都會(huì)收到剛剛輸入的內(nèi)容,這樣就簡單實(shí)現(xiàn)了一個(gè)C/S結(jié)構(gòu)的聊天室的功能。
注意:由于該程序需要訪問互聯(lián)網(wǎng),因此還需要在清單文件AndroidManifest.xml文件中授權(quán)訪問互聯(lián)網(wǎng)的權(quán)限:

<!-- 授權(quán)訪問互聯(lián)網(wǎng)--> <uses-permission android:name="android.permission.INTERNET" />

Demo程序運(yùn)行效果界面截圖如下:

這里寫圖片描述

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持武林網(wǎng)。

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 巴林右旗| 运城市| 庆云县| 宜阳县| 三门峡市| 汉川市| 西和县| 宜阳县| 绩溪县| 靖边县| 重庆市| 甘泉县| 铜陵市| 栾城县| 乌拉特后旗| 岳普湖县| 万年县| 嘉禾县| 油尖旺区| 保山市| 文成县| 岳池县| 大新县| 沙田区| 平昌县| 邻水| 湘阴县| 五河县| 安福县| 北票市| 湟源县| 仙居县| 合阳县| 肃南| 武汉市| 平邑县| 德惠市| 凤翔县| 元江| 元江| 黄大仙区|