UDP发送大型文件_不丢包[通俗易懂]

UDP发送大型文件_不丢包[通俗易懂]先上图1:如果对文件要求不高的话,可以使用UDP,UDP在实际测试中,丢包还是听验证的,但是效率高2:如果文件必须完整,还是使用TCP。Socket进行文件传输,比较稳妥近期的项目中要是用软件升级,系统文件有600M。一般的程序员会说,下载吗,直接下载安装就好了,我也是这样想的,素不知线下的网络的环境有多差,当时一个业务员和我说,要是能实现手机发送文件给设备就好了,毕竟大家都是用手机…

大家好,又见面了,我是你们的朋友全栈君。

先上图

UDP发送大型文件_不丢包[通俗易懂]UDP发送大型文件_不丢包[通俗易懂]

1:如果对文件要求不高的话 ,可以使用UDP,UDP在实际测试中,丢包还是听验证的,但是效率高

2:如果文件必须完整,还是使用TCP 。Socket进行文件传输,比较稳妥

近期的项目中要是用软件升级,系统文件有600M 。一般的程序员会说,下载吗 ,直接下载安装就好了 ,我也是这样想的 ,素不知线下的网络的环境 有多差,当时一个业务员和我说,要是能实现手机发送文件给设备就好了,毕竟大家都是用手机的,不然太浪费时间了 ,因为当时用的是腾讯的Im来实现即时通讯的,利用外网来发送文件,

那么问题就来了 ,这么大 ,要多久才能发完 ,那就用局域网来发送文件吧 ,第一个想到的就是UDP来实现 ,测试中发现DUP丢包问题特别明显,当时死活都找不到原因 ,后来把发送的次数和接受的次数对比打印了一下 ,命名发送了2k次,接收端只接受了500次,OK ,问题就是发送太快了 ,那么就让发送端发慢一点,

Thread.sleep(10);  一般设置5就OK了,这个可以根据自己的设备来设定休眠的时间

这样就解决问题了 ,

源码地址 :http://pan.baidu.com/s/1i4MB40l

好的,直接看代码吧 ,

1:新建一个Service,利用Bind的形式来开启服务 ,这样不必一直在后台运行 ,service中开启线程池 ,这样降低重建线程的次数,降低内存的消耗

package com.example.administrator.canchatdemo.service;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;

import com.example.administrator.canchatdemo.http.MessageReceiveRunnable;
import com.example.administrator.canchatdemo.http.MsgSendRunable;
import com.example.administrator.canchatdemo.http.UdpSend;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class SocketService extends Service {

    private static final String TAG = "message";
    private MyBinder binder = new MyBinder();
    // 设定固定数量线程的线程池
    ExecutorService executor = Executors.newFixedThreadPool(10);

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i(TAG, "=======SocketService_onCreate");
    }

    public void sendMessage(String data, String ip, int port) {
        Runnable runnable = new MsgSendRunable(data, ip, port);
        executor.execute(runnable);
    }

    public void receiveMessage(Context context, int port) {
        Runnable runnable = new MessageReceiveRunnable(context, port, true);
        executor.execute(runnable);
    }


    public void sendFileMessage(String filePath, String ip, int port) {
        Runnable runnable = new UdpSend(filePath, ip, port);
        executor.execute(runnable);
    }


//    public void receiveFileMessage(int port) {
//        Runnable runnable = new UdpReceive(port);
//        executor.execute(runnable);
//    }


    public class MyBinder extends Binder {
        public SocketService getService() {
            return SocketService.this;
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return START_NOT_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        return false;
    }

    @Override
    public void onDestroy() {
        Log.i(TAG, "=======onDestroy");
        MessageReceiveRunnable.stopReceMessage(); //停止接受消息
        super.onDestroy();
    }
}

2:新建发送文件的Runnable

package com.example.administrator.canchatdemo.http;

import android.util.Log;

import com.example.administrator.canchatdemo.entity.SendFileEntity;

import org.greenrobot.eventbus.EventBus;

