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)
blank

相关推荐

发表回复

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

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