当前位置: 移动技术网 > 移动技术>移动开发>Android > Android中Socket大文件断点上传示例

Android中Socket大文件断点上传示例

2019年07月24日  | 移动技术网移动技术  | 我要评论

什么是socket?     

所谓socket通常也称作“套接字”,用于描述ip地址和端口,是一个通信连的句柄,应用程序通常通过“套接字”向网络发送请求或者应答网络请求,它就是网络通信过程中端点的抽象表示。它主要包括以下两个协议:

tcp (transmission control protocol 传输控制协议):传输控制协议,提供的是面向连接、可靠的字节流服务。当客户和服务器彼此交换数据前,必须先在双方之间建立一个tcp连接,之后才能传输数据。tcp提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端。

udp (user datagram protocl 用户数据报协议):用户数据报协议,是一个简单的面向数据报的运输层协议。udp不提供可靠性,它只是把应用程序传给ip层的数据报发送出去,但是并不能保证它们能到达目的地。由于udp在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快。

详细解说如下:

tcp传输和udp不一样,tcp传输是流式的,必须先建立连接,然后数据流沿已连接的线路(虚电路)传输。因此tcp的数据流不会像udp数据报一样,每个数据报都要包含目标地址和端口,因为每个数据报要单独路由。tcp传输则只需要在建立连接时指定目标地址和端口就可以了。

形象的讲,tcp就像打电话,udp就像发电报。宏观上来看udp是不分客户端和服务端的。通信双方是平等的。微观上来讲只相对一个报文,发送端是客户端,监听端是服务端。发送端把数据报发给路由器就像把电报发给了邮局,后面的事情就是发送者无法控制,也无从知晓的了。所以说是不可靠的,可能会出现报文丢失而无从知晓。就像每张电报都要有收件人一样,每个数据报都要有目的地址和端口。

而tcp每次连接都是分客户端和服务端的。连接的发起者(相当与拨号打电话的人)是客户端,监听者(相当于在电话边等着接电话的人)是服务端。发起者指定要连接的服务器地址和端口(相当于拨号),监听者通过和发起者三次握手建立连接(相当于听到电话响去接电话)。建立连接后双方可以互相发送和接受数据(打电话)。

java如何操作socket?

值得一提的是,java分别为tcp和udp提供了相应的类,tcp是java.net中提供了两个类socket和serversocket,分别用来表示双向连接的客户端和服务端。这是两个封装得非常好的类,使用起来很方便!udp是java.net.datagramsocket.

127.0.0.1是回路地址,用于测试,相当于localhost本机地址,没有网卡,不设dns都可以访问,端口地址在0~65535之间,其中0~1023之间的端口是用于一些知名的网络服务和应用,用户的普通网络应用程序应该使用1024以上的端口.

socket通信模型如下:

服务器,使用serversocket监听指定的端口,端口可以随意指定(由于1024以下的端口通常属于保留端口,在一些操作系统中不可以随意使用,所以建议使用大于1024的端口),等待客户连接请求,客户连接后,会话产生;在完成会话后,关闭连接。

客户端,使用java socket通信对网络上某一个服务器的某一个端口发出连接请求,一旦连接成功,打开会话;会话完成后,关闭socket。客户端不需要指定打开的端口,通常临时的、动态的分配一个1024以上的端口。

tcp网络连接模型:

android客户端程序代分析:

uploadactivity.java  

package com.android.upload; 
import java.io.file;  
import java.io.outputstream;  
import java.io.pushbackinputstream;  
import java.io.randomaccessfile;  
import java.net.socket;  
  
import android.app.activity;  
import android.os.bundle;  
import android.os.environment;  
import android.os.handler;  
import android.os.message;  
import android.view.view;  
import android.view.view.onclicklistener; 
import android.widget.button;  
import android.widget.edittext;  
import android.widget.progressbar;  
import android.widget.textview;  
import android.widget.toast;  
  