import java.io.File;
import java.io.FileInputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class UdpSend implements Runnable {


    public static DatagramPacket dataPacket;


    String filePath;
    private int port;
    private String ip;

    public UdpSend(String filePath, String ip, int port) {
        this.filePath = filePath;
        this.port = port;
        this.ip = ip;
    }


    @Override
    public void run() {
        sendFile();
    }

    public void sendFile() {
        sendMessage(SendFileEntity.STATE_START, 0, "开始发送文件");
        Log.i("message", "准备发送文件");
        try {
            File file = new File(filePath);
            if (!file.exists()) {
                Log.i("message", "文件不存在");
                sendMessage(SendFileEntity.STATE_FAILED, 0, "文件不存在");
                return;
            }
            long fileSLength = file.length();
            DatagramSocket dataSocket = new DatagramSocket();
            //2,确定发送的具体的数据。
            FileInputStream in = new FileInputStream(filePath);
            byte[] buf = new byte[1024];
            int len;
            int sum = 0;
            while ((len = in.read(buf)) != -1) {
                sum += len;
                Log.e("message", "文件传输的大小==" + sum);
                int progress = (int) (sum * 100 / fileSLength);
                dataPacket = new DatagramPacket(buf, len, InetAddress.getByName(ip), port);
                dataSocket.send(dataPacket);
                updateProgress(progress);
                Thread.sleep(10);  //延时一段时间,防止传输太快。丢包
            }
            if (dataPacket != null) {
                dataSocket.close();
            }
            sendMessage(SendFileEntity.STATE_SUCCESS, 100, "发送成功");
        } catch (Exception e) {
            sendMessage(SendFileEntity.STATE_FAILED, 0, "发送失败:" + e.toString());
            Log.i("message", "发送文件异常:" + e.toString());
        }
    }

    int lsatProgress = 0;

    private void updateProgress(int progress) {
        if (progress > lsatProgress) {
            sendMessage(SendFileEntity.STATE_PROGRESS, progress, "发送中...");
            Log.e("message", "progress==" + progress);
        }
        lsatProgress = progress;
    }

    public void sendMessage(int state, int progress, String error) {
        SendFileEntity entity = new SendFileEntity(state, progress, error);
        EventBus.getDefault().post(entity);
    }

}



3:新建接收端的Runnable

  

package com.example.administrator.canchatdemo.http;


import android.content.Context;
import android.util.Log;

import com.example.administrator.canchatdemo.entity.ReceFileEntity;
import com.example.administrator.canchatdemo.util.NetUtil;

import org.greenrobot.eventbus.EventBus;

import java.io.File;
import java.io.FileOutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

/**  * Created by Administrator on 2016/4/12.  * 接受服务  */  public class MessageReceiveRunnable implements Runnable {

    private int port;
    //停止标志
    public static boolean flag;

    private DatagramSocket da = null;

    private Context context;

    public MessageReceiveRunnable(Context context, int port, boolean flag) {
        Log.i("message", "准备接受数据,开启线程");
        this.context = context;
        this.port = port;
        this.flag = flag;
    }

    public void run() {
//        int state, int progress, String error
        sendMessage(ReceFileEntity.STATE_START, 0, "准备接收");
        try {
            if (da == null) {
                da = new DatagramSocket(port);
            }
            String filePath = "/sdcard/zzz.mp4";
            File file = new File(filePath);
            if (file.exists()) {
                file.delete();
            }
            file.createNewFile();
            FileOutputStream out = new FileOutputStream(filePath);
            Log.i("message", "接收消息的开关==" + flag);
            long sum = 0;
            int number = 0;
            while (flag) {
                number++;
                byte[] buf = new byte[1024];
                DatagramPacket dp = new DatagramPacket(buf, buf.length);
                da.receive(dp);
                String ip = dp.getAddress().getHostAddress();
                String data = new String(dp.getData(), 0, dp.getLength());
                sum += buf.length;
                sendMessage(ReceFileEntity.STATE_PROGRESS, sum, "接收中...");
                Log.i("message", "==receiver===接受到了消息====ip=" + ip + "/" + sum + "/" + number);
                out.write(buf);
                if (!ip.contains(NetUtil.getLocalIp())) {
                    Log.i("message", "==receiver===接受到了别人发来的消息消息====ip=" + ip + "\ndata = " + data);
                }
            }
        } catch (Exception e) {
            sendMessage(ReceFileEntity.STATE_FAILED, 0, e.toString());
            Log.i(e.getMessage(), "服务器挂了");
        } finally {
            try {
                if (da != null)
                    da.close();
            } catch (Exception e) {
                sendMessage(ReceFileEntity.STATE_FAILED, 0, e.toString());
                e.printStackTrace();
            }
        }
    }

    public static void stopReceMessage() {
        flag = false;
    }

    public void sendMessage(int state, long progress, String error) {
        ReceFileEntity entity = new ReceFileEntity(state, progress, error);
        EventBus.getDefault().post(entity);
    }


}

