为什么要写这个系列
故事的开头要从博主两年前做的一个需求开始. 当时我在公司的Android项目组做开发, app要加一个免流量分享应用的功能. 应用场景是这样: 我手机上装了一个app, 现在要传给你, 不耗费双方的上网流量. 怎么做呢? 过程是这样:
- 我在手机上开一个WiFi热点, app内部起一个web服务, 把web服务的地址生成二维码.
- 你扫这个二维码, 得到一个网址, 如:http://192.168.x.xx:8080/
- 你在手机浏览器里打开这个网址, 看到的是一个介绍页面, 点击一个免流量下载按钮, 访问链接是:http://192.168.x.xx:8080/download
- 我这边app内部的web服务器将apk文件通过http发送给你, 对你来说, 跟平时在网页上下载是一样的, 下载完成后, 直接安装. 整个过程不访问外网, 所有通信都通过我的WiFi热点完成, 达到免流量的目的.
当时从技术上来拆分, 主要有以下三点:
- WiFi热点的控制 -> 通过系统api调用可以轻松完成
- 二维码生成 -> 通过zxing库可以做到
- 在手机里起web服务 -> 看起来有点麻烦
http这一块Android的客户端支持是很完善的, 像apache的HttpClient, java自带的UrlConnection都可以用, 但对于服务器这一块, 还真没接触过, 毕竟 像Tomcat, nginx这种web服务器一般来说都部署在专用服务器主机上的. 这该咋办呢?
基于当时的认知情况, 博主采用了一种最原始的方法: 基于Java的原生socket, 手动写一个简单的web服务器. 大体实现如下:
1.绑定tcp socket到8080端口, 接受连接请求
2. 收到连接请求后, 读取客户端发来的数据
3. 按行解析这些数据, 分析Request-Line, 得到访问的路径, 如果是”/”, 则读取index.html文件, 写入连接socket. 当然了, 在写文件数据前, 需要按照http规范发送Response-Header, 像Content-Length, Content-Type这些字段; 如果是”/download”, 则按照文件的格式发送Response-Header, 跟上apk文件的数据; 其他路径就直接返回404错误.
最后按这种方式实现了目标功能. 但是一眼看来这样实现是有缺点的, 稍微复杂一点的功能都要手动在这个基础上添加, 而很多功能其实都是在HTTP规范里面定义好的, 只要是一个规范的http服务器, 都会实现这些功能, 手动写服务器其实是重复造轮子的动作. 当时在想, 要是有一个可以方便嵌入到手机的web服务器该多好啊!
后来, 由于博主的兴趣在服务端, 正好公司有这个契机, 我就转去做服务器了. Android端也有一部分工作, 主要是给业务团队提供SDK, 封装一部分JNI.
又过了一段时间, 博主遇到一个需求, 手机客户端的日志, 存储到后台做分析. 这里又遇到http服务器了, 当然这次运行的目标