最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Android完整Socket解決方案

 更新時(shí)間:2018年02月09日 09:24:56   投稿:laozhang  
這篇文章主要介紹了Android完整Socket解決方案并通過(guò)代碼給大家做了實(shí)例分析,對(duì)此有興趣的朋友學(xué)習(xí)下。

整體步驟流程

先來(lái)說(shuō)一下整體的步驟思路吧:

發(fā)送 UDP 廣播,大家都知道 UDP 廣播的特性是整個(gè)網(wǎng)段的設(shè)備都可以收到這個(gè)消息。
接收方收到了 UDP 的廣播,將自己的 ip 地址,和雙方約定的端口號(hào),回復(fù)給 UDP 的發(fā)送方。
發(fā)送方拿到了對(duì)方的 ip 地址以及端口號(hào),就可以發(fā)起 TCP 請(qǐng)求了,建立 TCP 連接。
保持一個(gè) TCP 心跳,如果發(fā)現(xiàn)對(duì)方不在了,超時(shí)重復(fù) 1 步驟,重新建立聯(lián)系。

整體的步驟就和上述的一樣,下面用代碼展開(kāi):

搭建 UDP 模塊

public UDPSocket(Context context) {
  this.mContext = context;
  int cpuNumbers = Runtime.getRuntime().availableProcessors();
  // 根據(jù)CPU數(shù)目初始化線程池
  mThreadPool = Executors.newFixedThreadPool(cpuNumbers * Config.POOL_SIZE);
  // 記錄創(chuàng)建對(duì)象時(shí)的時(shí)間
  lastReceiveTime = System.currentTimeMillis();
  messageReceiveList = new ArrayList<>();
  Log.d(TAG, "創(chuàng)建 UDP 對(duì)象");
//  createUser();
 }

首先進(jìn)行一些初始化操作,準(zhǔn)備線程池,記錄對(duì)象初始的時(shí)間等等。

public void startUDPSocket() {
  if (client != null) return;
  try {
   // 表明這個(gè) Socket 在設(shè)置的端口上監(jiān)聽(tīng)數(shù)據(jù)。
   client = new DatagramSocket(CLIENT_PORT);
   client.setReuseAddress(true);
   if (receivePacket == null) {
    // 創(chuàng)建接受數(shù)據(jù)的 packet
    receivePacket = new DatagramPacket(receiveByte, BUFFER_LENGTH);
   }
   startSocketThread();
  } catch (SocketException e) {
   e.printStackTrace();
  }
 }

緊接著就創(chuàng)建了真正的一個(gè) UDP Socket 端,DatagramSocket,注意這里傳入的端口號(hào) CLIENT_PORT 的意思是這個(gè) DatagramSocket 在此端口號(hào)接收消息。

/**
  * 開(kāi)啟發(fā)送數(shù)據(jù)的線程
  */
 private void startSocketThread() {
  clientThread = new Thread(new Runnable() {
   @Override
   public void run() {
    receiveMessage();
   }
  });
  isThreadRunning = true;
  clientThread.start();
  Log.d(TAG, "開(kāi)啟 UDP 數(shù)據(jù)接收線程");
  startHeartbeatTimer();
 }

我們都知道 Socket 中要處理數(shù)據(jù)的發(fā)送和接收,并且發(fā)送和接收都是阻塞的,應(yīng)該放在子線程中,這里就開(kāi)啟了一個(gè)線程,來(lái)處理接收到的 UDP 消息(UDP 模塊上一篇文章講得比較詳細(xì)了,所以這里就不詳細(xì)展開(kāi)了)

/**
  * 處理接受到的消息
  */
 private void receiveMessage() {
  while (isThreadRunning) {
   try {
    if (client != null) {
     client.receive(receivePacket);
    }
    lastReceiveTime = System.currentTimeMillis();
    Log.d(TAG, "receive packet success...");
   } catch (IOException e) {
    Log.e(TAG, "UDP數(shù)據(jù)包接收失敗!線程停止");
    stopUDPSocket();
    e.printStackTrace();
    return;
   }
   if (receivePacket == null || receivePacket.getLength() == 0) {
    Log.e(TAG, "無(wú)法接收UDP數(shù)據(jù)或者接收到的UDP數(shù)據(jù)為空");
    continue;
   }
   String strReceive = new String(receivePacket.getData(), receivePacket.getOffset(), receivePacket.getLength());
   Log.d(TAG, strReceive + " from " + receivePacket.getAddress().getHostAddress() + ":" + receivePacket.getPort());
   //解析接收到的 json 信息
   notifyMessageReceive(strReceive);
   // 每次接收完UDP數(shù)據(jù)后,重置長(zhǎng)度。否則可能會(huì)導(dǎo)致下次收到數(shù)據(jù)包被截?cái)唷?
   if (receivePacket != null) {
    receivePacket.setLength(BUFFER_LENGTH);
   }
  }
 }