import com.android.service.uploadlogservice;  
import com.android.socket.utils.streamtool; 
 
  
public class uploadactivity extends activity {  
  private edittext filenametext;  
  private textview resulview;  
  private progressbar uploadbar;  
  private uploadlogservice logservice;  
  private boolean start=true; 
  private handler handler = new handler(){  
    @override  
    public void handlemessage(message msg) {  
      int length = msg.getdata().getint("size");  
      uploadbar.setprogress(length);  
      float num = (float)uploadbar.getprogress()/(float)uploadbar.getmax();  
      int result = (int)(num * 100);  
      resulview.settext(result+ "%");  
      if(uploadbar.getprogress()==uploadbar.getmax()){  
        toast.maketext(uploadactivity.this, r.string.success, 1).show();  
      }  
    }  
  };  
    
  @override  
  public void oncreate(bundle savedinstancestate) {  
    super.oncreate(savedinstancestate);  
    setcontentview(r.layout.main);  
      
    logservice = new uploadlogservice(this);  
    filenametext = (edittext)this.findviewbyid(r.id.filename);  
    uploadbar = (progressbar) this.findviewbyid(r.id.uploadbar);  
    resulview = (textview)this.findviewbyid(r.id.result);  
    button button =(button)this.findviewbyid(r.id.button);  
    button button1 =(button)this.findviewbyid(r.id.stop);  
    button1 .setonclicklistener(new onclicklistener() { 
       
      @override 
      public void onclick(view v) { 
        start=false; 
         
      } 
    }); 
    button.setonclicklistener(new view.onclicklistener() {  
      @override  
      public void onclick(view v) {  
        start=true; 
        string filename = filenametext.gettext().tostring();  
        if(environment.getexternalstoragestate().equals(environment.media_mounted)){  
          file uploadfile = new file(environment.getexternalstoragedirectory(), filename);  
          if(uploadfile.exists()){  
            uploadfile(uploadfile);  
          }else{  
            toast.maketext(uploadactivity.this, r.string.filenotexsit, 1).show();  
          }  
        }else{  
          toast.maketext(uploadactivity.this, r.string.sdcarderror, 1).show();  
        }  
      }  
    });  
  }  
  /** 
   * 上传文件 
   * @param uploadfile 
   */  
  private void uploadfile(final file uploadfile) {  
    new thread(new runnable() {       
      @override  
      public void run() {  
        try {  
          uploadbar.setmax((int)uploadfile.length());  
          string souceid = logservice.getbindid(uploadfile);  
          string head = "content-length="+ uploadfile.length() + ";filename="+ uploadfile.getname() + ";sourceid="+  
            (souceid==null? "" : souceid)+"\r\n";  
          socket socket = new socket("192.168.1.78",7878);  
          outputstream outstream = socket.getoutputstream();  
          outstream.write(head.getbytes());  
            
          pushbackinputstream instream = new pushbackinputstream(socket.getinputstream());    
          string response = streamtool.readline(instream);  
          string[] items = response.split(";");  
          string responseid = items[0].substring(items[0].indexof("=")+1);  
          string position = items[1].substring(items[1].indexof("=")+1);  
          if(souceid==null){//代表原来没有上传过此文件,往数据库添加一条绑定记录  
            logservice.save(responseid, uploadfile);  
          }  
          randomaccessfile fileoutstream = new randomaccessfile(uploadfile, "r");  
          fileoutstream.seek(integer.valueof(position));  
          byte[] buffer = new byte[1024];  
          int len = -1;  
          int length = integer.valueof(position);  
          while(start&&(len = fileoutstream.read(buffer)) != -1){  
            outstream.write(buffer, 0, len);  
            length += len;  
            message msg = new message();  
            msg.getdata().putint("size", length);  
            handler.sendmessage(msg);  
          }  
          fileoutstream.close();  
          outstream.close();  
          instream.close();  
          socket.close();  
          if(length==uploadfile.length()) logservice.delete(uploadfile);  
        } catch (exception e) {  
          e.printstacktrace();  
        }  
      }  
    }).start();  
  }  
}  

streamtool.java  

package com.android.socket.utils; 
 
import java.io.bytearrayoutputstream; 
import java.io.file; 
import java.io.fileoutputstream; 
import java.io.ioexception; 
import java.io.inputstream; 
import java.io.pushbackinputstream; 
 
