43 安卓 NanoHTTPD Demo

安卓demo,使用NanoHTTPD; 使用未被占用的端口,前端资源转发内部文件夹,且限制其他应用访问

为了在Android中使用NanoHTTPD创建一个HTTP服务器,监听未被占用的端口,并将前端资源转发到内部文件夹,同时限制其他应用访问,可以参考以下示例代码:

import android.Manifest;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.util.Random;

import fi.iki.elonen.NanoHTTPD;

public class MainActivity extends AppCompatActivity {

    private static final int REQUEST_PERMISSIONS = 100;
    private static final String TAG = "MainActivity";
    private static final String INTERNAL_FOLDER_PATH = "/data/data/<your_package_name>/files/public_html"; // 替换为你的内部文件夹路径

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED ||
                ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                        != PackageManager.PERMISSION_GRANTED ||
                ContextCompat.checkSelfPermission(this, Manifest.permission.INTERNET)
                        != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.INTERNET},
                    REQUEST_PERMISSIONS);
        } else {
            startServer();
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if (requestCode == REQUEST_PERMISSIONS) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED &&
                    grantResults[1] == PackageManager.PERMISSION_GRANTED &&
                    grantResults[2] == PackageManager.PERMISSION_GRANTED) {
                startServer();
            } else {
                Toast.makeText(this, "Permission denied", Toast.LENGTH_SHORT).show();
            }
        }
    }

    private void startServer() {
        Random rand = new Random();
        int port = -1;
        while (port == -1 || !isPortAvailable(port)) { // 循环查找可用端口
            port = rand.nextInt(60000 - 1024) + 1024; // 生成1024到65535之间的随机端口号
        }

        MyHTTPServer server = null;
        try {
            server = new MyHTTPServer(new InetSocketAddress(InetAddress.getLocalHost(), port));
            Log.i(TAG, "Server started on port: " + port);
            server.start(NanoHTTPD.SOCKET_READ_TIMEOUT, false);
        } catch (IOException e) {
            Log.e(TAG, "Failed to start server on port: " + port, e);
            Toast.makeText(this, "Failed to start server on port: " + port, Toast.LENGTH_SHORT).show();
        }
    }

    private boolean isPortAvailable(int port) {
        // 检查端口是否可用,这可以通过尝试绑定来实现,但此处简化处理,实际应用中可能需要更复杂的检查逻辑
        return true; // 这里假设端口可用,实际应替换为相应的方法进行检测
    }

    public static class MyHTTPServer extends NanoHTTPD {

        public MyHTTPServer(InetSocketAddress address) {
            super(address);
        }

        @Override
        public Response serve(IHTTPSession session) {
            Method method = session.getMethod();
            String uri = session.getUri();

            File file = new File(INTERNAL_FOLDER_PATH + uri);
            if (file.exists() && file.isFile()) {
                try {
                    FileInputStream fis = new FileInputStream(file);
                    byte[] data = new byte[(int) file.length()];
                    fis.read(data);
                    fis.close();
                    return newFixedLengthResponse(Response.Status.OK, MIME_TYPES.get("html"), ByteBuffer.wrap(data), data.length);
                } catch (Exception e) {
                    Log.e(TAG, "Error serving file", e);
                    return newFixedLengthResponse(Response.Status.INTERNAL_ERROR, MIME_PLAINTEXT, "Error serving file");
                }
            } else {
                return newFixedLengthResponse(Response.Status.NOT_FOUND, MIME_PLAINTEXT, "File not found");
            }
        }
    }
}

在这个示例中:

  • 首先,我们在启动服务器之前请求了必要的权限。
  • 然后,我们寻找一个未被占用的端口并启动服务器。
  • 自定义的MyHTTPServer类重写了serve()方法以服务来自客户端的请求。当接收到请求时,它会从指定的内部文件夹加载文件内容作为响应。

注意

  • 要确保你已经正确设置了内部文件夹路径(INTERNAL_FOLDER_PATH)。
  • 关于“限制其他应用访问”,由于是在同一设备上运行,Android默认的安全模型通常不允许其他应用直接访问此应用的数据目录。然而,如果需要进一步强化安全性,可以通过ContentProvider或其他安全机制提供更加精细的控制。
  • 在真实场景下,请务必实现isPortAvailable()方法,用来检查端口是否已被占用。

此外,在Android应用中开启HTTP服务器需要注意系统的后台执行策略和网络权限管理,确保服务能在合适的时机和环境下运行。

安卓demo,使用NanoHTTPD; 对 /static 路径转发到应用内部静态文件,/api 转发到远程服务端;使用webview 访问NanoHTTPD

为了实现使用NanoHTTPD在Android中创建一个简单的本地服务器,将 /static 路径映射到应用内部的静态文件夹,并将 /api 转发到远程服务端,同时通过WebView访问这个本地服务器,可以参考以下示例代码:

