大家好,又见面了,我是你们的朋友全栈君。
先上图
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账号...