public class streamtool { 
    
   public static void save(file file, byte[] data) throws exception { 
     fileoutputstream outstream = new fileoutputstream(file); 
     outstream.write(data); 
     outstream.close(); 
   } 
    
   public static string readline(pushbackinputstream in) throws ioexception { 
      char buf[] = new char[128]; 
      int room = buf.length; 
      int offset = 0; 
      int c; 
loop:    while (true) { 
        switch (c = in.read()) { 
          case -1: 
          case '\n': 
            break loop; 
          case '\r': 
            int c2 = in.read(); 
            if ((c2 != '\n') && (c2 != -1)) in.unread(c2); 
            break loop; 
          default: 
            if (--room < 0) { 
              char[] linebuffer = buf; 
              buf = new char[offset + 128]; 
              room = buf.length - offset - 1; 
              system.arraycopy(linebuffer, 0, buf, 0, offset); 
               
            } 
            buf[offset++] = (char) c; 
            break; 
        } 
      } 
      if ((c == -1) && (offset == 0)) return null; 
      return string.copyvalueof(buf, 0, offset); 
  } 
    
  /** 
  * 读取流 
  * @param instream 
  * @return 字节数组 
  * @throws exception 
  */ 
  public static byte[] readstream(inputstream instream) throws exception{ 
      bytearrayoutputstream outsteam = new bytearrayoutputstream(); 
      byte[] buffer = new byte[1024]; 
      int len = -1; 
      while( (len=instream.read(buffer)) != -1){ 
        outsteam.write(buffer, 0, len); 
      } 
      outsteam.close(); 
      instream.close(); 
      return outsteam.tobytearray(); 
  } 
} 

uploadlogservice.java  

package com.android.service; 
 
import java.io.file; 
 
import android.content.context; 
import android.database.cursor; 
import android.database.sqlite.sqlitedatabase; 
 
public class uploadlogservice { 
  private dbopenhelper dbopenhelper; 
   
  public uploadlogservice(context context){ 
    this.dbopenhelper = new dbopenhelper(context); 
  } 
   
  public void save(string sourceid, file uploadfile){ 
    sqlitedatabase db = dbopenhelper.getwritabledatabase(); 
    db.execsql("insert into uploadlog(uploadfilepath, sourceid) values(?,?)", 
        new object[]{uploadfile.getabsolutepath(),sourceid}); 
  } 
   
  public void delete(file uploadfile){ 
    sqlitedatabase db = dbopenhelper.getwritabledatabase(); 
    db.execsql("delete from uploadlog where uploadfilepath=?", new object[]{uploadfile.getabsolutepath()}); 
  } 
   
  public string getbindid(file uploadfile){ 
    sqlitedatabase db = dbopenhelper.getreadabledatabase(); 
    cursor cursor = db.rawquery("select sourceid from uploadlog where uploadfilepath=?",  
        new string[]{uploadfile.getabsolutepath()}); 
    if(cursor.movetofirst()){ 
      return cursor.getstring(0); 
    } 
    return null; 
  } 
} 

dbopenhelper.java  

package com.android.service; 
 
import android.content.context; 
import android.database.sqlite.sqlitedatabase; 
import android.database.sqlite.sqliteopenhelper; 
 
public class dbopenhelper extends sqliteopenhelper { 
 
  public dbopenhelper(context context) { 
    super(context, "upload.db", null, 1); 
  } 
 
  @override 
  public void oncreate(sqlitedatabase db) { 
    db.execsql("create table uploadlog (_id integer primary key autoincrement, uploadfilepath varchar(100), sourceid varchar(10))"); 
  } 
 
  @override 
  public void onupgrade(sqlitedatabase db, int oldversion, int newversion) { 
    db.execsql("drop table if exists uploadlog"); 
    oncreate(db);     
  } 
 
} 

main.xml  

<?xml version="1.0" encoding="utf-8"?> 
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android" 
  android:orientation="vertical" 
  android:layout_width="fill_parent" 
  android:layout_height="fill_parent" 
  > 