首先,扩展NanoHTTPD以处理不同的路由请求:

import fi.iki.elonen.NanoHTTPD;
import fi.iki.elonen.router.RouterNanoHTTPD;

public class CustomServer extends RouterNanoHTTPD {

    public CustomServer(int port) {
        super(port);
        setupRoutes();
    }

    private void setupRoutes() {
        // 将/static路径映射到本地静态资源目录
        addRoute("/static/*", new StaticFileHandler(getInternalFilesDir("public_html"))); // 替换为你的内部文件夹路径

        // 将/api转发到远程服务端
        addRoute("/api/*", new ApiProxyHandler()); // 这里假设有个ApiProxyHandler来处理API转发逻辑
    }

    // 静态文件处理器类
    public static class StaticFileHandler extends SimpleWebServer.StaticResourceHandler {
        public StaticFileHandler(File rootDir) {
            super(rootDir, null, true);
        }
    }

    // API代理处理器类(需要实现)
    public static class ApiProxyHandler implements RouterNanoHTTPD.UriResource {
        @Override
        public NanoHTTPD.Response get(NanoHTTPD.IHTTPSession session) {
            // 在这里编写向远程服务器发送请求并返回响应的逻辑
            // 示例:
            String remoteUrl = "http://your-remote-server.com" + session.getUri(); // 构造远程URL
            // 使用网络库(如OkHttp)发起请求,获取响应,并将其转换为NanoHTTPD的Response对象返回
            return NanoHTTPD.newFixedLengthResponse(Response.Status.OK, "application/json", "API Response"); // 示例响应
        }

        // 可能还需要覆盖其他方法以支持POST、PUT等HTTP方法
    }

    // 启动服务器
    public static void startServer(Context context) {
        int port = 8080; // 设置你的端口号
        CustomServer server = new CustomServer(port);

        try {
            server.start(NanoHTTPD.SOCKET_READ_TIMEOUT, false);
            Log.i("CustomServer", "Server started on port: " + port);
        } catch (IOException e) {
            Log.e("CustomServer", "Failed to start server on port: " + port, e);
        }
    }
}

然后,在你的Activity中启动服务器并设置WebView:

import android.Manifest;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;

public class MainActivity extends AppCompatActivity {

    private static final int REQUEST_PERMISSIONS = 100;
    private WebView webView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        webView = findViewById(R.id.web_view);

        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED ||
                ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                        != PackageManager.PERMISSION_GRANTED ||
                ContextCompat.checkSelfPermission(this, Manifest.permission.INTERNET)
                        != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.INTERNET},
                    REQUEST_PERMISSIONS);
        } else {
            startServerAndLoadWebView();
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if (requestCode == REQUEST_PERMISSIONS) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED &&
                    grantResults[1] == PackageManager.PERMISSION_GRANTED &&
                    grantResults[2] == PackageManager.PERMISSION_GRANTED) {
                startServerAndLoadWebView();
            } else {
                Toast.makeText(this, "Permission denied", Toast.LENGTH_SHORT).show();
            }
        }
    }

    private void startServerAndLoadWebView() {
        CustomServer.startServer(this); // 启动服务器

        WebSettings settings = webView.getSettings();
        settings.setJavaScriptEnabled(true);
        settings.setAllowFileAccessFromFileURLs(true);
        settings.setAllowUniversalAccessFromFileURLs(true);

        webView.setWebViewClient(new WebViewClient() {
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                // 确保WebView加载本地服务器资源而不是跳转外部浏览器
                if (url.startsWith("http://localhost:8080")) { // 注意替换为你服务器的实际地址和端口
                    view.loadUrl(url);
                    return true;
                }
                return super.shouldOverrideUrlLoading(view, url);
            }
        });

        // 加载本地服务器上的HTML文件作为起始页面
        webView.loadUrl("http://localhost:8080/index.html"); // 替换为你服务器上实际的HTML文件路径
    }
}

注意

  1. 在上述示例中,我们使用了RouterNanoHTTPD以便更方便地处理路由。
  2. ApiProxyHandler 类需要实现向远程服务器转发请求并返回响应的功能。你可以选择任何适合的网络库(例如OkHttp)来完成这一部分。
  3. 在实际项目中,请确保已经正确设置了内部文件夹路径和远程服务器URL。
  4. 对于安全性和性能,请根据实际情况调整配置,如开启HTTPS、限制允许的请求类型等。

安卓使用 NanoHTTPD 或RouterNanoHTTPD 开启https服务及转发https请求到远程 

在Android中,NanoHTTPD并不直接支持HTTPS服务。但是,你可以通过集成SSL/TLS证书和自定义SocketFactory来实现HTTPS功能。然而,RouterNanoHTTPD本身并没有提供内置的HTTPS支持,你需要对NanoHTTPD进行扩展以支持HTTPS。

