你不知道的UDP傳輸(局域網)

你不知道的UDP傳輸(局域網)

近日閑來無事 特將工作中研究過的小功能提出來,分享給大家。是一篇有關于UDP傳輸文件、視頻的demo。當然 網上肯定已經存在很多了,我也只是將我自己研究的心得來在這里給大家講一下,說的不好 大家多多擔待 !

首先 先為大家簡單的講一下TCP與UDP的區別(真的是簡單的講一下 做個鋪墊嘛)

相同:都是傳輸層

不同:使用TCP協議傳輸數據,當數據從A端傳到B端后,B端會發送一個確認包(ACK包)給A端,告知A端數據我已收到!有重傳機制,UDP協議就沒有這種確認機制!

UDP 協議是無線連接的數據傳輸并且無重傳機制,很大的可能會造成丟包、收到重復包、亂序的情況,并且無法做這種情況作出處理 只能選擇再次發送(如非特殊要求,估計大家不會使用的-但是 我遇到了 哎...)


好了 上面簡單的介紹了TCP與UDP的區別,那么下面 咱們該切入正題了

首先 基本配置

<uses-permission android:name="android.permission.INTERNET" />
   <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
   <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
   <uses-permission android:name="android.permission.RECORD_AUDIO" />
   <uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
   <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
   <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
   <uses-permission android:name="android.permission.MOUNT_FORMAT_FILESYSTEMS" />
   <uses-permission android:name="android.permission.CAMERA" />
   ``` 

鏈接網絡(局域網) 、SD卡寫入寫出、照相機等。

此demo只有一個APP即可 即是客戶端也是服務端

首先在MainActivity中啟動線程發送自己的信息讓另一個手機的demo接收。 整體框架全部基于Thread,Handler,Socket發送與接收消息

//廣播上線(自己)
private void reBroad() {
    Msg msg = new Msg();
    msg.setSendUserName(user.getName());
    msg.setSendUserIp(user.getIp());
    msg.setReceiveUserIp(Tools.getBroadCastIP());
    msg.setMsgType(Tools.CMD_ONLINE);//通知上線命令
    msg.setDate(Tools.getTimel());
    // 發送廣播通知上線
    tools.sendMsg(msg);
}

// 心跳響應廣播
class HeartBroadCast extends Thread {
public void run() {
while (!mainA.isPaused) {
try {
sleep(10000);
} catch (InterruptedException e) {
}
Msg msgBroad = new Msg();
msgBroad.setSendUserName(mainA.user.getName());
msgBroad.setSendUserIp(mainA.user.getIp());
msgBroad.setMsgType(Tools.CMD_CHECK);
msgBroad.setReceiveUserIp(Tools.getBroadCastIP());
msgBroad.setDate(Tools.getTimel());
// 發送消息
sendMsg(msgBroad);
}
}
}

// 檢測用戶是否在線,如果超過15說明用戶已離線,秒則從列表中清除該用戶
class CheckUserOnline extends Thread{
    @Override
    public void run()
    {
        while(!mainA.isPaused)
        {
            for (int i = 0; i < mainA.userList.size(); i++)
            {
                long cm=System.currentTimeMillis()-mainA.userList.get(i).getOnlineTime();
                if(cm>15000)
                {
                    //刷新列表
                    TipsMain(Tools.DESTROYUSER,i);
                }
            }
            try {
                sleep(8000);
                //防掉線,廣播
                //Tips(Tools.CONSTANTBROAD,null);
            } catch (InterruptedException e) {
            }
        }
    }
}
``` 

依照上面大家可以清楚的看到 在起始的MainActivity中首先就去發送自己上線的通知,另一端同樣的啟動線程隨時的接收---保持自己能夠隨時和B端連接上。大家可以看到幾乎所有的發送全部由Thread中進行操作(原因很簡單 我就不說明了)。

在之后的操作 就會全部通過message發送到主線程去進行UI(數據)的更新,給大家看下在發送和接收消息的時候應該如何對的數據進行處理吧。