<textview  
  android:layout_width="fill_parent"  
  android:layout_height="wrap_content"  
  android:text="@string/filename" 
  /> 
   
  <edittext  
    android:layout_width="fill_parent"  
    android:layout_height="wrap_content"  
    android:text="022.jpg" 
    android:id="@+id/filename" 
    /> 
     
  <button  
    android:layout_width="wrap_content"  
    android:layout_height="wrap_content"  
    android:text="@string/button" 
    android:id="@+id/button" 
    /> 
  <button  
    android:layout_width="wrap_content"  
    android:layout_height="wrap_content"  
    android:text="暂停" 
    android:id="@+id/stop" 
    /> 
  <progressbar  
      android:layout_width="fill_parent"  
      android:layout_height="20px" 
      style="?android:attr/progressbarstylehorizontal" 
      android:id="@+id/uploadbar" 
      />  
  <textview  
    android:layout_width="fill_parent"  
    android:layout_height="wrap_content"  
    android:gravity="center" 
    android:id="@+id/result" 
    />   
</linearlayout> 

androidmanifest.xml  

<?xml version="1.0" encoding="utf-8"?> 
<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
  package="com.android.upload" 
  android:versioncode="1" 
  android:versionname="1.0" > 
 
  <uses-sdk android:minsdkversion="8" /> 
 
  <application 
    android:icon="@drawable/ic_launcher" 
    android:label="@string/app_name" > 
    <activity 
      android:name=".uploadactivity" 
      android:label="@string/app_name" > 
      <intent-filter> 
        <action android:name="android.intent.action.main" /> 
 
        <category android:name="android.intent.category.launcher" /> 
      </intent-filter> 
    </activity> 
  </application> 
  <!-- 访问网络的权限 --> 
  <uses-permission android:name="android.permission.internet"/> 
  <!-- 在sdcard中创建与删除文件权限 --> 
  <uses-permission android:name="android.permission.mount_unmount_filesystems"/> 
  <!-- 往sdcard写入数据权限 --> 
  <uses-permission android:name="android.permission.write_external_storage"/> 
</manifest> 

java服务端:

socketserver.javapackage com.android.socket.server; 
 
import java.io.file; 
import java.io.fileinputstream; 
import java.io.fileoutputstream; 
import java.io.ioexception; 
import java.io.outputstream; 
import java.io.pushbackinputstream; 
import java.io.randomaccessfile; 
import java.net.serversocket; 
import java.net.socket; 
import java.text.simpledateformat; 
import java.util.date; 
import java.util.hashmap; 
import java.util.map; 
import java.util.properties; 
import java.util.concurrent.executorservice; 
import java.util.concurrent.executors; 
 
import com.android.socket.utils.streamtool; 
 
public class socketserver { 
  private string uploadpath="d:/uploadfile/"; 
  private executorservice executorservice;// 线程池 
  private serversocket ss = null; 
  private int port;// 监听端口 
  private boolean quit;// 是否退出 
  private map<long, filelog> datas = new hashmap<long, filelog>();// 存放断点数据,最好改为数据库存放 
 
  public socketserver(int port) { 
    this.port = port; 
    // 初始化线程池 
    executorservice = executors.newfixedthreadpool(runtime.getruntime() 
        .availableprocessors() * 50); 
  } 
 
  // 启动服务 
  public void start() throws exception { 
    ss = new serversocket(port); 
    while (!quit) { 
      socket socket = ss.accept();// 接受客户端的请求 
      // 为支持多用户并发访问,采用线程池管理每一个用户的连接请求 
      executorservice.execute(new sockettask(socket));// 启动一个线程来处理请求 
    } 
  } 
 
  // 退出 
  public void quit() { 
    this.quit = true; 
    try { 
      ss.close(); 
    } catch (ioexception e) { 
      e.printstacktrace(); 
    } 
  } 
 
  public static void main(string[] args) throws exception { 
    socketserver server = new socketserver(7878); 
    server.start(); 
  } 
 
  private class sockettask implements runnable { 
    private socket socket; 
 
    public sockettask(socket socket) { 
      this.socket = socket; 
    } 
 
