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>
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/157473.html原文链接:https://javaforall.cn
相关文章
- crontab 用法(执行python文件)[通俗易懂]
- vscode怎么html和php混编,vscode如何编译运行html文件[通俗易懂]
- 解析MP4文件中的sps和pps[通俗易懂]
- 您的CPU支持该TensorFlow二进制文件未编译为使用的指令:AVX AVX2[通俗易懂]
- Linux文件的rwx含义,文件(目录)rwx权限的意义[通俗易懂]
- linux 内网文件传输工具_Linux服务器局域网(内网)快速传输文件[通俗易懂]
- python–xlsx文件的读写[通俗易懂]
- 解决在打开word时,出现 “word 在试图打开文件时遇到错误” 的问题(亲测有效)[通俗易懂]
- 虚拟机中安装GHO文件配置说明[通俗易懂]
- Oracle 根据dbf文件的数据恢复[通俗易懂]
- C#文件流FileStream的用法[通俗易懂]
- linux(4)Linux 文件内容查看[通俗易懂]
- 文件上传-文件名长度绕过白名单限制[通俗易懂]
- 使用JAVA读写Properties属性文件详解编程语言
- 实现Linux脚本文件一键执行(linux脚本文件执行)
- Linux下快速批量重命名文件(批量重命名linux)
- Linux中移动文件的常用命令及其用法(linux移动文件的命令)
- Linux 文件操作,轻松管理您的文件!(linux文件操作)
- 行神奇的操作Linux文件打开:命令行的魔力操作(linux文件打开命令)
- 如何在Linux中加载DLL文件?(linux加载dll)
- 器文件妙用Linux:优雅高效删除服务器文件(删除linux服务)
- MySQL实现本地文件上传功能(mysql 上传本地文件)
- 文件的导入 Oracle中CSV格式文件的快速导入(oracle中csv格式)
- php删除与复制文件夹及其文件夹下所有文件的实现代码
- 解析PHP实现下载文件的两种方法