//發送消息線程
   class UdpSend extends Thread {
       Msg msg = null;

       UdpSend(Msg msg) {
           this.msg = msg;
       }

       @Override
       public void run() {
           try {
               LogUtil.defLog("Running--"+msg);
               byte[] data = Tools.toByteArray(msg);
               //1、創建DatagramSocket用于UDP數據傳送
               DatagramSocket ds = new DatagramSocket(Tools.PORT_SEND);
               //2、創建需要發送的數據包
               DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getByName(msg.getReceiveUserIp()), Tools.PORT_RECEIVE);
               //3、發送
               packet.setData(data);
               ds.send(packet);
               //4、關閉連接
               ds.close();
           } catch (IOException e) {
               e.printStackTrace();
           }
       }
   }

   //接收消息
   public void receiveMsg() {
       new UdpReceive().start();
   }

   class UdpReceive extends Thread {
       Msg msg = null;

       UdpReceive() {
       }

       public void run() {
           //消息循環
           while (true) {
               try {
                   //1、創建DatagramSocket;
                   DatagramSocket ds = new DatagramSocket(Tools.PORT_RECEIVE);
                   //2、創建數據包,用于接收內容。
                   byte[] data = new byte[1024 * 4];
                   DatagramPacket packet = new DatagramPacket(data, data.length);
                   //3、接收數據
                   packet.setData(data);
                   ds.receive(packet);
                   byte[] data2 = new byte[packet.getLength()];
                   System.arraycopy(data, 0, data2, 0, data2.length);// 得到接收的數據
                   Msg msg = (Msg) Tools.toObject(data2);
                   ds.close();
                   //解析消息
                   parse(msg);
               } catch (Exception e) {
               }
           }
       }
   }
   ``` 
 
 同樣需要啟動線程,前面介紹了 防止數據在傳輸過程中出現問題,當然 假如真的出現了問題 我可以告訴你一個更簡單的方法 ---重新發送!!
 我現在依舊覺得我每一次的成功都是我實驗前的祈禱造成的,哈哈 廢話不多說了 繼續講解。
 
 Socket是鏈接中一定要存在的橋梁傳送數據全靠他了啊。
 
 然后接到的數據就要開始解析啦

發送數據將數據壓縮成流文件,Socket創建(要接受的id,鏈接號)
public void creatClient() throws Exception {
Socket s = new Socket(msg.getSendUserIp(), 2222);
// 讀文件w
File file = new File(path);
BufferedInputStream is = new BufferedInputStream(new FileInputStream(file));
BufferedOutputStream os =new BufferedOutputStream( s.getOutputStream());
// 讀文件
double n = 1;
byte[] data = new byte[Tools.byteSize];// 每次讀取的字節數
int len=-1;
while ((len=is.read(data))!= -1) {
os.write(data,0,len);
Tools.sendProgress+=len;//進度
}
Tools.sendProgress=-1;
is.close();
os.flush();
os.close();
}
```

ServerSocket接收(連接號) 開始對文件進行解壓

 ServerSocket ss = new ServerSocket(2222);
     Socket s = new Socket();
     s = ss.accept();
     File filesDir = connectB.getExternalFilesDir(null);
     if (!filesDir.exists()) {
         filesDir.mkdirs();
     }
     String name =Tools.newfileName;
     File file = new File(filesDir + name);
     LogUtil.defLog("存數據--" + file.getAbsolutePath());
     if (!file.exists()) {
         file.createNewFile();
     }
     BufferedInputStream is = new BufferedInputStream(s.getInputStream()); // 讀進
     BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(file));// 寫出

     Thread.sleep(1000);
     byte[] data = new byte[Tools.byteSize];// 每次讀取的字節數
     int len = -1;
     while ((len = is.read(data)) != -1) {
         os.write(data, 0, len);
         Tools.sendProgress += len;// 進度
     }
     Tools.sendProgress = -1;
     is.close();
     os.flush();
     os.close();
     Tools.TipsMain(Tools.INTENT, connectB.getFilesDir().getAbsolutePath());
     s.close();
     Tools.TipsMain(Tools.SHOW, "接收完成" + Tools.newfileName);
     ``` 

---
大家有木有注意到我將文件存放的位置,沒錯 我存在了內存中的包里面
簡單的原因 存放在這里 我才能將里面的數據顯示出來(其他位置可能也行 不過我沒有試過)

** 總結:其實UDP傳輸很簡單的 只要將里面的邏輯關系搞清楚 整套的東西很好出來 我會在明年的時候 將點對點的視頻通話研究出來 到時這篇文章我在進行更新現在的代碼我放進我的[guyhub](https://github.com/SmithGao/HelmetClient)上了 有需要的童鞋可以下載看看 記得拿兩個手機哈 一個是沒法測試的 .希望你們可以有所學習,另外你會發現更多地驚喜哦 歡迎start**
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容