在子線程接收 UDP 數(shù)據(jù),并且 notifyMessageReceive 方法通過(guò)接口來(lái)向外通知消息。

/**
  * 發(fā)送心跳包
  *
  * @param message
  */
 public void sendMessage(final String message) {
  mThreadPool.execute(new Runnable() {
   @Override
   public void run() {
    try {
     BROADCAST_IP = WifiUtil.getBroadcastAddress();
     Log.d(TAG, "BROADCAST_IP:" + BROADCAST_IP);
     InetAddress targetAddress = InetAddress.getByName(BROADCAST_IP);
     DatagramPacket packet = new DatagramPacket(message.getBytes(), message.length(), targetAddress, CLIENT_PORT);
     client.send(packet);
     // 數(shù)據(jù)發(fā)送事件
     Log.d(TAG, "數(shù)據(jù)發(fā)送成功");
    } catch (UnknownHostException e) {
     e.printStackTrace();
    } catch (IOException e) {
     e.printStackTrace();
    }
   }
  });
 }

接著 startHeartbeatTimer 開(kāi)啟一個(gè)心跳線程,每間隔五秒,就去廣播一個(gè) UDP 消息。注意這里 getBroadcastAddress 是獲取的網(wǎng)段 ip,發(fā)送這個(gè) UDP 消息的時(shí)候,整個(gè)網(wǎng)段的所有設(shè)備都可以接收到。

到此為止,我們發(fā)送端的 UDP 算是搭建完成了。

搭建 TCP 模塊

接下來(lái) TCP 模塊該出場(chǎng)了,UDP 發(fā)送心跳廣播的目的就是找到對(duì)應(yīng)設(shè)備的 ip 地址和約定好的端口,所以在 UDP 數(shù)據(jù)的接收方法里:

/**
  * 處理 udp 收到的消息
  *
  * @param message
  */
 private void handleUdpMessage(String message) {
  try {
   JSONObject jsonObject = new JSONObject(message);
   String ip = jsonObject.optString(Config.TCP_IP);
   String port = jsonObject.optString(Config.TCP_PORT);
   if (!TextUtils.isEmpty(ip) && !TextUtils.isEmpty(port)) {
    startTcpConnection(ip, port);
   }
  } catch (JSONException e) {
   e.printStackTrace();
  }
 }

這個(gè)方法的目的就是取到對(duì)方 UDPServer 端,發(fā)給我的 UDP 消息,將它的 ip 地址告訴了我,以及我們提前約定好的端口號(hào)。

怎么獲得一個(gè)設(shè)備的 ip 呢?

public String getLocalIPAddress() {
  WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
  return intToIp(wifiInfo.getIpAddress());
 }
 private static String intToIp(int i) {
  return (i & 0xFF) + "." + ((i >> 8) & 0xFF) + "." + ((i >> 16) & 0xFF) + "."
    + ((i >> 24) & 0xFF);
 }

現(xiàn)在拿到了對(duì)方的 ip,以及約定好的端口號(hào),終于可以開(kāi)啟一個(gè) TCP 客戶端了。

private boolean startTcpConnection(final String ip, final int port) {
  try {
   if (mSocket == null) {
    mSocket = new Socket(ip, port);
    mSocket.setKeepAlive(true);
    mSocket.setTcpNoDelay(true);
    mSocket.setReuseAddress(true);
   }
   InputStream is = mSocket.getInputStream();
   br = new BufferedReader(new InputStreamReader(is));
   OutputStream os = mSocket.getOutputStream();
   pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(os)), true);
   Log.d(TAG, "tcp 創(chuàng)建成功...");
   return true;
  } catch (Exception e) {
   e.printStackTrace();
  }
  return false;
 }

當(dāng) TCP 客戶端成功建立的時(shí)候,我們就可以通過(guò) TCP Socket 來(lái)發(fā)送和接收消息了。

