欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android Socket通信實(shí)現(xiàn)簡(jiǎn)單聊天室

 更新時(shí)間:2020年04月05日 08:42:36   作者:cvil  
這篇文章主要為大家詳細(xì)介紹了Android網(wǎng)絡(luò)編程之Socket通信實(shí)現(xiàn)簡(jiǎn)單聊天室,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

socket通信是基于底層TCP/IP協(xié)議實(shí)現(xiàn)的。這種服務(wù)端不需要任何的配置文件和tomcat就可以完成服務(wù)端的發(fā)布,使用純java代碼實(shí)現(xiàn)通信。socket是對(duì)TCP/IP的封裝調(diào)用,本身并不是一種協(xié)議,我們通過(guò)socket來(lái)調(diào)用協(xié)議來(lái)跟服務(wù)端進(jìn)行通信和數(shù)據(jù)的傳輸。socket就像客戶(hù)端與服務(wù)端之間的一條信息通道,每一個(gè)不同的客戶(hù)端都會(huì)建立一個(gè)獨(dú)立的socket,雙方都沒(méi)有關(guān)閉連接的話(huà),連接—也就是建立好的這條socket通道將一直保持,服務(wù)端要跟那一個(gè)客戶(hù)端通信只需要找到對(duì)應(yīng)的socket對(duì)象就可以進(jìn)行數(shù)據(jù)傳遞。

第一次握手:客戶(hù)端發(fā)送syn包(syn=j)到服務(wù)器,并進(jìn)入SYN_SEND狀態(tài),等待服務(wù)器確認(rèn);
第二次握手:服務(wù)器收到syn包,必須確認(rèn)客戶(hù)的SYN(ack=j+1),同時(shí)自己也發(fā)送一個(gè)SYN包(syn=k),即SYN+ACK包,此時(shí)服務(wù)器進(jìn)入SYN_RECV狀態(tài);
第三次握手:客戶(hù)端收到服務(wù)器的SYN+ACK包,向服務(wù)器發(fā)送確認(rèn)包ACK(ack=k+1),此包發(fā)送完畢,客戶(hù)端和服務(wù)器進(jìn)入ESTABLISHED狀態(tài),完成三次握手。

一. 服務(wù)端:在客戶(hù)端跟服務(wù)端通信之前,服務(wù)端必須先開(kāi)啟。首先來(lái)看一下服務(wù)端Socket的編寫(xiě)吧。服務(wù)端就是一個(gè)簡(jiǎn)單的java項(xiàng)目,由于聊天室可能會(huì)有多個(gè)客戶(hù)端同時(shí)連接并發(fā)送消息,我們這里使用線(xiàn)程池來(lái)處理客戶(hù)端的請(qǐng)求。

List<Socket> list = new ArrayList<Socket>();
ExecutorService executorService;
 BufferedReader br;
 private static final int PORT = 12345;
 private static final int POOL_SIZE = 5 ;


public Socket_Server() throws IOException {
 executorService = Executors.newFixedThreadPool(POOL_SIZE);
 ServerSocket serverSocket = new ServerSocket(PORT);
 System.out.println(serverSocket.getInetAddress().getHostAddress() + ":服務(wù)端就緒。");
 Socket client = null;
 while (true) {//為每一個(gè)連接到服務(wù)器的客戶(hù)端分配一個(gè)線(xiàn)程進(jìn)行消息的接收和發(fā)送
  client = serverSocket.accept();
  list.add(client);
  executorService.execute(new Service(client));
 }
 }

首先我們創(chuàng)建了一個(gè)大小為5的固定大小線(xiàn)程池,并創(chuàng)建端口號(hào)為12345的服務(wù)端socket接收客戶(hù)端請(qǐng)求,通過(guò)一個(gè)while循環(huán)不斷輪詢(xún)來(lái)自服務(wù)端的連接請(qǐng)求,在while循環(huán)里面調(diào)用了serverSocket.accept();是線(xiàn)程進(jìn)入阻塞狀態(tài),也就是說(shuō)在沒(méi)有接收到客戶(hù)端的請(qǐng)求時(shí),程序?qū)⒁恢蓖A粼谶@里,當(dāng)有客戶(hù)端連接服務(wù)端是,代碼開(kāi)始往下走,我們把接收到的客戶(hù)端socket放入list里面,這樣我們就把所有連接到服務(wù)端的socket保存下來(lái)了,這樣就使得我們可以隨時(shí)對(duì)任一客戶(hù)端進(jìn)行數(shù)據(jù)傳遞。之后就是線(xiàn)程池調(diào)用execute執(zhí)行一個(gè)線(xiàn)程,把連接過(guò)來(lái)的socket作為參數(shù)傳進(jìn)去。接下來(lái)分析下service的內(nèi)容:

