欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2023-12-16 17:56:52
什么是socket?      所谓socket通常也称作“套接字”,用于描述ip地址和端口,是一个通信连的...

什么是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通信模型如下:

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

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

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

tcp网络连接模型:

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

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

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前端控制:

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

后台监控日志:

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

下载后的文件路径:

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

源码下载地址:androidsocket_jb51.rar

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

上一篇:

下一篇: