java socket实现代理Android App
实现逻辑就是转发请求和响应。
核心代码
// 启动代理服务器private void startProxyServer() {new Thread(new ProxyServer()).start();}// 代理服务器static class ProxyServer implements Runnable {@Overridepublic void run() {try {// 监听指定的端口int port = 8098; //一般使用49152到65535之间的端口ServerSocket server = new ServerSocket(port);// 当一个ServerSocket关闭并释放其绑定的端口后,操作系统通常会在几分钟内不允许其他Socket再次绑定到该端口。// true:操作系统将允许其他Socket立即绑定到刚刚被释放的端口。server.setReuseAddress(true);// 使用线程池,防止过多线程耗尽资源ExecutorService threadPool = Executors.newFixedThreadPool(50);while (true) {Socket socket = server.accept(); //会一直阻塞,直到有客户端连接进来// new Thread 只是创建一个类的对象实例而已。而真正创建线程的是start()方法。// 这里并没有直接调用start()方法,所以并没创建新线程,而是交给线程池去执行。threadPool.submit(new ProxyClient(socket));}} catch (Exception e) {Log.e("ProxyServer", e.getMessage(), e);}}}// 代理客户端static class ProxyClient implements Runnable {private final Socket proxySocket;//代理Socketprivate Socket targetSocket = null;//目标Socketpublic ProxyClient(Socket socket) {this.proxySocket = socket;}@Overridepublic void run() {try {//客户端请求的报文InputStream req = proxySocket.getInputStream();int read;int contentLength = 0;//body长度String method = null;//请求方法String url = null;//请求地址String protocol = null;//请求协议ByteArrayOutputStream os = new ByteArrayOutputStream();ByteArrayOutputStream reqBack = new ByteArrayOutputStream();//解析,提取请求报文while ((read = req.read()) != -1) {os.write(read);reqBack.write(read);if (read == '\n') {//CONNECT www.xx.com:443/xx/yy HTTP/1.1String line = os.toString("UTF-8");os.reset();//重置,以便再次使用if ("\r\n".equals(line)) {//空行,请求头结束标志break;}StringTokenizer stringTokenizer = new StringTokenizer(line, " ");if (method == null) {//八种请求方法:GET、POST、HEAD、OPTIONS、PUT、PATCH、DELETE、TRACE、CONNECT 方法method = stringTokenizer.nextToken().toLowerCase();//connecturl = stringTokenizer.nextToken();//www.xx.com:443/xx/yyprotocol = stringTokenizer.nextToken().trim();//HTTP/1.1} else {String key = stringTokenizer.nextToken().toLowerCase();if ("content-length:".equals(key)) {String value = stringTokenizer.nextToken().trim();contentLength = Integer.parseInt(value);}}}}if (contentLength > 0) {for (int i = 0; i < contentLength; i++) {reqBack.write(req.read());}}//完整请求报文// String request = reqBack.toString("UTF-8");// System.out.println("请求报文开始");// System.out.print(request);// System.out.println("\r\n请求报文结束");//拼接完整urlif (url != null && !url.startsWith("http")) {url = method.equals("connect") ? "https://" + url : "http://" + url;}URL u = new URL(url);//目标ipString targetHost = u.getHost();//目标端口int targetPort = u.getPort();if (targetPort == -1) {targetPort = 80;}//目标SockettargetSocket = new Socket(targetHost, targetPort);if ("connect".equals(method)) {//https//HTTP/1.1 200 Connection established//报文直接发送给代理SocketOutputStream outputStream = proxySocket.getOutputStream();outputStream.write((protocol + " 200 Connection established\r\n").getBytes(StandardCharsets.UTF_8));outputStream.write("Proxy-agent: ProxyServer/1.0\r\n".getBytes(StandardCharsets.UTF_8));outputStream.write("\r\n".getBytes(StandardCharsets.UTF_8));outputStream.flush();//前者转发给后者,代理Socket转发给目标SocketThread proxy2target = new Thread(new ForwardData(proxySocket, targetSocket));proxy2target.start();//前者转发给后者,目标Socket转发给代理SocketThread target2proxy = new Thread(new ForwardData(targetSocket, proxySocket));target2proxy.start();proxy2target.join();} else {//http//请求报文转发给目标SocketOutputStream outputStream = targetSocket.getOutputStream();outputStream.write(reqBack.toByteArray());outputStream.flush();//前者转发给后者,目标Socket转发给代理SocketThread thread = new Thread(new ForwardData(targetSocket, proxySocket));thread.start();thread.join();}} catch (Exception e) {Log.e("ProxyClient", e.getMessage(), e);} finally {try {if (targetSocket != null) {targetSocket.close();}} catch (IOException e) {Log.e("ProxyClient", e.getMessage(), e);}try {if (proxySocket != null) {proxySocket.close();}} catch (IOException e) {Log.e("ProxyClient", e.getMessage(), e);}}// Log.e("ProxyClient", "结束");}// 转发数据static class ForwardData implements Runnable {private final Socket inputSocket;private final Socket outputSocket;public ForwardData(Socket inputSocket, Socket outputSocket) {this.inputSocket = inputSocket;this.outputSocket = outputSocket;}@Overridepublic void run() {try {InputStream inputStream = inputSocket.getInputStream();OutputStream outputStream = outputSocket.getOutputStream();int read;while ((read = inputStream.read()) != -1) {outputStream.write(read);}} catch (Exception e) {// Log.e("ForwardData", inputSocket + e.getMessage());}}}}
app源码
proxyserver: 代理服务器app
我已打包,打包地址:https://gitee.com/gloweds/proxyserver/raw/master/app/release/app-release.apk
有时会报错,但是这个错误不影响功能。
报错时间线如下:
2023-10-06 11:29:16.478 客户端请求结束(发起http请求)
2023-10-06 11:29:16.555 代理proxySocket的read()报错 Connection reset
2023-10-06 11:29:16.571 关闭两个Socket连接
2023-10-06 11:29:16.571 目标targetSocket的read()报错 Socket closed
上面报错的原因,是因为客户端请求发送报文没有完整发送结束标识-1,
如果客户端完整发送结束标识,上面的两个错误不会发生(Connection reset、Socket closed),但是这个错误不影响功能,可以不用处理。
代理proxySocket的read()报错 Connection reset,是因为客户端未完整发送结束标识-1,而客户端请求都结束了,也成功拿到了响应数据,这时关闭连接就导致代理proxySocket的read()报错 Connection reset。
目标targetSocket的read()报错 Socket closed,因为前面代理proxySocket的read()阻塞未正常发送结束标识,所以targetSocket的read()也阻塞了,关闭两个proxySocket和targetSocket连接后,targetSocket的read()自然报错Socket closed连接被强制关闭了。
https请求流程图:
相关文章:

