大家好,又见面了,我是你们的朋友全栈君。
先上图
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中开启线程池 ,这样降低重建线程的次数,降低内存的消耗
代码语言:javascript复制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
代码语言:javascript复制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
代码语言:javascript复制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的工具类
代码语言:javascript复制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:新建发送,接收的对象,用来更新界面
代码语言:javascript复制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:另一个对象
代码语言:javascript复制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。用多台手机来测试,代码自己小改一下就可以了
代码语言:javascript复制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
代码语言:javascript复制<?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