細(xì)節(jié)處理

接下來(lái)就是一些細(xì)節(jié)處理了,比如我們的 UDP 心跳,當(dāng) TCP 建立成功之時(shí),我們要停止 UDP 的心跳:

if (startTcpConnection(ip, Integer.valueOf(port))) {// 嘗試建立 TCP 連接
     if (mListener != null) {
      mListener.onSuccess();
     }
     startReceiveTcpThread();
     startHeartbeatTimer();
    } else {
     if (mListener != null) {
      mListener.onFailed(Config.ErrorCode.CREATE_TCP_ERROR);
     }
    }
   // TCP已經(jīng)成功建立連接,停止 UDP 的心跳包。
   public void stopHeartbeatTimer() {
    if (timer != null) {
     timer.exit();
     timer = null;
    }
 }

對(duì) TCP 連接進(jìn)行心跳保護(hù):

/**
  * 啟動(dòng)心跳
  */
 private void startHeartbeatTimer() {
  if (timer == null) {
   timer = new HeartbeatTimer();
  }
  timer.setOnScheduleListener(new HeartbeatTimer.OnScheduleListener() {
   @Override
   public void onSchedule() {
    Log.d(TAG, "timer is onSchedule...");
    long duration = System.currentTimeMillis() - lastReceiveTime;
    Log.d(TAG, "duration:" + duration);
    if (duration > TIME_OUT) {//若超過(guò)十五秒都沒(méi)收到我的心跳包,則認(rèn)為對(duì)方不在線。
     Log.d(TAG, "tcp ping 超時(shí),對(duì)方已經(jīng)下線");
     stopTcpConnection();
     if (mListener != null) {
      mListener.onFailed(Config.ErrorCode.PING_TCP_TIMEOUT);
     }
    } else if (duration > HEARTBEAT_MESSAGE_DURATION) {//若超過(guò)兩秒他沒(méi)收到我的心跳包,則重新發(fā)一個(gè)。
     JSONObject jsonObject = new JSONObject();
     try {
      jsonObject.put(Config.MSG, Config.PING);
     } catch (JSONException e) {
      e.printStackTrace();
     }
     sendTcpMessage(jsonObject.toString());
    }
   }
  });
  timer.startTimer(0, 1000 * 2);
 }

首先會(huì)每隔兩秒,就給對(duì)方發(fā)送一個(gè) ping 包,看看對(duì)面在不在,如果超過(guò) 15 秒還沒(méi)有回復(fù)我,那就說(shuō)明對(duì)方掉線了,關(guān)閉我這邊的 TCP 端。進(jìn)入 onFailed 方法。

@Override
    public void onFailed(int errorCode) {// tcp 異常處理
     switch (errorCode) {
      case Config.ErrorCode.CREATE_TCP_ERROR:
       break;
      case Config.ErrorCode.PING_TCP_TIMEOUT:
       udpSocket.startHeartbeatTimer();
       tcpSocket = null;
       break;
     }
    }

當(dāng) TCP 連接超時(shí),我就會(huì)重新啟動(dòng) UDP 的廣播心跳,尋找等待連接的設(shè)備。進(jìn)入下一個(gè)步驟循環(huán)。

對(duì)于數(shù)據(jù)傳輸?shù)母袷桨〉鹊燃?xì)節(jié),這個(gè)和業(yè)務(wù)相關(guān)。自己來(lái)定就好。

還可以根據(jù)自己業(yè)務(wù)的模式,是 CPU 密集型啊,還是 IO 密集型啊,來(lái)開(kāi)啟不同的線程通道。這個(gè)就涉及線程的知識(shí)了。

源碼分享:https://github.com/itsMelo/AndroidSocket