java socket实现代理Android App
实现逻辑就是转发请求和响应。 核心代码 // 启动代理服务器private void startProxyServer() {new Thread(new ProxyServer()).start();}// 代理服务器static class ProxyServer implements Runnable {Overridepublic void run() {try {// 监听指定的端口int port 8098; //一…...

Nacos与Eureka的区别
大家好我是苏麟今天说一说Nacos与Eureka的区别. Nacos Nacos的服务实例分为两种l类型: 临时实例:如果实例宕机超过一定时间,会从服务列表剔除,默认的类型。非临时实例:如果实例宕机,不会从服务列表剔除&…...
浅谈Rob Pike的五条编程规范
又是一篇需要我们多些思考的文章~ 简介下Rob Pike Rob Pike是Unix的先驱,UTF-8的设计人,Go语言核心设计者之一。 Rob Pike的5条编程规则 原文地址:http://users.ece.utexas.edu/~adnan/pike.html 中文翻译: 罗布派克&#x…...

LeetCode 377.组合总和IV 可解决一步爬m个台阶到n阶楼顶问题( 完全背包 + 排列数)
给你一个由 不同 整数组成的数组 nums ,和一个目标整数 target 。请你从 nums 中找出并返回总和为 target 的元素组合的个数。 题目数据保证答案符合 32 位整数范围 示例 1: 输入:nums [1,2,3], target 4 输出:7 解释&#x…...

C中volatile总结
在CPU处理过程中,需要将内存中的数据载入到寄存器中才能计算,所以可能涉及到一个问题,如果内存中的数据被更改了,但是寄存器还是使用的旧数据,这样就会造成数据的不同步。 一、volatile关键字的作用 使用volatile关键…...

asp.net班级管理系统VS开发sqlserver数据库web结构c#编程Microsoft Visual Studio
一、源码特点 asp.net班级管理系统 是一套完善的web设计管理系统,系统具有完整的源代码和数据库,系统主要采用B/S模式开发。开发环境为vs2010,数据库为sqlserver2008,使用c#语言开发 asp.net班级管理系统 二、功能介绍 1…...

【Pytorch笔记】6.Transforms
pytorch官方文档 - transforms transforms需要使用计算机视觉工具包:torchvision。 torchvision.transforms:常用的图像预处理方法; torchvision.datasets:常用数据集的dataset实现,如MNIST、CIFAR-10、ImageNet等&am…...

nodejs+vue临沂特色产品销售平台elementui
从实际工作出发,对过去的临沂特色产品销售平台存在的问题进行分析,完善用户的使用体会。采用计算机系统来管理信息 提高了工作的效率。 随着信息化社会的形成和微电子技术日新月异的发展,临沂特色产品销售平台是针对目前临沂特色产品销售…...
机器学习必修课 - 使用管道 Pipeline
目标:学习使用管道(pipeline)来提高机器学习代码的效率。 1. 运行环境:Google Colab import pandas as pd from sklearn.model_selection import train_test_split!git clone https://github.com/JeffereyWu/Housing-prices-dat…...