4:获取IP的工具类

   

package com.example.administrator.canchatdemo.util;

import android.util.Log;
import android.widget.Toast;

import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.Enumeration;

/**  * Created by Administrator on 2016/4/14.  * 定义常量  */ public class NetUtil {
    //定义端口
    public static final int PORT_ALL = 51000;

    //获取255ip
    public static String getIpToAll() {
        try {
            String ip = getLocalIp();
            if (ip == null)
                return null;
            return getLocalIp().substring(0, 10) + "255";
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    //获取本地ip
    public static String getLocalIp() {
        try {
            for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements(); ) {
                NetworkInterface intf = en.nextElement();
                for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements(); ) {
                    InetAddress inetAddress = enumIpAddr.nextElement();
                    if (!inetAddress.isLoopbackAddress() && inetAddress instanceof Inet4Address) {
                        return inetAddress.getHostAddress().toString();
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

4:新建发送,接收的对象,用来更新界面

  

package com.example.administrator.canchatdemo.entity;

/**  * UDP文件发送状态  */  public class SendFileEntity {

    int sendState;
    String desc;
    int progress;

    public static final int STATE_START = 0;
    public static final int STATE_PROGRESS = 1;
    public static final int STATE_SUCCESS = 2;
    public static final int STATE_FAILED = 3;

    public SendFileEntity(int sendState, int progress, String desc) {
        this.sendState = sendState;
        this.progress = progress;
        this.desc = desc;
    }

    public int getProgress() {
        return progress;
    }

    public void setProgress(int progress) {
        this.progress = progress;
    }

    public int getSendTate() {
        return sendState;
    }

    public void setSendTate(int sendState) {
        this.sendState = sendState;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    @Override
    public String toString() {
        return "SendFileEntity{" +
                "sendState=" + sendState +
                ", desc='" + desc + '\'' +
                ", progress=" + progress +
                '}';
    }
}

  5:另一个对象

       

package com.example.administrator.canchatdemo.entity;

/**  * UDP文件接受状态  */  public class ReceFileEntity {

    int sendState;
    String desc;
    long progress;

    public static final int STATE_START = 0;
    public static final int STATE_PROGRESS = 1;
    public static final int STATE_SUCCESS = 2;
    public static final int STATE_FAILED = 3;

    public ReceFileEntity(int sendState, long progress, String desc) {
        this.sendState = sendState;
        this.progress = progress;
        this.desc = desc;
    }

    public long getProgress() {
        return progress;
    }

    public void setProgress(long progress) {
        this.progress = progress;
    }

    public int getSendTate() {
        return sendState;
    }

    public void setSendTate(int sendState) {
        this.sendState = sendState;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    @Override
    public String toString() {
        return "SendFileEntity{" +
                "sendState=" + sendState +
                ", desc='" + desc + '\'' +
                ", progress=" + progress +
                '}';
    }
}

6:测试界面的代码 ,因为我是用自己的手机来发送 ,自己的手机来接收。需要的伙伴可以修改设备IP。用多台手机来测试,代码自己小改一下就可以了

package com.example.administrator.canchatdemo;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;

import com.example.administrator.canchatdemo.entity.ReceFileEntity;
import com.example.administrator.canchatdemo.entity.SendFileEntity;
import com.example.administrator.canchatdemo.service.SocketService;
import com.example.administrator.canchatdemo.util.NetUtil;

import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;


public class TestChatActiivty extends Activity implements View.OnClickListener {

    EditText et_content;
    Button button_send, btn_jump;
    TextView tv_ip;

    private SocketService service;
    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder binder) {
            SocketService.MyBinder myBinder = (SocketService.MyBinder) binder;
            service = myBinder.getService();
            service.receiveMessage(TestChatActiivty.this, NetUtil.PORT_ALL);
//            service.receiveFileMessage(NetUtil.PORT_ALL);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.test_main_chat);
        EventBus.getDefault().register(TestChatActiivty.this);
        initView();
    }

    TextView tv_send_state;
    TextView tv_send_progress;
    TextView tv_send_desc;


    TextView tv_rece_state;
    TextView tv_rece_progress;
    TextView tv_rece_desc;


    private void initView() {
        tv_send_state = (TextView) findViewById(R.id.tv_send_state);
        tv_send_progress = (TextView) findViewById(R.id.tv_send_progress);
        tv_send_desc = (TextView) findViewById(R.id.tv_send_desc);

        tv_rece_state = (TextView) findViewById(R.id.tv_rece_state);
        tv_rece_progress = (TextView) findViewById(R.id.tv_rece_progress);
        tv_rece_desc = (TextView) findViewById(R.id.tv_rece_desc);


        tv_ip = (TextView) findViewById(R.id.tv_ip);
        tv_ip.setText(NetUtil.getLocalIp());
        et_content = (EditText) findViewById(R.id.et_content);
        button_send = (Button) findViewById(R.id.button_send);
        button_send.setOnClickListener(this);
        btn_jump = (Button) findViewById(R.id.btn_jump);
        btn_jump.setOnClickListener(this);
    }

    String userIPToSend = "192.168.25.105";  //锤子

    // String userIPToSend = "192.168.25.114";  //小米
    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.btn_jump:
                String content = getContent();
                service.sendMessage(content, userIPToSend, NetUtil.PORT_ALL);
                break;
            case R.id.button_send:
                String filePath = "/sdcard/bbb.mp4";
                service.sendFileMessage(filePath, userIPToSend, NetUtil.PORT_ALL);
                break;
        }

    }


    @Subscribe(threadMode = ThreadMode.MAIN)
    public void sendFileNotify(SendFileEntity entity) {
        String stateShow = "";
        Log.i("down", "=====主界面消息==" + entity.toString());
        int state = entity.getSendTate();
        if (state == SendFileEntity.STATE_START) {
            stateShow = "准备发送";
        } else if (state == SendFileEntity.STATE_PROGRESS) {
            stateShow = "发送中";
        } else if (state == SendFileEntity.STATE_SUCCESS) {
            stateShow = "发送成功";
        } else if (state == SendFileEntity.STATE_FAILED) {
            stateShow = "发送失败";
        }
        tv_send_state.setText("发送状态===>" + stateShow);
        tv_send_progress.setText("发送进度===>" + entity.getProgress());
        tv_send_desc.setText("发送描述===>" + entity.getDesc());
    }


    @Subscribe(threadMode = ThreadMode.MAIN)
    public void sendFileNotify(ReceFileEntity entity) {
        Log.i("down", "=====主界面消息==" + entity.toString());
        int state = entity.getSendTate();
        String receState = "";
        if (state == ReceFileEntity.STATE_START) {
            receState = "准备接收";
        } else if (state == ReceFileEntity.STATE_PROGRESS) {
            receState = "接收中";
        } else if (state == ReceFileEntity.STATE_SUCCESS) {
            receState = "接收成功";
        } else if (state == ReceFileEntity.STATE_FAILED) {
            receState = "接收失败";
        }
        tv_rece_state.setText("接收状态===>" + receState);
        tv_rece_progress.setText("接收大小===>" + entity.getProgress());
        tv_rece_desc.setText("发送描述===>" + entity.getDesc());
    }


    public String getContent() {
        return et_content.getText().toString().trim();
    }

    @Override
    protected void onResume() {
        super.onResume();
        Intent intent = new Intent(TestChatActiivty.this, SocketService.class);
        bindService(intent, conn, BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        unbindService(conn);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        EventBus.getDefault().unregister(TestChatActiivty.this);
    }
}

UI的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">


    <TextView
        android:id="@+id/tv_ip"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:gravity="center"
        android:text="ip"
        android:textColor="@color/myWhite"
        android:textSize="20sp" />

    <EditText
        android:id="@+id/et_content"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="50dp"
        android:ems="10"
        android:inputType="textPersonName"
        android:text="Name"
        android:visibility="gone" />


    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="发送的文件请放置根目录,命名请看代码" />

    <Button
        android:id="@+id/button_send"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:text="文件消息" />


    <Button
        android:id="@+id/btn_jump"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:text="文字消息"
        android:visibility="gone" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <TextView
            android:id="@+id/tv_send_state"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="发送状态===> 准备发送" />

        <TextView
            android:id="@+id/tv_send_progress"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="发送进度===> 30%" />

        <TextView
            android:id="@+id/tv_send_desc"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="发送进度===> 下载中..." />

    </LinearLayout>


    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="50dp"
        android:orientation="vertical">

        <TextView
            android:id="@+id/tv_rece_state"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="接受状态===> 准备发送" />

        <TextView
            android:id="@+id/tv_rece_progress"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="接收进度===> 30%" />

        <TextView
            android:id="@+id/tv_rece_desc"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="接收进度===> 下载中..." />

    </LinearLayout>


</LinearLayout>

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/157473.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • Windows文件服务器文件审计,文件监控软件,File_System_Auditor2.53安装教程[通俗易懂]

    Windows文件服务器文件审计,文件监控软件,File_System_Auditor2.53安装教程[通俗易懂]这里写目录标题一、事前准备二、安装过程2.1、安装.net2.02.2、安装File_System_Auditor2.2.1、下载[File_System_Auditor安装包](https://download.csdn.net/download/weixin_42523454/20592714),解压2.2.2、先安装FSASetup_Console_1.522.2.3、一直下一步,安装完成之后,导入注册表2.2.4、打开激活软件2.2.5、输入资料,获取授权书,2号处为成功2.2.6、卸载FSASe

  • Stata Kendall 相关系数作图

    Stata Kendall 相关系数作图StataKendall相关系数作图回答Superficial.的问题,测试CSDN的markdown发帖功能如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML图表FLowchart流程图导出与导入导出导入此帖目的有二:回答Superficial.的问题,测试CSDN的markdown发帖功能如何插入一段漂亮的代码片去博客设置页面,选择

  • 图集谷-写真集-爬虫-1.0[通俗易懂]

    图集谷-写真集-爬虫-1.0[通俗易懂]图集谷写真集爬虫

  • 模式先行全新解读微商分销系统

    模式先行全新解读微商分销系统在微商迅速发展的时代下,微商分销系统的需求也日渐上升,这对微商分销系统也有着更高的要求。一款好的微商分销系统需要具备“快速搭建、功能强大、顺畅分销、管理有序,支付便捷,扩展性强”等条件,模式先行,分销模式也是恒量分销系统的一个非常重要的指标。微商分销系统通常为一级/二级/三级分销模式,即品牌商可以发展一级/二级/三级分销商,每一级分销商均可以往下再发展一级/二级分销商。我们以三级分销模式为例解读A…

  • ubuntu 安装gcc「建议收藏」

    ubuntu 安装gcc「建议收藏」一定要记得先update,不然找不到gccsudoapt-getupdate然后输入下述命令即可sudoapt-getinstallgcc

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号