相關(guān)文章

  • Android APK 32位和64位的區(qū)別及說(shuō)明

    Android APK 32位和64位的區(qū)別及說(shuō)明

    這篇文章主要介紹了Android APK 32位和64位的區(qū)別及說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-11-11
  • 深入了解Android中GestureDetector的定義與使用

    深入了解Android中GestureDetector的定義與使用

    Android中的GestureDetector?可以使用?MotionEvents?檢測(cè)各種手勢(shì)和事件,非常的好用。本文將會(huì)通過(guò)幾個(gè)具體的例子來(lái)講解一下GestureDetector的具體使用方法,需要的可以參考一下
    2023-01-01
  • Android實(shí)現(xiàn)下載m3u8視頻文件問(wèn)題解決

    Android實(shí)現(xiàn)下載m3u8視頻文件問(wèn)題解決

    這篇文章主要介紹了Android實(shí)現(xiàn)下載m3u8視頻文件,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧
    2023-01-01
  • 深入學(xué)習(xí)Android?ANR?的原理分析及解決辦法

    深入學(xué)習(xí)Android?ANR?的原理分析及解決辦法

    Android系統(tǒng)中,AMS和WMS會(huì)檢測(cè)App的響應(yīng)時(shí)間,如果App在特定時(shí)間無(wú)法相應(yīng)屏幕觸摸或鍵盤輸入時(shí)間,或者特定事件沒(méi)有處理完畢,就會(huì)出現(xiàn)ANR。本文將帶領(lǐng)大學(xué)深入學(xué)習(xí)一下ANR的原理及解決辦法,感興趣的同學(xué)可以學(xué)習(xí)一下
    2021-11-11
  • 分享Android微信紅包插件

    分享Android微信紅包插件

    這篇文章為大家準(zhǔn)備了Android微信紅包插件,輕輕松松幫助大家實(shí)現(xiàn)微信紅包功能,感興趣的小伙伴們可以參考一下
    2015-12-12
  • 解析Android開(kāi)發(fā)優(yōu)化之:軟引用與弱引用的應(yīng)用

    解析Android開(kāi)發(fā)優(yōu)化之:軟引用與弱引用的應(yīng)用

    Java從JDK1.2版本開(kāi)始,就把對(duì)象的引用分為四種級(jí)別,從而使程序能更加靈活的控制對(duì)象的生命周期。這四種級(jí)別由高到低依次為:強(qiáng)引用、軟引用、弱引用和虛引用,本篇文章重點(diǎn)介紹一下軟引用和弱引用
    2013-05-05
  • Android開(kāi)發(fā)ImageView圖片無(wú)法顯示解決過(guò)程

    Android開(kāi)發(fā)ImageView圖片無(wú)法顯示解決過(guò)程

    在Android中ImageView無(wú)法顯示加載的本地SDCard圖片:過(guò)程為先調(diào)用本地照相機(jī)程序攝像,然后將拍攝的圖片加載在ImageView中顯示,具體解決方法如下,感興趣的朋友可以參考下哈
    2013-06-06
  • Android編程實(shí)現(xiàn)ListView內(nèi)容無(wú)限循環(huán)顯示的方法

    Android編程實(shí)現(xiàn)ListView內(nèi)容無(wú)限循環(huán)顯示的方法

    這篇文章主要介紹了Android編程實(shí)現(xiàn)ListView內(nèi)容無(wú)限循環(huán)顯示的方法,通過(guò)繼承Adapter類實(shí)現(xiàn)ListView中的數(shù)據(jù)無(wú)限循環(huán)顯示功能,需要的朋友可以參考下
    2017-06-06
  • Android中設(shè)置組件半透明和透明的效果示例

    Android中設(shè)置組件半透明和透明的效果示例

    這篇文章主要給大家介紹了Android中設(shè)置組件半透明和透明效果的相關(guān)資料,文中給出了詳細(xì)的介紹和示例代碼,對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起看看吧。
    2017-05-05
  • Android基礎(chǔ)知識(shí)之單點(diǎn)觸摸

    Android基礎(chǔ)知識(shí)之單點(diǎn)觸摸

    這篇文章主要為大家詳細(xì)介紹了Android基礎(chǔ)知識(shí)之單點(diǎn)觸摸,很簡(jiǎn)單的操作,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-06-06

最新評(píng)論

永福县| 吉首市| 南岸区| 介休市| 北碚区| 阳高县| 临沧市| 健康| 泸溪县| 邢台市| 扎鲁特旗| 泸州市| 太仆寺旗| 青州市| 和平县| 锦州市| 长泰县| 金乡县| 贵南县| 榆林市| 昌江| 本溪| 班玛县| 景泰县| 滦南县| 日喀则市| 紫阳县| 兴安县| 日照市| 茌平县| 龙游县| 义乌市| 和顺县| 兴义市| 泰安市| 东港市| 科尔| 许昌市| 正镶白旗| 安西县| 南岸区|