以下是一个基本的示例,展示如何使用自定义的SecureServerSocketFactory来创建一个支持HTTPS的服务器(基于NanoHTTPD

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import fi.iki.elonen.NanoHTTPD;
import fi.iki.elonen.router.RouterNanoHTTPD;

public class SecureCustomServer extends RouterNanoHTTPD {

    private static final String SSL_KEYSTORE_PATH = "path_to_your_keystore";
    private static final String SSL_KEYSTORE_PASSWORD = "your_keystore_password";
    private static final int PORT = 4443; // HTTPS通常使用非标准端口

    public SecureCustomServer() {
        super(PORT);
        setupSSL();
        setupRoutes();
    }

    private void setupSSL() {
        try {
            KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
            InputStream keyInput = new FileInputStream(SSL_KEYSTORE_PATH);
            try {
                keyStore.load(keyInput, SSL_KEYSTORE_PASSWORD.toCharArray());
            } finally {
                keyInput.close();
            }

            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            keyManagerFactory.init(keyStore, SSL_KEYSTORE_PASSWORD.toCharArray());

            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            trustManagerFactory.init(keyStore);

            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);

            SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();

            this.myServerSocketFactory = sslSocketFactory;

        } catch (Exception e) {
            throw new RuntimeException("Failed to initialize SSL", e);
        }
    }

    private void setupRoutes() {
        addRoute("/api/*", new ApiProxyHandler()); // 假设有个ApiProxyHandler来处理API转发逻辑
    }

    // ... 其他代码如前所述 ...
}

对于将HTTPS请求转发到远程服务端的部分,同样需要在网络请求库(例如OkHttp或Apache HttpClient等)中配置相应的SSL上下文,并正确发起HTTPS请求。下面是一个简化的OkHttp示例:

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

public class ApiProxyHandler implements RouterNanoHTTPD.UriResource {

    private OkHttpClient httpClient;

    public ApiProxyHandler() {
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        // 如果需要,可以在这里添加信任的证书、客户端证书等配置
        httpClient = builder.build();
    }

    @Override
    public NanoHTTPD.Response get(RouterNanoHTTPD.UriResource uriResource, Map<String, String> urlParams, NanoHTTPD.IHTTPSession session) {
        String remoteUrl = "https://your-remote-server.com/api" + session.getUri(); // 构造远程URL
        Request request = new Request.Builder()
                .url(remoteUrl)
                .build();

        try (Response response = httpClient.newCall(request).execute()) {
            return NanoHTTPD.newFixedLengthResponse(
                    Response.Status.valueOf(response.code()),
                    response.header("Content-Type"),
                    response.body().string());
        } catch (IOException e) {
            Log.e("ApiProxyHandler", "Error forwarding API call", e);
            return NanoHTTPD.newFixedLengthResponse(Response.Status.INTERNAL_ERROR, MIME_PLAINTEXT, "Internal Server Error");
        }
    }

    // 可能还需要覆盖其他方法以支持POST、PUT等HTTP方法
}

请确保替换上述示例中的路径、密码以及远程服务器地址,并根据实际情况调整证书加载方式。同时,请注意在Android环境中运行HTTPS服务器可能需要额外处理权限和安全问题。

  • 21
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
安卓Socket Demo是一个演示程序,用于展示在安卓平台上如何使用Socket进行网络通信。Socket是一种网络编程接口,可以实现计算机之间的通信。 在安卓Socket Demo中,首先需要进行网络权限的配置。在AndroidManifest.xml中添加以下权限: ``` <uses-permission android:name="android.permission.INTERNET" /> ``` 然后,在应用程序中创建一个Socket对象,指定要连接的服务器的IP地址和端口号。使用Socket的InputStream和OutputStream来实现数据的读取和发送。 以下是一个简单的安卓Socket Demo示例代码: ``` import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; public class SocketDemo { private Socket clientSocket; private PrintWriter out; private BufferedReader in; public void connectToServer() { try { // 创建Socket对象,指定服务器的IP地址和端口号 clientSocket = new Socket("服务器IP地址", 服务器端口号); // 获取输出流,用于向服务器发送数据 out = new PrintWriter(clientSocket.getOutputStream(), true); // 获取输入流,用于读取服务器返回的数据 in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); // 向服务器发送数据 out.println("Hello, Server!"); // 读取服务器返回的数据 String response = in.readLine(); System.out.println("Server response: " + response); // 关闭连接 out.close(); in.close(); clientSocket.close(); } catch (IOException e) { e.printStackTrace(); } } } ``` 在上面的示例中,我们创建了一个Socket对象并连接到了指定的服务器。然后通过获取Socket的输入流和输出流,实现了向服务器发送数据和接收服务器返回的数据的功能。 总结来说,安卓Socket Demo是一个演示程序,用于展示在安卓平台上如何使用Socket进行网络通信。通过创建Socket对象并获取输入流和输出流,可以实现与服务器的数据交互。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值