    @override 
    public void run() { 
      try { 
        system.out.println("accepted connenction from " 
            + socket.getinetaddress() + " @ " + socket.getport()); 
        pushbackinputstream instream = new pushbackinputstream( 
            socket.getinputstream()); 
        // 得到客户端发来的第一行协议数据:content-length=143253434;filename=xxx.3gp;sourceid= 
        // 如果用户初次上传文件,sourceid的值为空。 
        string head = streamtool.readline(instream); 
        system.out.println(head); 
        if (head != null) { 
          // 下面从协议数据中读取各种参数值 
          string[] items = head.split(";"); 
          string filelength = items[0].substring(items[0].indexof("=") + 1); 
          string filename = items[1].substring(items[1].indexof("=") + 1); 
          string sourceid = items[2].substring(items[2].indexof("=") + 1); 
          long id = system.currenttimemillis(); 
          filelog log = null; 
          if (null != sourceid && !"".equals(sourceid)) { 
            id = long.valueof(sourceid); 
            log = find(id);//查找上传的文件是否存在上传记录 
          } 
          file file = null; 
          int position = 0; 
          if(log==null){//如果上传的文件不存在上传记录,为文件添加跟踪记录 
            string path = new simpledateformat("yyyy/mm/dd/hh/mm").format(new date()); 
            file dir = new file(uploadpath+ path); 
            if(!dir.exists()) dir.mkdirs(); 
            file = new file(dir, filename); 
            if(file.exists()){//如果上传的文件发生重名,然后进行改名 
              filename = filename.substring(0, filename.indexof(".")-1)+ dir.listfiles().length+ filename.substring(filename.indexof(".")); 
              file = new file(dir, filename); 
            } 
            save(id, file); 
          }else{// 如果上传的文件存在上传记录,读取上次的断点位置 
            file = new file(log.getpath());//从上传记录中得到文件的路径 
            if(file.exists()){ 
              file logfile = new file(file.getparentfile(), file.getname()+".log"); 
              if(logfile.exists()){ 
                properties properties = new properties(); 
                properties.load(new fileinputstream(logfile)); 
                position = integer.valueof(properties.getproperty("length"));//读取断点位置 
              } 
            } 
          } 
           
          outputstream outstream = socket.getoutputstream(); 
          string response = "sourceid="+ id+ ";position="+ position+ "\r\n"; 
          //服务器收到客户端的请求信息后,给客户端返回响应信息:sourceid=1274773833264;position=0 
          //sourceid由服务生成,唯一标识上传的文件,position指示客户端从文件的什么位置开始上传 
          outstream.write(response.getbytes()); 
           
          randomaccessfile fileoutstream = new randomaccessfile(file, "rwd"); 
          if(position==0) fileoutstream.setlength(integer.valueof(filelength));//设置文件长度 
          fileoutstream.seek(position);//移动文件指定的位置开始写入数据 
          byte[] buffer = new byte[1024]; 
          int len = -1; 
          int length = position; 
          while( (len=instream.read(buffer)) != -1){//从输入流中读取数据写入到文件中 
            fileoutstream.write(buffer, 0, len); 
            length += len; 
            properties properties = new properties(); 
            properties.put("length", string.valueof(length)); 
            fileoutputstream logfile = new fileoutputstream(new file(file.getparentfile(), file.getname()+".log")); 
            properties.store(logfile, null);//实时记录文件的最后保存位置 
            logfile.close(); 
          } 
          if(length==fileoutstream.length()) delete(id); 
          fileoutstream.close();          
          instream.close(); 
          outstream.close(); 
          file = null; 
        } 
      } catch (exception e) { 
        e.printstacktrace(); 
      } finally { 
        try { 
          if(socket != null && !socket.isclosed()) socket.close(); 
        } catch (ioexception e) {} 
      } 
    } 
 
  } 
 
  public filelog find(long sourceid) { 
    return datas.get(sourceid); 
  } 
 
  // 保存上传记录 
  public void save(long id, file savefile) { 
    // 日后可以改成通过数据库存放 
    datas.put(id, new filelog(id, savefile.getabsolutepath())); 
  } 
 
  // 当文件上传完毕,删除记录 
  public void delete(long sourceid) { 
    if (datas.containskey(sourceid)) 
      datas.remove(sourceid); 
  } 
 
