zoukankan      html  css  js  c++  java
  • Android如何实现茄子快传

    Android如何实现茄子快传
    茄子快传是一款文件传输应用,相信大家都很熟悉这款应用,应该很多人用过用来文件的传输。它有两个核心的功能:

    端到端的文件传输
    Web端的文件传输
    这两个核心的功能我们具体来分析一下!

    端到端的文件传输
    所谓的端到端的文件传输是指应用端发送到应用端(这里的应用端指Android应用端),这种文件传输方式是文件发送端和文件接收端必须安装应用。

    效果图
    文件发送方


    文件接收方

    简单的文件传输的话,我们可以用蓝牙,wifi直连,ftp这几种方式来进行文件的传输。但是:

    蓝牙传输的话,速度太慢,而且要配对。相对比较麻烦。
    wifi直连差不多跟蓝牙一样,但是速率很快,也要配对。
    ftp可以实现文件的批量传输,但是没有文件的缩略图。
    最初分析这个项目的时候就想着通过自定义协议的Socket的通信来实现,自定义的协议包括header + body的自定义协议, header部分包括了文件的信息(长度,大小,文件路径,缩略图), body部分就是文件。现在实现这一功能。(后序:后面开发《网页传》功能的时候,可以考虑这两个核心的功能都能用在Android架设微型Http服务器来实现。这是后话了。)

    流程图


    编码实现
    两部设备文件传输是需要在一个局域网的条件下的,只有文件发送方连接上文件接收方的热点(搭建了一个局域网),这样文件发送方和文件接收方就在一个局域网里面,我们才可以进行Socket通信。这是一个大前提!

    初始化条件 – Ap(热点)和Wifi的管理, 文件的扫描
    对Android的Ap(热点)和Wifi的一些操作都封装在下面两个类:

    WifiMgr.java

    APMgr.java

    关于热点和Wifi的操作都是根据WifiManager来操作的。所以要像操作WifiManeger是必须要一些权限的。必须在AndroidManifest.xml清单文件里面声明权限:


    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    1
    2
    3
    4
    5
    文件接收端打开热点并且配置热点的代码:


    //1.初始化热点
    WifiMgr.getInstance(getContext()).disableWifi();
    if(ApMgr.isApOn(getContext())){
    ApMgr.disableAp(getContext());
    }

    //热点相关的广播
    mWifiAPBroadcastReceiver = new WifiAPBroadcastReceiver() {
    @Override
    public void onWifiApEnabled() {
    Log.i(TAG, "======>>>onWifiApEnabled !!!");
    if(!mIsInitialized){
    mUdpServerRuannable = createSendMsgToFileSenderRunnable();
    AppContext.MAIN_EXECUTOR.execute(mUdpServerRuannable);
    mIsInitialized = true;

    tv_desc.setText(getResources().getString(R.string.tip_now_init_is_finish));
    tv_desc.postDelayed(new Runnable() {
    @Override
    public void run() {
    tv_desc.setText(getResources().getString(R.string.tip_is_waitting_connect));
    }
    }, 2*1000);
    }
    }
    };
    IntentFilter filter = new IntentFilter(WifiAPBroadcastReceiver.ACTION_WIFI_AP_STATE_CHANGED);
    registerReceiver(mWifiAPBroadcastReceiver, filter);

    ApMgr.isApOn(getContext()); // check Ap state :boolean
    String ssid = TextUtils.isNullOrBlank(android.os.Build.DEVICE) ? Constant.DEFAULT_SSID : android.os.Build.DEVICE;
    ApMgr.configApState(getContext(), ssid); // change Ap state :boolean

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    对于类WifiAPBroadcastReceiver是热点的一个广播类,最后一行代码是配置指定名称的热点,这里是以设备名称作为热点的名称。

    文件发送端发送文件,文件发送端首先要选择要发送的文件,然后将要选择的文件存储起来,这里我是用了一个HashMap将发送的文件存储起来,key是文件的路径,value是FileInfo对象。

    以下是扫描手机存储盘上面的文件列表的代码:


    /**
    * 存储卡获取 指定后缀名文件
    * @param context
    * @param extension
    * @return
    */
    public static List<FileInfo> getSpecificTypeFiles(Context context, String[] extension){
    List<FileInfo> fileInfoList = new ArrayList<FileInfo>();

    //内存卡文件的Uri
    Uri fileUri= MediaStore.Files.getContentUri("external");
    //筛选列,这里只筛选了:文件路径和含后缀的文件名
    String[] projection=new String[]{
    MediaStore.Files.FileColumns.DATA, MediaStore.Files.FileColumns.TITLE
    };

    //构造筛选条件语句
    String selection="";
    for(int i=0;i<extension.length;i++)
    {
    if(i!=0)
    {
    selection=selection+" OR ";
    }
    selection=selection+ MediaStore.Files.FileColumns.DATA+" LIKE '%"+extension[i]+"'";
    }
    //按时间降序条件
    String sortOrder = MediaStore.Files.FileColumns.DATE_MODIFIED;

    Cursor cursor = context.getContentResolver().query(fileUri, projection, selection, null, sortOrder);
    if(cursor != null){
    while (cursor.moveToNext()){
    try{
    String data = cursor.getString(0);
    FileInfo fileInfo = new FileInfo();
    fileInfo.setFilePath(data);

    long size = 0;
    try{
    File file = new File(data);
    size = file.length();
    fileInfo.setSize(size);
    }catch(Exception e){

    }
    fileInfoList.add(fileInfo);
    }catch (Exception e){
    Log.i("FileUtils", "------>>>" + e.getMessage());
    }

    }
    }
    Log.i(TAG, "getSize ===>>> " + fileInfoList.size());
    return fileInfoList;
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    注意**:这里扫描的FileInfo对象只是扫描了文件路径filePath, 还有文件的大小size。
    FileInfo的其他属性到文件传输的时候再二次获取,获取FileInfo的其他属性都在FileUtils这个工具类里面了。

    文件发送端打开wifi扫描热点并且连接热点的代码:


    if(!WifiMgr.getInstance(getContext()).isWifiEnable()) {//wifi未打开的情况,打开wifi
    WifiMgr.getInstance(getContext()).openWifi();
    }

    //开始扫描
    WifiMgr.getInstance(getContext()).startScan();
    mScanResultList = WifiMgr.getInstance(getContext()).getScanResultList();
    mScanResultList = ListUtils.filterWithNoPassword(mScanResultList);

    if(mScanResultList != null){
    mWifiScanResultAdapter = new WifiScanResultAdapter(getContext(),mScanResultList);
    lv_result.setAdapter(mWifiScanResultAdapter);
    lv_result.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    //单击选中指定的网络
    ScanResult scanResult = mScanResultList.get(position);
    Log.i(TAG, "###select the wifi info ======>>>" + scanResult.toString());

    //1.连接网络
    String ssid = Constant.DEFAULT_SSID;
    ssid = scanResult.SSID;
    WifiMgr.getInstance(getContext()).openWifi();
    WifiMgr.getInstance(getContext()).addNetwork(WifiMgr.createWifiCfg(ssid, null, WifiMgr.WIFICIPHER_NOPASS));

    //2.发送UDP通知信息到 文件接收方 开启ServerSocketRunnable
    mUdpServerRuannable = createSendMsgToServerRunnable(WifiMgr.getInstance(getContext()).getIpAddressFromHotspot());
    AppContext.MAIN_EXECUTOR.execute(mUdpServerRuannable);
    }
    });
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    对于ListUtils.filterWithNoPassword是将扫描的结果进行过滤,过滤掉有密码的扫描结果。

    lv_result.setOnItemClickListener回调的方法是连接指定的热点来形成一个局域网。文件传输的大前提条件就已经形成了。

    到这里文件发送端和文件接收端的初始化环境也就搭建起来了。

    文件传输模块
    文件传输模块的核心代码就只有4个类,Transferable, BaseTransfer, FileSender, FileReceiver。

    Transferable是接口。

    BaseTransfer, FileSender, FileReceiver是类。

    对于文件发送端,每一个文件发送对应一个FileSender,而对于文件接收端,每一个文件的接收对应一个FileReceiver。
    而FileSender,FileReceiver是继承自 抽象类BaseTransfer的。 BaseTransfer是实现了Transferable接口。

    下面是4个类图的关系:

    在Transferable接口中定义了4个方法,分别是初始化,解析头部,解析主体,结束。解析头部和解析主体分别对应上面说的自定义协议的header和body。初始化是为每一次文件传输做初始化工作,而结束是为每一次文件传输做结束工作,比如关闭一些资源流,Socket等等。

    而BaseTransfer就只是实现了Transferable, 里面封装了一些常量。没有实现具体的方法,具体的实现是FileSender,FileReceiver。

    代码详情:

    Transferable
    BaseTransfer
    FileSender
    FileReceiver

    总结
    端到端的文件传输就分析到这里,主要是Ap热点的操作,Wifi的操作,Socket通信来实现文件的传输。但是这里的Socket用到的不是异步IO,是同步IO。所以会引起阻塞。比如在FileSender中的暂停文件传输pause方法调用之后,会引起FileReceiver中文件传输的阻塞。如果你对异步IO有兴趣,你也可以去实现一下。

    对于端对端的核心代码都是在 io.github.mayubao.kuaichuan.core 包下面。
    这是我在github上面的项目链接 https://github.com/mayubao/KuaiChuan

    web端的文件传输
    所谓的Web端的文件传输是指文件发送端作为一个Http服务器,提供文件接收端来下载。这种文件传输方式是文件发送端必须安装应用,而文件接收端只需要有浏览器即可。

    效果图
    文件发送端


    文件接收端


    在android应用端架设微型Http服务器来实现文件的传输。这里可以用ftp来实现,为什么不用ftp呢?因为没有缩略图,这是重点!

    web端的文件传输的核心重点:

    文件发送端热点的开启(参考端对端的热点操作类 APMgr.java)
    文件发送端架设Http服务器。
    Android端的Http服务器
    Android上微型Http服务器(Socket实现),结合上面的效果图分析。主要解决三种Http url的请求形式就行了,由上面的文件接收端的效果图可以看出来(文件接收端是去访问文件发送端的Http服务器),大致可以分为三种链接:

    Index主页链接 http://hostname:port
    Image链接 http://hostname:port/image/xxx.xxx
    Download链接 http://hostname:port/download/xxx.xxx


    下面用Socket来实现在Android上面的微型Http服务器的。

    关于Http协议,我简单的描述一下Http协议。对于Http协议,就是”请求-回复(响应)“的这种通信模式。客户端发出请求,服务器根据请求,返回一个回复(响应)给客户端。

    Http请求的大致分为四个部分:
    1. 请求行
    2. 请求头
    3. 空行
    4. 请求实体

    Http响应的大致分为四个部分:
    1. 状态行
    2. 响应头
    3. 空行
    4. 响应实体

    Http请求(POST请求)的示例:


    POST /image/index.html HTTP/1.1
    Host: 127.0.0.1:7878
    Connection: keep-alive
    Content-Length: 247
    Cache-Control: no-cache
    Origin: chrome-extension://fdmmgilgnpjigdojojpjoooidkmcomcm
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36
    Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryLIr5t1rdtuD8Ztuw
    Accept: */*
    Accept-Encoding: gzip, deflate
    Accept-Language: zh-CN,zh;q=0.8,en;q=0.6

    ------WebKitFormBoundaryLIr5t1rdtuD8Ztuw
    Content-Disposition: form-data; name="username"

    mayubao
    ------WebKitFormBoundaryLIr5t1rdtuD8Ztuw
    Content-Disposition: form-data; name="username"

    123456
    ------WebKitFormBoundaryLIr5t1rdtuD8Ztuw--
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    1.请求行(请求方式 + uri + http版本)


    POST /image/index.html HTTP/1.1
    1
    2
    2.请求头


    Host: 127.0.0.1:7878
    Connection: keep-alive
    Content-Length: 247
    Cache-Control: no-cache
    Origin: chrome-extension://fdmmgilgnpjigdojojpjoooidkmcomcm
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36
    Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryLIr5t1rdtuD8Ztuw
    Accept: */*
    Accept-Encoding: gzip, deflate
    Accept-Language: zh-CN,zh;q=0.8,en;q=0.6
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    3.空行

    4.请求实体(对于GET请求一般没有请求实体)


    ------WebKitFormBoundaryLIr5t1rdtuD8Ztuw
    Content-Disposition: form-data; name="username"

    mayubao
    ------WebKitFormBoundaryLIr5t1rdtuD8Ztuw
    Content-Disposition: form-data; name="username"

    123456
    ------WebKitFormBoundaryLIr5t1rdtuD8Ztuw--
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    Http响应的示例:


    HTTP/1.0 200 OK
    Cache-Control:public, max-age=86400
    Content-Length:235
    Content-Type:image/png
    Date:Wed, 21 Dec 2016 08:20:54 GMT

    请求实体
    1
    2
    3
    4
    5
    6
    7
    8
    1.状态行(Http版本 + 状态 + 描述)


    HTTP/1.0 200 OK
    1
    2
    2.响应头


    HTTP/1.0 200 OK
    Cache-Control:public, max-age=86400
    Content-Length:235
    Content-Type:image/png
    Date:Wed, 21 Dec 2016 08:20:54 GMT
    1
    2
    3
    4
    5
    6
    3.空行

    4.响应实体

    上面只是简单的叙述了一下Http一般的请求-响应流程,还有对应请求,响应的结构。如果你想进一步了解http协议,请私下自行了解。

    回到我们的重点 AndroidMicroServer:
    AndroidMicroServer是Http服务器的核心类,还有关联到其他的类,有IndexUriResHandler,ImageUriResHandler, DowloadUriResHandler。是AndroidMicroServer根据不同的Uri格式分配给指定的Handler去处理的。

    UML的分析图如下:

    下面是AndroidMicroServer的源码:


    /**
    * The micro server in Android
    * Created by mayubao on 2016/12/14.
    * Contact me 345269374@qq.com
    */
    public class AndroidMicroServer {

    private static final String TAG = AndroidMicroServer.class.getSimpleName();

    /**
    * the server port
    */
    private int mPort;

    /**
    * the server socket
    */
    private ServerSocket mServerSocket;

    /**
    * the thread pool which handle the incoming request
    */
    private ExecutorService mThreadPool = Executors.newCachedThreadPool();

    /**
    * uri router handler
    */
    private List<ResUriHandler> mResUriHandlerList = new ArrayList<ResUriHandler>();


    /**
    * the flag which the micro server enable
    */
    private boolean mIsEnable = true;


    public AndroidMicroServer(int port){
    this.mPort = port;
    }

    /**
    * register the resource uri handler
    * @param resUriHandler
    */
    public void resgisterResUriHandler(ResUriHandler resUriHandler){
    this.mResUriHandlerList.add(resUriHandler);
    }


    /**
    * unresigter all the resource uri hanlders
    */
    public void unresgisterResUriHandlerList(){
    for(ResUriHandler resUriHandler : mResUriHandlerList){
    resUriHandler.destroy();
    resUriHandler = null;
    }
    }

    /**
    * start the android micro server
    */
    public void start(){
    mThreadPool.submit(new Runnable() {
    @Override
    public void run() {
    try {
    mServerSocket = new ServerSocket(mPort);

    while(mIsEnable){
    Socket socket = mServerSocket.accept();
    hanlderSocketAsyn(socket);
    }
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    });
    }

    /**
    * stop the android micro server
    */
    public void stop(){
    if(mIsEnable){
    mIsEnable = false;
    }

    //release resource
    unresgisterResUriHandlerList();

    if(mServerSocket != null){
    try {
    // mServerSocket.accept(); //fuck ! fix the problem, block the main thread
    mServerSocket.close();
    mServerSocket = null;
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }


    /**
    * handle the incoming socket
    * @param socket
    */
    private void hanlderSocketAsyn(final Socket socket) {
    mThreadPool.submit(new Runnable() {
    @Override
    public void run() {
    //1. auto create request object by the parameter socket
    Request request = createRequest(socket);

    //2. loop the mResUriHandlerList, and assign the task to the specify ResUriHandler
    for(ResUriHandler resUriHandler : mResUriHandlerList){
    if(!resUriHandler.matches(request.getUri())){
    continue;
    }

    resUriHandler.handler(request);
    }
    }
    });


    }

    /**
    * create the requset object by the specify socket
    *
    * @param socket
    * @return
    */
    private Request createRequest(Socket socket) {
    Request request = new Request();
    request.setUnderlySocket(socket);
    try {
    //Get the reqeust line
    SocketAddress socketAddress = socket.getRemoteSocketAddress();
    InputStream is = socket.getInputStream();
    String requestLine = IOStreamUtils.readLine(is);
    SLog.i(TAG, socketAddress + "requestLine------>>>" + requestLine);
    String requestType = requestLine.split(" ")[0];
    String requestUri = requestLine.split(" ")[1];


    // requestUri = URLDecoder.decode(requestUri, "UTF-8");

    request.setUri(requestUri);


    //Get the header line
    String header = "";
    while((header = IOStreamUtils.readLine(is)) != null){
    SLog.i(TAG, socketAddress + "header------>>>" + requestLine);
    String headerKey = header.split(":")[0];
    String headerVal = header.split(":")[1];
    request.addHeader(headerKey, headerVal);
    }
    } catch (IOException e) {
    e.printStackTrace();
    }

    return request;
    }

    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    AndroidMicroServer主要有两个方法:
    1. start (Http服务器的开启)
    2. stop (Http服务器的关闭,主要用来关闭ServerSocket和反注册UriResHandler)

    start方法 是Http服务器的入口

    对于start方法:


    /**
    * start the android micro server
    */
    public void start(){
    mThreadPool.submit(new Runnable() {
    @Override
    public void run() {
    try {
    mServerSocket = new ServerSocket(mPort);

    while(mIsEnable){
    Socket socket = mServerSocket.accept();
    hanlderSocketAsyn(socket);
    }
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    });
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    开启一个线程去执行ServerSocket, while循环去接收每一个进来的Socket。 而hanlderSocketAsyn(socket)是异步处理每一个进来的socket。


    /**
    * handle the incoming socket
    * @param socket
    */
    private void hanlderSocketAsyn(final Socket socket) {
    mThreadPool.submit(new Runnable() {
    @Override
    public void run() {
    //1. auto create request object by the parameter socket
    Request request = createRequest(socket);

    //2. loop the mResUriHandlerList, and assign the task to the specify ResUriHandler
    for(ResUriHandler resUriHandler : mResUriHandlerList){
    if(!resUriHandler.matches(request.getUri())){
    continue;
    }

    resUriHandler.handler(request);
    }
    }
    });
    }


    /**
    * create the requset object by the specify socket
    *
    * @param socket
    * @return
    */
    private Request createRequest(Socket socket) {
    Request request = new Request();
    request.setUnderlySocket(socket);
    try {
    //Get the reqeust line
    SocketAddress socketAddress = socket.getRemoteSocketAddress();
    InputStream is = socket.getInputStream();
    String requestLine = IOStreamUtils.readLine(is);
    SLog.i(TAG, socketAddress + "requestLine------>>>" + requestLine);
    String requestType = requestLine.split(" ")[0];
    String requestUri = requestLine.split(" ")[1];

    // //解决URL中文乱码的问题
    // requestUri = URLDecoder.decode(requestUri, "UTF-8");

    request.setUri(requestUri);


    //Get the header line
    String header = "";
    while((header = IOStreamUtils.readLine(is)) != null){
    SLog.i(TAG, socketAddress + "header------>>>" + requestLine);
    String headerKey = header.split(":")[0];
    String headerVal = header.split(":")[1];
    request.addHeader(headerKey, headerVal);
    }
    } catch (IOException e) {
    e.printStackTrace();
    }

    return request;
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    对于每一个进来的Socket:

    通过createRequest(socket)来创建一个Request对象,对应一个Http Request对象。在createRequest(socket)中如何去从socket中去读取每一行呢?对于每一个Http请求的每一行都是以’ ’字节结尾的。只要判断读取字节流的时候判断连续的两个字节是以’ ’结尾的就是一行结尾的标识。详情请查看IOStreamUtils.java

    根据请求行的path,分配给对应的Uri处理对象去处理,而所对应uri如何获取,是从Socket的Inputsream读取Http Request的请求行中读取出来的。对于ResUriHandler,是一个接口。主要根据请求行的uri 分配给对应的ResUriHandler去处理。 ResUriHandler的实现类是对应给出响应的处理类。

    注意:可参考上面的UML的类图分析

    ResUriHandler有三个实现类分别对应上面分析的三种Uri格式:

    IndexResUriHandler 处理发送文件列表的显示
    ImageResUriHandler 处理文件图片
    DownloadResUriHandler 处理文件下载
    总结
    AndroidMicroServer是架设在Android平台上面的一个微型HttpServer, 是根据快传项目的具体需求来实现的。巧妙的利用ResUriHandler来处理不同的uri。注意这不是一般通用的HttpServer, 之前有想过在Github上面去找一些Server端的代码来进行开发,发现代码关联太多,而且不容易定制,所以才会萌生自己用ServerSocket来实现符合自己需求的HttpServer。

    对于HttpServer的核心代码都是在 io.github.mayubao.kuaichuan.micro_server包下面。
    这是我在github上面的项目链接 https://github.com/mayubao/KuaiChuan

  • 相关阅读:
    快速上手php:使用PhpStrom调试php
    快速上手php:使用PhpStrom部署项目
    使用自定义tld标签简化jsp的繁琐操作
    京东购物体验杂谈
    Mysql将近两个月的记录合并为一行显示
    学习WebSocket(二):使用Spring WebSocket做一个简单聊天室
    学习WebSocket(一):Spring WebSocket的简单使用
    springMVC的@ResponseBody、@RequestBody使用需要注意的地方
    如何使用maven建一个web3.0的项目
    项目管理工具 Redmine 安装试用手记
  • 原文地址:https://www.cnblogs.com/timssd/p/10659961.html
Copyright © 2011-2022 走看看