class Service implements Runnable {

 Socket client;
 BufferedReader br;
 String msg = "";

 public Service(Socket client) {
  this.client = client;
  try {
  br = new BufferedReader(new InputStreamReader(
   client.getInputStream()));
  msg = "用戶(hù):" + client.getInetAddress() + "加入了聊天室,當(dāng)前人數(shù):"
   + list.size();
  sendMsg();
  } catch (Exception e) {
  e.printStackTrace();
  }

 }

 public void run() {
  try {
  while (true) {

   if ((msg = br.readLine()) != null) {
   if(msg.equals("bye")){
    list.remove(this.client) ;
    br.close() ;
    msg = "用戶(hù):" + client.getInetAddress() + "離開(kāi)了聊天室,當(dāng)前人數(shù):" + list.size();
    sendMsg() ;
    client.close() ;
    break ;
   }else{
    msg = client.getInetAddress() + "說(shuō):" + msg;
    sendMsg() ;
   }
   }
  }
  } catch (Exception e) {
  e.printStackTrace();
  }
 }

 public void sendMsg() {//為每一個(gè)用戶(hù)發(fā)送這個(gè)消息:msg
  PrintWriter pw;
  System.out.println(msg);
  for (Socket client : list) {
  try {
   pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(
    client.getOutputStream())));
   pw.println(msg);
   pw.flush() ;
  } catch (Exception e) {
   e.printStackTrace();
  }
  }
 }
 }

在service的構(gòu)造方法中使用了作為參數(shù)傳進(jìn)來(lái)的socket,在里面我們通過(guò)這個(gè)socket獲取輸入流包裝成一個(gè)BufferedReader,br = new BufferedReader(new InputStreamReader(client.getInputStream()));這里我們是主要是針對(duì)聊天,所以使用的是字符流進(jìn)行數(shù)據(jù)的傳輸,這個(gè)類(lèi)里面聲明了一個(gè)成員變量msg,通過(guò)這個(gè)變量來(lái)給每個(gè)客戶(hù)端發(fā)送信息。下面看下sendMsg方法:

public void sendMsg() {//為每一個(gè)用戶(hù)發(fā)送這個(gè)消息:msg
  PrintWriter pw;
  System.out.println(msg);
  for (Socket client : list) {
  try {
   pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(
    client.getOutputStream())));
   pw.println(msg);
   pw.flush() ;
  } catch (Exception e) {
   e.printStackTrace();
  }
  }
 }

這里我們通過(guò)遍歷list里面的每個(gè)socket獲得它的的輸出流并且包裝成PrintWriter 向客戶(hù)端發(fā)送信息, pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(client.getOutputStream())));向每一個(gè)客戶(hù)端發(fā)送成員變量msg內(nèi)容,在PrintWriter調(diào)用print之后一定要調(diào)用flush刷新輸出流進(jìn)行數(shù)據(jù)的傳遞,否則客戶(hù)端無(wú)法接收到服務(wù)端發(fā)送的數(shù)據(jù)。接下來(lái)看這個(gè)類(lèi)的住方法 run:

public void run() {
 try {
 while (true) {

  if ((msg = br.readLine()) != null) {
  if(msg.equals("bye")){
   list.remove(this.client) ;
   br.close() ;
   msg = "用戶(hù):" + client.getInetAddress() + "離開(kāi)了聊天室,當(dāng)前人數(shù):" + list.size();
   sendMsg() ;
   client.close() ;
    break ;
   }else{
   msg = client.getInetAddress() + "說(shuō):" + msg;
  sendMsg() ;
  }
   }
  }
  } catch (Exception e) {
  e.printStackTrace();
  }
 }

與前面類(lèi)似,也是通過(guò)一個(gè)while進(jìn)行無(wú)限循環(huán)進(jìn)行讀取socket的輸入流,如果內(nèi)容不為空就調(diào)用sendmsg對(duì)每一個(gè)客戶(hù)端進(jìn)行信息發(fā)送,有個(gè)小小的處理就是如果發(fā)送過(guò)來(lái)的信息是bye的時(shí)候就斷開(kāi)對(duì)應(yīng)socket的鏈接,退出聊天室。以上是對(duì)服務(wù)端的分析,接下來(lái)我們來(lái)看Android客戶(hù)端。