  private class filelog { 
    private long id; 
    private string path; 
     
    public filelog(long id, string path) { 
      super(); 
      this.id = id; 
      this.path = path; 
    } 
 
    public long getid() { 
      return id; 
    } 
 
    public void setid(long id) { 
      this.id = id; 
    } 
 
    public string getpath() { 
      return path; 
    } 
 
    public void setpath(string path) { 
      this.path = path; 
    } 
 
  } 
} 
serverwindow.javapackage com.android.socket.server; 
 
import java.awt.borderlayout; 
import java.awt.frame; 
import java.awt.label; 
import java.awt.event.windowevent; 
import java.awt.event.windowlistener; 
 
public class serverwindow extends frame{ 
  private socketserver server; 
  private label label; 
   
  public serverwindow(string title){ 
    super(title); 
    server = new socketserver(7878); 
    label = new label(); 
    add(label, borderlayout.page_start); 
    label.settext("服务器已经启动"); 
    this.addwindowlistener(new windowlistener() { 
      @override 
      public void windowopened(windowevent e) { 
        new thread(new runnable() {      
          @override 
          public void run() { 
            try { 
              server.start(); 
            } catch (exception e) { 
              e.printstacktrace(); 
            } 
          } 
        }).start(); 
      } 
       
      @override 
      public void windowiconified(windowevent e) { 
      } 
       
      @override 
      public void windowdeiconified(windowevent e) { 
      } 
       
      @override 
      public void windowdeactivated(windowevent e) { 
      } 
       
      @override 
      public void windowclosing(windowevent e) { 
         server.quit(); 
         system.exit(0); 
      } 
       
      @override 
      public void windowclosed(windowevent e) { 
      } 
       
      @override 
      public void windowactivated(windowevent e) { 
      } 
    }); 
  } 
  /** 
   * @param args 
   */ 
  public static void main(string[] args) { 
    serverwindow window = new serverwindow("文件上传服务端");  
    window.setsize(300, 300);  
    window.setvisible(true); 
  } 
 
} 
streamtool.javapackage com.android.socket.utils; 
 
import java.io.bytearrayoutputstream; 
import java.io.file; 
import java.io.fileoutputstream; 
import java.io.ioexception; 
import java.io.inputstream; 
import java.io.pushbackinputstream; 
 
public class streamtool { 
    
   public static void save(file file, byte[] data) throws exception { 
     fileoutputstream outstream = new fileoutputstream(file); 
     outstream.write(data); 
     outstream.close(); 
   } 
    
   public static string readline(pushbackinputstream in) throws ioexception { 
      char buf[] = new char[128]; 
      int room = buf.length; 
      int offset = 0; 
      int c; 
loop:    while (true) { 
        switch (c = in.read()) { 
          case -1: 
          case '\n': 
            break loop; 
          case '\r': 
            int c2 = in.read(); 
            if ((c2 != '\n') && (c2 != -1)) in.unread(c2); 
            break loop; 
          default: 
            if (--room < 0) { 
              char[] linebuffer = buf; 
              buf = new char[offset + 128]; 
              room = buf.length - offset - 1; 
              system.arraycopy(linebuffer, 0, buf, 0, offset); 
               
            } 
            buf[offset++] = (char) c; 
            break; 
        } 
      } 
      if ((c == -1) && (offset == 0)) return null; 
      return string.copyvalueof(buf, 0, offset); 
  } 
    
  /** 
  * 读取流 
  * @param instream 
  * @return 字节数组 
  * @throws exception 
  */ 
  public static byte[] readstream(inputstream instream) throws exception{ 
      bytearrayoutputstream outsteam = new bytearrayoutputstream(); 
      byte[] buffer = new byte[1024]; 
      int len = -1; 
      while( (len=instream.read(buffer)) != -1){ 
        outsteam.write(buffer, 0, len); 
      } 
      outsteam.close(); 
      instream.close(); 
      return outsteam.tobytearray(); 
  } 
 
} 

运行效果如下:

android前端控制:

后台监控日志:

下载后的文件路径:

 

源码下载地址:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网