WEB各类常用测试工具
一、单元测试/测试运行器 1、Jest 知名的 Java 单元测试工具,由 Facebook 开源,开箱即用。它在最基础层面被设计用于快速、简单地编写地道的 Java 测试,能自动模拟 require() 返回的 CommonJS 模块,并提供了包括内置的测试环境 …...
Naive UI 文档地址
最近几天官网访问不了,自己用github pages 部署了个 官网 github pages...
在CentOS7系统中安装MySQL5.7
第一步:下载MySQL包 > wget http://repo.mysql.com/mysql57-community-release-el7-10.noarch.rpm第二步:安装MySQL源 > rpm -Uvh mysql57-community-release-el7-10.noarch.rpm第三步:安装MySQL服务端 > yum install -y mysql-c…...

R语言通过接口获取网上数据平台的免费数据
大家好,我是带我去滑雪! 作为一名统计学专业的学生,时常和数据打交道,我深知数据的重要性。数据是实证研究的重要基础,每当在完成一篇科研论文中的实证研究部分时,我都能深刻体会实证研究最复杂、最耗时的工…...

【Docker内容大集合】Docker从认识到实践再到底层原理大汇总
前言 那么这里博主先安利一些干货满满的专栏了! 首先是博主的高质量博客的汇总,这个专栏里面的博客,都是博主最最用心写的一部分,干货满满,希望对大家有帮助。 高质量博客汇总https://blog.csdn.net/yu_cblog/categ…...

算法题:摆动序列
这道题是一道贪心算法题,如果前两个数是递增,则后面要递减,如果不符合则往后遍历,直到找到符合的。(完整题目附在了最后) 代码如下: class Solution(object):def wiggleMaxLength(self, nums):…...

复习 --- QT服务器客户端
服务器: 头文件: #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include<QTcpServer> #include<QTcpSocket> #include<QMessageBox> #include<QDebug> #include<QList> #include<QListWidget> #in…...

Godot 官方2D游戏笔记(1):导入动画资源和添加节点
前言 Godot 官方给了我们2D游戏和3D游戏的案例,不过如果是独立开发者只用考虑2D游戏就可以了,因为2D游戏纯粹,我们只需要关注游戏的玩法即可。2D游戏的美术素材简单,交互逻辑简单,我们可以把更多的时间放在游戏的玩法…...
leetcode 热题 100
数组和字符串匹配 子串和子序列 原串:“abcabc” 子串:“abc”, 连续但不大于原串的字符串 子序列:“acc”, 字符来自原串且保持在原串中顺序不变的字符串 子排列: “aabbcc”, 字符来自原串且只能用1次,但可有不同排列顺序的字…...

Ae 效果:CC Lens
扭曲/CC Lens Distort/CC Lens CC Lens (CC 镜头)主要用于添加或移除摄像机镜头扭曲,比如桶形失真 Barrel、枕形失真 Pincushion以及鱼眼失真 Fisheye等。或者,用它来创建一些特殊的动画效果。 ◆ ◆ ◆ 效果属性说明 Center 中…...

【Redis】基础数据结构-quicklist
Redis List 在Redis3.2版之前,Redis使用压缩列表和双向链表作为List的底层实现。当元素个数比较少并且元素长度比较小时,Redis使用压缩列表实现,否则Redis使用双向链表实现。 ziplist存在问题 不能保存过多的元素,否则查找复杂度…...

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析
1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具,该工具基于TUN接口实现其功能,利用反向TCP/TLS连接建立一条隐蔽的通信信道,支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式,适应复杂网…...
Oracle查询表空间大小
1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

MMaDA: Multimodal Large Diffusion Language Models
CODE : https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA,它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构…...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)
Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败,具体原因是客户端发送了密码认证请求,但Redis服务器未设置密码 1.为Redis设置密码(匹配客户端配置) 步骤: 1).修…...

JVM虚拟机:内存结构、垃圾回收、性能优化
1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...

JVM 内存结构 详解
内存结构 运行时数据区: Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器: 线程私有,程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 每个线程都有一个程序计数…...

RabbitMQ 各类交换机
为什么要用交换机? 交换机用来路由消息。如果直发队列,这个消息就被处理消失了,那别的队列也需要这个消息怎么办?那就要用到交换机 交换机类型 1,fanout:广播 特点 广播所有消息:将消息…...

AWSLambda之设置时区
目标 希望Lambda运行的时区是东八区。 解决 只需要设置lambda的环境变量TZ为东八区时区即可,即Asia/Shanghai。 参考 使用 Lambda 环境变量...