二. 客戶(hù)端:客戶(hù)端基本與服務(wù)端一樣,我們直接上代碼吧。

 //首先還是貼出成員變量

 private Button send;
 private EditText edt_input;
 private TextView txt_content;
 private static final String SERVER_PATH = "172.16.10.18";
 private static final int PORT = 12345;
 private Socket client;
 private BufferedReader br;
 private PrintWriter pw;
 private StringBuffer content = new StringBuffer();

 private void initView() {
 send = (Button) findViewById(R.id.send);
 edt_input = (EditText) findViewById(R.id.input);
 txt_content = (TextView) findViewById(R.id.chat_content);
 // --------發(fā)起網(wǎng)絡(luò)連接-----
 new Thread() {
  public void run() {
  try {
   client = new Socket(SERVER_PATH, PORT);
   br = new BufferedReader(new InputStreamReader(
    client.getInputStream()));
   pw = new PrintWriter(new BufferedWriter(
    new OutputStreamWriter(client.getOutputStream())));
  } catch (Exception e) {
   e.printStackTrace();
  }
  }
 }.start();

 send.setOnClickListener(new OnClickListener() {

  @Override
  public void onClick(View v) {
  if (client != null && client.isConnected() && !client.isOutputShutdown()) {
   String input = edt_input.getText().toString();
   pw.println(input);
   pw.flush();
   ((EditText)findViewById(R.id.input)).setText("");
  }
  }
 });

 new Thread(this).start();
 }

首先還是傳統(tǒng)的new一個(gè)thread來(lái)建立與服務(wù)端的連接,因?yàn)橹骶€(xiàn)程不能訪(fǎng)問(wèn)網(wǎng)絡(luò),由于我們客戶(hù)端肯定是只有當(dāng)前這一個(gè)socket的,所以只有一個(gè)線(xiàn)程,不用跟服務(wù)端一樣使用線(xiàn)程池了。連接一旦建立,獲取socket的輸入輸出流來(lái)包裝成對(duì)應(yīng)的BufferedReader和PrintWriter:br = new BufferedReader(new InputStreamReader(client.getInputStream()));pw = new PrintWriter(new BufferedWriter( new OutputStreamWriter(client.getOutputStream())));由于這里只會(huì)使用一個(gè)socket,所以這里的相關(guān)變量都可以使用成員變量。然后走下來(lái)就是對(duì)send按鈕的監(jiān)聽(tīng),點(diǎn)擊發(fā)送的話(huà),把內(nèi)容發(fā)送給服務(wù)端,服務(wù)端接收到之后發(fā)送給每一個(gè)保持著鏈接的客戶(hù)端。這個(gè)activity也是實(shí)現(xiàn)了runnable接口的,接下來(lái)看run方法:

public void run() {
 while (true) {
  if (client != null && client.isConnected()
   && !client.isInputShutdown()) {
  try {
   String response;
   if ((response = br.readLine()) != null) {
   content.append(response + "\n");
   mHandler.sendEmptyMessage(UPDATE_CONTENT);
   }
  } catch (Exception e) {
   e.printStackTrace();
  }
  }
 }
 }

這里跟服務(wù)端一樣,通過(guò)一個(gè)while無(wú)限循環(huán)讀取來(lái)自服務(wù)端的信息,一旦讀取到信息之后就通過(guò)handler從子線(xiàn)程發(fā)送消息到主線(xiàn)程,主線(xiàn)程進(jìn)行數(shù)據(jù)的更新,其實(shí)就是向顯示聊天室內(nèi)容的textview追加聊天內(nèi)容并且setText上去:

Handler mHandler = new Handler() {
 public void handleMessage(Message msg) {
  switch (msg.what) {
  case UPDATE_CONTENT:
  txt_content.append(content);
  break;

  default:
  break;
  }
 };
 };

總體來(lái)說(shuō)客戶(hù)端還是比服務(wù)端容易點(diǎn),沒(méi)有涉及到并發(fā),只需要做當(dāng)前這個(gè)客戶(hù)端對(duì)應(yīng)的socket通信就行了。以上就是對(duì)socket的一個(gè)簡(jiǎn)單總結(jié)和在安卓里面的簡(jiǎn)單應(yīng)用實(shí)現(xiàn)聊天室功能。效果圖:

 

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

最新評(píng)論