Android实现WebServer服务端和客户端(附带源码)

06-01 1067阅读

一、项目介绍

在移动互联网时代,Android 端不仅需要作为客户端发起请求,还能胜任轻量级的服务端角色,提供简单的数据或文件分发服务。本项目目标是在 Android 设备(或模拟器)上同时运行一个 HTTP WebServer(服务端)和一个 HTTP Client(客户端),实现设备本地局域网内的文件浏览、上传下载及简单 API 接口调用。

  • 应用场景

    • 局域网内播放本地媒体文件

    • 简易分布式调试:多台设备互相请求数据

    • 边缘设备数据采集与展示

    • 核心功能

      1. 启动 WebServer,监听指定端口(如 8080)。

      2. 服务端响应 /files 列表当前设备指定目录下文件。

      3. 客户端发起 GET/POST 请求,支持文件下载与上传。

      4. WebServer 支持简单的 REST 风格接口。


      二、相关知识

      在动手编码之前,需要读者对以下知识有一定掌握:

      1. HTTP 协议基础

        • 请求行、请求头、请求体

        • 响应行、响应头、响应体

        • 常见状态码(200、404、500 等)

          Android实现WebServer服务端和客户端(附带源码)
          (图片来源网络,侵删)
        • Android 网络编程

          • HttpURLConnection 与 OkHttp 库

            Android实现WebServer服务端和客户端(附带源码)
            (图片来源网络,侵删)
          • 异步请求:AsyncTask(兼容老版本)、Thread + Handler、ExecutorService、RxJava

          • 多线程与并发

            Android实现WebServer服务端和客户端(附带源码)
            (图片来源网络,侵删)
            • Java Thread、Runnable

            • 线程池管理

            • 同步与线程安全

            • 文件读写权限

              • Android 6.0+ 动态权限申请 (READ_EXTERNAL_STORAGE、WRITE_EXTERNAL_STORAGE)

              • Storage Access Framework(可选)

              • 常用第三方库

                • NanoHTTPD:轻量级 Java HTTP 服务器

                • OkHttp:高效 HTTP 客户端


      三、实现思路

      1. 整体架构

        • MainActivity:启动与停止服务器,展示客户端请求结果界面。

        • WebServer:继承自 NanoHTTPD,重写 serve() 方法处理请求。

        • HttpClientHelper:封装客户端 HTTP 请求逻辑,支持同步/异步接口调用及文件上传下载。

        • 流程图

      [MainActivity] 
           │
        用户点击“启动服务器”
           │
           ▼
      [WebServer.start()] ──┬── 监听 8080 端口
                           │
                           └─ 进入请求处理循环
                                serve(session) ── 分发到对应 URI → 返回 Response
      
      1. 模块职责划分

        模块职责
        MainActivityUI 控制,启动/停止服务,发起客户端请求
        WebServer接收并响应 HTTP 请求
        HttpClientHelperHTTP 请求工具(GET/POST/下载/上传)
        PermissionManager动态权限申请管理

      四、实现代码

      下面将所有源文件与布局文件整合到同一个代码块中,用注释标注文件边界,并附带详细注释。

      // ---------------------- 文件: MainActivity.java ----------------------
      package com.example.webserverapp;
      import android.Manifest;
      import android.os.Bundle;
      import android.os.Environment;
      import android.view.View;
      import android.widget.Button;
      import android.widget.TextView;
      import androidx.annotation.NonNull;
      import androidx.appcompat.app.AppCompatActivity;
      import java.io.IOException;
      // MainActivity:负责启动/停止服务器,发起客户端请求,并展示结果
      public class MainActivity extends AppCompatActivity {
          private Button btnStartServer, btnStopServer, btnGetFiles;
          private TextView tvResult;
          private WebServer webServer;
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_main); // 加载布局文件 activity_main.xml
              // 初始化 UI 元素
              btnStartServer = findViewById(R.id.btn_start_server);
              btnStopServer  = findViewById(R.id.btn_stop_server);
              btnGetFiles    = findViewById(R.id.btn_get_files);
              tvResult       = findViewById(R.id.tv_result);
              // 点击“启动服务器”
              btnStartServer.setOnClickListener(v -> {
                  // 确保已申请存储权限
                  if (PermissionManager.hasStoragePermission(this)) {
                      startWebServer();
                  } else {
                      PermissionManager.requestStoragePermission(this);
                  }
              });
              // 点击“停止服务器”
              btnStopServer.setOnClickListener(v -> stopWebServer());
              // 点击“获取文件列表”
              btnGetFiles.setOnClickListener(v -> {
                  // 异步请求 /files 接口
                  HttpClientHelper.get("http://127.0.0.1:8080/files", new HttpClientHelper.Callback() {
                      @Override
                      public void onSuccess(String response) {
                          runOnUiThread(() -> tvResult.setText(response));
                      }
                      @Override
                      public void onFailure(Exception e) {
                          runOnUiThread(() -> tvResult.setText("请求失败: " + e.getMessage()));
                      }
                  });
              });
          }
          /** 启动 WebServer */
          private void startWebServer() {
              try {
                  // 将根目录设为设备外部存储 Download 文件夹
                  String rootPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();
                  webServer = new WebServer(8080, rootPath);
                  webServer.start();
                  tvResult.setText("服务器已启动,监听端口 8080");
              } catch (IOException e) {
                  tvResult.setText("服务器启动失败: " + e.getMessage());
              }
          }
          /** 停止 WebServer */
          private void stopWebServer() {
              if (webServer != null) {
                  webServer.stop();
                  tvResult.setText("服务器已停止");
              }
          }
          @Override
          public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, 
                                                 @NonNull int[] grantResults) {
              // 处理权限申请回调
              super.onRequestPermissionsResult(requestCode, permissions, grantResults);
              PermissionManager.handlePermissionResult(this, requestCode, permissions, grantResults);
          }
      }
      // ---------------------- 文件: WebServer.java ----------------------
      package com.example.webserverapp;
      import fi.iki.elonen.NanoHTTPD;
      import java.io.File;
      import java.io.FileInputStream;
      import java.util.Map;
      // WebServer:继承自 NanoHTTPD,重写 serve() 实现自定义路由与文件服务
      public class WebServer extends NanoHTTPD {
          private String rootDir;
          public WebServer(int port, String rootDir) {
              super(port);
              this.rootDir = rootDir;
          }
          @Override
          public Response serve(IHTTPSession session) {
              String uri = session.getUri();                  // 获取请求路径
              Method method = session.getMethod();            // 请求方法:GET/POST
              Map params = session.getParms(); // 请求参数
              // 路由分发
              if (uri.equals("/files") && Method.GET.equals(method)) {
                  return listFiles();
              } else if (uri.equals("/upload") && Method.POST.equals(method)) {
                  return handleUpload(session);
              } else {
                  return newFixedLengthResponse(Response.Status.NOT_FOUND, "text/plain", "404 Not Found");
              }
          }
          /** 列举 rootDir 下所有文件名,并以 JSON 格式返回 */
          private Response listFiles() {
              File dir = new File(rootDir);
              StringBuilder sb = new StringBuilder("[");
              File[] files = dir.listFiles();
              if (files != null) {
                  for (File f : files) {
                      sb.append("\"").append(f.getName()).append("\",");
                  }
                  if (sb.charAt(sb.length() - 1) == ',') sb.deleteCharAt(sb.length() - 1);
              }
              sb.append("]");
              return newFixedLengthResponse(Response.Status.OK, "application/json", sb.toString());
          }
          /** 处理文件上传(POST /upload) */
          private Response handleUpload(IHTTPSession session) {
              // TODO: 解析 multipart/form-data,保存文件到 rootDir
              return newFixedLengthResponse(Response.Status.OK, "text/plain", "上传成功");
          }
      }
      // ---------------------- 文件: HttpClientHelper.java ----------------------
      package com.example.webserverapp;
      import java.io.BufferedInputStream;
      import java.io.BufferedOutputStream;
      import java.io.FileOutputStream;
      import java.io.InputStream;
      import java.net.HttpURLConnection;
      import java.net.URL;
      // HttpClientHelper:封装 GET/POST 及文件下载上传
      public class HttpClientHelper {
          public interface Callback {
              void onSuccess(String response);
              void onFailure(Exception e);
          }
          /** 发起 GET 请求 */
          public static void get(String urlStr, Callback cb) {
              new Thread(() -> {
                  try {
                      URL url = new URL(urlStr);
                      HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                      conn.setRequestMethod("GET");
                      InputStream in = new BufferedInputStream(conn.getInputStream());
                      StringBuilder sb = new StringBuilder();
                      byte[] buf = new byte[1024];
                      int len;
                      while ((len = in.read(buf)) != -1) sb.append(new String(buf, 0, len));
                      in.close();
                      cb.onSuccess(sb.toString());
                  } catch (Exception e) {
                      cb.onFailure(e);
                  }
              }).start();
          }
          /** 简单文件下载示例 */
          public static void downloadFile(String urlStr, String destPath, Callback cb) {
              new Thread(() -> {
                  try {
                      URL url = new URL(urlStr);
                      HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                      try (InputStream in = conn.getInputStream();
                           FileOutputStream fos = new FileOutputStream(destPath)) {
                          byte[] buf = new byte[8192];
                          int len;
                          while ((len = in.read(buf)) != -1) fos.write(buf, 0, len);
                      }
                      cb.onSuccess("下载完成: " + destPath);
                  } catch (Exception e) {
                      cb.onFailure(e);
                  }
              }).start();
          }
          // TODO: 实现文件上传 uploadFile()
      }
      // ---------------------- 文件: PermissionManager.java ----------------------
      package com.example.webserverapp;
      import android.app.Activity;
      import android.content.pm.PackageManager;
      import androidx.core.app.ActivityCompat;
      import androidx.core.content.ContextCompat;
      // PermissionManager:动态权限申请工具类
      public class PermissionManager {
          private static final int REQ_STORAGE = 1001;
          public static boolean hasStoragePermission(Activity act) {
              return ContextCompat.checkSelfPermission(act, Manifest.permission.READ_EXTERNAL_STORAGE)
                      == PackageManager.PERMISSION_GRANTED;
          }
          public static void requestStoragePermission(Activity act) {
              ActivityCompat.requestPermissions(act,
                  new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE},
                  REQ_STORAGE);
          }
          public static void handlePermissionResult(Activity act, int requestCode,
      String[] permissions, int[] grantResults) {
              if (requestCode == REQ_STORAGE) {
                  // 简化:未授权则提示并退出
                  if (grantResults.length > 0 && grantResults[0] != PackageManager.PERMISSION_GRANTED) {
                      // 提示用户开启权限
                  }
              }
          }
      }
      // ---------------------- 文件: res/layout/activity_main.xml ----------------------
      
      
          
          
          
          
      
      

      五、代码解读

      • MainActivity.startWebServer():创建并启动 WebServer 实例,根目录设为 Download 文件夹。

      • MainActivity.stopWebServer():调用 webServer.stop() 关闭服务器监听。

      • WebServer.serve(IHTTPSession):核心路由分发,根据 URI 与 Method 调度到对应的处理方法。

      • WebServer.listFiles():扫描指定目录文件,返回 JSON 数组。

      • WebServer.handleUpload(...):(待完善)处理 multipart/form-data 上传。

      • HttpClientHelper.get(...):异步 GET 请求并回调结果。

      • HttpClientHelper.downloadFile(...):使用 HttpURLConnection 下载文件到指定路径。

      • PermissionManager 系列方法:检查和申请存储权限,确保读写外部存储。


        六、项目总结

        1. 成果回顾

          • 成功在 Android 端同时运行轻量级 HTTP 服务端和客户端。

          • 支持基本 REST 接口与文件服务。

          • 技术收获

            • 深刻理解 HTTP 协议请求/响应流程。

            • 掌握了 NanoHTTPD 在 Android 中的集成与使用。

            • 强化了 Android 异步网络请求与多线程模型。

            • 后续优化

              • 完善文件上传逻辑,支持大文件断点续传。

              • 增加界面友好度,如文件列表分页、下载进度显示。

              • 引入 OkHttp 库替换原生 HttpURLConnection,提升性能与稳定性。

              • 将业务逻辑拆分为 MVVM 模式,提高可维护性。

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。

相关阅读

目录[+]

取消
微信二维码
微信二维码
支付宝二维码