- Flutter Application: Flutter应用标准模版
- Flutter Module :Flutter与原生混合开发
- Flutter Plugin:Flutter插件
- Flutter Package:纯Dart组件
- Flutter Skeleton:Flutter应用建议实践例程模版
月度归档: 2020 年 12 月
flutter开发:sqlflite的使用
引入插件
1 2 3 4 5 |
dependencies: flutter: sdk: flutter cupertino_icons: ^1.0.0 sqflite: ^1.3.2+1 |
封装公共方法
这里的两个公共类是封装的比较好的,只要有sqlflite数据库操作就可以拿过来直接用的
- 数据库初始化,获取数据库对象以及关闭数据库。在sqflite中的数据库操作,首先得获取到数据库对象,通过数据库对象才可以对相应的表进行增删查改。
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 |
import 'package:path/path.dart'; import 'package:sqflite/sqflite.dart'; class SqlManager { static const _VERSION = 1; static const _NAME = "my.db"; static Database _database; ///初始化 static init() async { var databasesPath = await getDatabasesPath(); String path = join(databasesPath, _NAME); _database = await openDatabase(path, version: _VERSION, onCreate: (Database db, int version) async {}); } ///判断表是否存在 static isTableExits(String tableName) async { await getCurrentDatabase(); var res = await _database.rawQuery( "select * from Sqlite_master where type = 'table' and name = '$tableName'"); return res != null && res.length > 0; } ///获取当前数据库对象 static Future<Database> getCurrentDatabase() async { if (_database == null) { await init(); } return _database; } ///关闭 static close() { _database?.close(); _database = null; } } |
- 在对某一张表进行操作时,先得判断数据库对象是否存在,不存在就得创建。再判断表是否在该数据库中存在,不存在就创建。
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 |
import 'package:meta/meta.dart'; import 'package:sqflite/sqflite.dart'; import 'SqlManager.dart'; abstract class BaseDbProvider { bool isTableExits = false; createTableString(); tableName(); ///创建表sql语句 tableBaseString(String sql) { return sql; } Future<Database> getDataBase() async { return await open(); } ///super 函数对父类进行初始化 @mustCallSuper prepare(name, String createSql) async { isTableExits = await SqlManager.isTableExits(name); if (!isTableExits) { Database db = await SqlManager.getCurrentDatabase(); return await db.execute(createSql); } } @mustCallSuper open() async { if (!isTableExits) { await prepare(tableName(), createTableString()); } return await SqlManager.getCurrentDatabase(); } } |
具体操作
- 新建bean类,这里除了基本的get和set方法之外还有User对象转map,map转User的两个方法。
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 |
class User { User(); int _id; String _name; String _desc; int get id => _id; String get name => _name; String get desc => _desc; set desc(String value) { _desc = value; } set name(String value) { _name = value; } set id(int value) { _id = value; } Map<String, dynamic> toMap() { var map = Map<String, dynamic>(); map['id'] = _id; map['name'] = _name; map['desc'] = _desc; return map; } User.fromMapObject(Map<String, dynamic> map) { this._id = map['id']; this._name = map['name']; this._desc = map['desc']; } } |
- User表的增删查改方法
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 |
import 'package:sqflite/sqlite_api.dart'; import 'BaseDbProvider.dart'; import 'User.dart'; class UserDbProvider extends BaseDbProvider { ///表名 final String name = 'UserInfo'; final String columnId = "id"; final String columnName = "name"; final String columnDesc = "desc"; UserDbProvider(); //获取表名称 @override tableName() { return name; } //创建表操作 @override createTableString() { return ''' create table $name ( $columnId integer primary key,$columnName text not null, $columnDesc text not null) '''; } ///查询数据 Future selectUser(int id) async { Database db = await getDataBase(); return await db.rawQuery("select * from $name where $columnId = $id"); } //查询数据库所有 Future<List<Map<String, dynamic>>> selectMapList() async { var db = await getDataBase(); var result = await db.query(name); return result; } //获取数据库里所有user Future<List<User>> getAllUser() async { var userMapList = await selectMapList(); var count = userMapList.length; List<User> userList = List<User>(); for (int i = 0; i < count; i++) { userList.add(User.fromMapObject(userMapList[i])); } return userList; } //根据id查询user Future<User> getUser(int id) async { var noteMapList = await selectUser(id); // Get 'Map List' from database var user = User.fromMapObject(noteMapList[id]); return user; } //增加数据 Future<int> insertUser(User user) async { var db = await getDataBase(); var result = await db.insert(name, user.toMap()); return result; } //更新数据 Future<int> update(User user) async { var database = await getDataBase(); var result = await database.rawUpdate( "update $name set $columnName = ?,$columnDesc = ? where $columnId= ?", [user.name, user.desc, user.id]); return result; } //删除数据 Future<int> deleteUser(int id) async { var db = await getDataBase(); var result = await db.rawDelete('DELETE FROM $name WHERE $columnId = $id'); return result; } } |
外部使用
- 创建Provider对象。
1 |
UserDbProvider provider = UserDbProvider(); |
2.通过对象调用User的增删查改方法
1 2 3 4 5 6 |
if (user.id == null) { user.id = new DateTime.now().millisecondsSinceEpoch; //id 为当前时间戳 result = await provider.insertUser(user); } else { result = await provider.update(user); } |
完整demo
参考链接
PlantUML高效绘制流程图
背景
最近需要绘制较多的流程图,结果发现原始的绘图方式不便于维护调整,后来发现PlantUML
可以代码的方式实现,感觉不错。目前比较方便的工具是IntelliJ IDEA
提供的PlantUML
插件了。其他的比如vscode
提供的插件,目前测试不能正常绘制流程图。
IntelliJ IDEA
新建一个.md
扩展名的文件,就可以直接预览并且导出PlantUML
绘制的流程图了。
活动图(新语法)
当前活动图(activity diagram)的语法有诸多限制和缺点,比如代码难以维护。
所以从V7947开始提出一种全新的、更好的语法格式和软件实现供用户使用(beta版)。
就像序列图一样,新的软件实现的另一个优点是它不再依赖与Graphviz。
新的语法将会替换旧的语法。然而考虑到兼容性,旧的语法仍被能够使用以确保向前兼容。
但是我们鼓励用户使用新的语法格式。
简单活动图
活动标签(activity label)以冒号开始,以分号结束。
文本格式支持creole wiki语法。
活动默认安装它们定义的顺序就行连接。
1 2 3 4 5 |
@startuml :Hello world; :This is on defined on several **lines**; @enduml |
Clang的线程安全分析模块Thread Safety Analysis
背景
最近在浏览 Android 11 源代码的时候,发现在ART虚拟机头文件 art_method.h 中,存在大量的类似REQUIRES_SHARED(Locks::mutator_lock_);
的代码。
Brotli压缩
Brotli是一种全新的数据格式,可以提供比Zopfli高20-26%的压缩比。据谷歌研究,Brotli压缩速度同zlib的Deflate实现大致相同,而在Canterbury语料库上的压缩密度比LZMA和bzip2略大。
链接:Google开源Brotli压缩算法 。
微软使用了一种基于谷歌提供的C代码的实现,向.NET Core 2.1添加了Brotli压缩支持。由于Brotli得到了许多Web浏览器和Web服务器的广泛支持,所以.NET Core提供对这项技术的支持是非常有用的。
什么是 Brotli 压缩算法
Brotli最初发布于2015年,用于网络字体的离线压缩。Google软件工程师在2015年9月发布了包含通用无损数据压缩的Brotli增强版本,特别侧重于HTTP压缩。其中的编码器被部分改写以提高压缩比,编码器和解码器都提高了速度,流式API已被改进,增加更多压缩质量级别。新版本还展现了跨平台的性能改进,以及减少解码所需的内存。
与常见的通用压缩算法不同,Brotli使用一个预定义的120千字节字典。该字典包含超过13000个常用单词、短语和其他子字符串,这些来自一个文本和HTML文档的大型语料库。预定义的算法可以提升较小文件的压缩密度。
使用brotli替换deflate来对文本文件压缩通常可以增加20%的压缩密度,而压缩与解压缩速度则大致不变。使用Brotli进行流压缩的内容编码类型已被提议使用“br”。
Brotli 的实际压缩效果,需要根据需要来具体分析,目前并不是每个网站都有较好的加速效果,另外,目前仅仅被限制在HTTPS上,HTTP是不支持的。
在 ubuntu 20.04 系统上自带的 apache 2.4.41 上启用 brotli 压缩算法的方式如下:
1 2 3 |
$ sudo apt-get install brotli $ sudo a2enmod brotli |
在 Apache 2 的配置文件增加 brolti 的配置信息,如下:
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 |
<VirtualHost 1.2.3.4:443> DocumentRoot /var/www/ ServerName example.com SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem Include /etc/letsencrypt/options-ssl-apache.conf CustomLog /var/log/apache2/example.com.access.log common ErrorLog /var/log/apache2/example.com.error.log <IfModule mod_brotli.c> SetOutputFilter BROTLI_COMPRESS;DEFLATE BrotliCompressionQuality 6 BrotliCompressionWindow 18 AddOutputFilterByType BROTLI_COMPRESS text/html AddOutputFilterByType BROTLI_COMPRESS text/plain AddOutputFilterByType BROTLI_COMPRESS text/xml AddOutputFilterByType BROTLI_COMPRESS text/css AddOutputFilterByType BROTLI_COMPRESS text/javascript AddOutputFilterByType BROTLI_COMPRESS application/x-javascript AddOutputFilterByType BROTLI_COMPRESS application/javascript AddOutputFilterByType BROTLI_COMPRESS application/json AddOutputFilterByType BROTLI_COMPRESS application/x-font-ttf AddOutputFilterByType BROTLI_COMPRESS application/vnd.ms-fontobject AddOutputFilterByType BROTLI_COMPRESS image/x-icon </IfModule> </VirtualHost> |
重启服务,如下:
1 |
$ sudo systemctl restart apache2.service |
测试结果
1 2 3 4 5 6 7 8 9 10 11 12 |
$ curl -I -H 'Accept-Encoding: br' https://www.mobibrw.com HTTP/1.1 200 OK Date: Wed, 10 Feb 2021 01:17:49 GMT Server: Apache Vary: Accept-Encoding,Cookie Cache-Control: max-age=3, must-revalidate Upgrade: h2 Connection: Upgrade Last-Modified: Wed, 10 Feb 2021 01:16:24 GMT Content-Encoding: br Content-Length: 1 Content-Type: text/html; charset=UTF-8 |
参考链接
Android SSL证书设置和锁定(SSL/TLS Pinning)
我们认识到在移动端开发中安全性设置非常重要,尤其是目前非常流程H5混合式开发APP,在Android开发中,我们可以通过证书锁定的方式来增加客户端与服务端的安全保障《证书锁定SSL Pinning简介及用途》,本文主要介绍 SSL数字证书在Android开发中的证书锁定(SSL/TLS Pinning)
1. 常规SSL证书设置
通常由CA权威机构签发的证书,其根证书都内置在最新的Android操作系统中,因此默认情况下可不进行SSL证书锁定,开发APP时也就变得非常简单,以infinisign.com为例,摘自App security best practices
1.1 JAVA方法
1 2 3 4 |
URL url = new URL("https://infinisign.com"); HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection(); urlConnection.connect(); InputStream in = urlConnection.getInputStream(); |
1.2 KOTLIN方法
1 2 3 4 5 6 |
val url = URL("https://infinisign.com") val urlConnection = url.openConnection() as HttpsURLConnection urlConnection.connect() urlConnection.inputStream.use { ... } |
2. 网络安全性设置
本方案是官方提供,但需要依赖Android N(Android 7.0 API 24)及以后版本,可在APP开发阶段在APP中内置安全性设置,以达到防止中间人攻击(MITM)的目的,此方法只限制在Android 7.0 API 24以后版本,因此该版本之前的安全性设置仍然需要使用证书锁定方法,本文以infinisign.com为例。
2.1 创建配置文件
创建文件res/xml/network_security_config.xml
,需要注意的是,使用证书锁定,需要配置一个备份密钥,假如证书到期或更换了CA品牌后,不至于重新发行APP,这个备份密码可以是中级证书或根证书。
通俗的说,如果系统检测到签发证书过期了,则自动使用其中级或才根级证书作为验证,因为通常中级机构、根机构的证书到期时间非常长。
但实际情况是,infinisign.com所售的CA签发证书有效期都是一年,而现在发行APP或更新APP通常在一年都会有更新重新上架操作。
1 2 3 4 5 6 7 8 9 |
<?xml version="1.0" encoding="utf-8"?> <network-security-config> <domain-config> <domain includeSubdomains="true">infinisign.com</domain> <pin digest="SHA-256">wLgBEAGmLltnXbK6pzpvPMeOCTKZ0QwrWGem6DkNf6o</pin> <!-- 备份密钥,比如infinisign.com的中级机构是geotrust --> <pin digest="SHA-256">wLgBEAGmLltnXbK6pzpvPMeOCTKZ0QwrWGem6DkNf6o</pin> </domain-config> </network-security-config> |
2.2 引入配置文件
在Androidmanifest.xml
引入配置文件android:networkSecurityConfig="@xml/network_security_config"
1 2 3 4 5 6 7 |
<?xml version="1.0" encoding="utf-8"?> <manifest ... > <application android:networkSecurityConfig="@xml/network_security_config"> <!-- application 其它子元素 --> </application> </manifest> |
3. okHttp锁定
okHttp是一个用于Android处理网络请求的开源项目,是安卓端最流行的轻量级的网络框架,其主要用来替代HttpUrlConnection处理方式
1 2 3 4 5 |
client = new OkHttpClient.Builder() .certificatePinner(new CertificatePinner.Builder() .add("infinisign.com", "sha256/S8Ff3JCaO4V...") .build()) .build(); |
不过需要注意的是,okHttp锁定证书方式不适用于公钥锁定方式,必须以证书锁定方式内置SSL数字证书。
4. TrustManager锁定
TrustManager是一个比较老的证书锁定方法,主要用于早期的Android版本或者用于一些CA根机构在Android系统中缺失根证书的情形下,当然也适用于自签名证书的锁定,通常我们不建议这样做,因为TrustManager的缺点是中间人仍然可以使用成熟的绕过方案来实现截持。
在SSL普及的今天,let's encrypt的开源免费解决方案,和一些入门的便宜DV域名型SSL证书(见PositiveSSL ¥39/年)足以媲美自签名方案。
详细请参考javax.net.ssl.TrustManager接口实现,简单步骤如下
-
在APP源码中内置证书
1/res/raw -
使用KeyStore加载证书
1234val resourceStream = resources.openRawResource(R.raw.infinisign_cert)val keyStoreType = KeyStore.getDefaultType()val keyStore = KeyStore.getInstance(keyStoreType)keyStore.load(resourceStream, null) -
TrustManagerFactory实例化证书
123val trustManagerAlgorithm = TrustManagerFactory.getDefaultAlgorithm()val trustManagerFactory = TrustManagerFactory.getInstance(trustManagerAlgorithm)trustManagerFactory.init(keyStore) -
创建SSLContext实例,与TrustManager进行绑定。
12345val sslContext = SSLContext.getInstance("TLS")sslContext.init(null, trustManagerFactory.trustManagers, null)val url = URL("http://infinisign.com/")val urlConnection = url.openConnection() as HttpsURLConnectionurlConnection.sslSocketFactory = sslContext.socketFactory
总结
在多数移动操作系统中有大大小小几十个CA机构内置的根证书,但也不排除已经不被信任的CA机构存在的旧根证书,还有一些例如国内的一些基于Android老版本的操作系统仍然面临着安全风险,所以使用SSL数字证书锁定(SSL/TLS Pinning)的目标是缩小可信CA的范围,让APP客户端传送数据更安全,更切实地保障用户数据。
参考链接
SSL相关漏洞解决方法
本次涉及漏洞
1.漏洞名称:SSL 3.0 POODLE攻击信息泄露漏洞(CVE-2014-3566)【原理扫描】
2.SSL/TLS 受诫礼(BAR-MITZVAH)攻击漏洞(CVE-2015-2808)【原理扫描】
知识普及1:SSL协议要点
SSL(Secure Sockets Layer 安全套接层)是一种基于Web应用的安全通信协议,最早由Netscape(网景)公司提出。SSL介于TCP协议和应用层协议之间,主要作用就是将HTTP、FTP等应用层的数据进行加密然后依托可靠的TCP协议在互联网上传输到目的地,其中最典型的应用就是https。
SSL提供3个基本的安全服务:
1)身份合法性:数据发送方和接收方要确认彼此身份,要确保各自的身份不会被冒充。
2)数据机密性:所有传输的数据都进行加密,并且要确保即使数据被截获也无法破解。
3)数据完整性:确保收到的数据与发送方发出的数据一致,没有被篡改。
SSL协议主要采用的数据加密算法:
1)非对称加密算法:数据加密和解密使用不同的密钥,如RSA公钥加密算法。优点是安全级别高,很难被破解;缺点是加密解密的速度慢,因此只适用于小量数据的加密。SSL协议采用非对称加密算法实现数字签名,验证数据发送方(或接收方)的身份,同时也用非对称加密算法交换密钥(用于数据加密的对称加密算法的密钥,以及用于数据完整性验证的MAC算法)。
2)对称加密算法:数据加密和解密使用同一个密钥,如DES、3DES、RC4等都是对称加密算法。优点是加解密速度快,适用于大数据量的加密,但安全性较差。SSL协议采用对称加密算法对传输的数据进行加密。
3)MAC算法:Message Authentication Codes,即消息认证码算法,MAC含有密钥散列函数算法,兼容了MD和SHA算法的特性,并在此基础上加入了密钥。SSL协议采用MAC算法来检验消息的完整性。
知识普及2:SSL协议的版本
目前在用的SSL协议主要有5个版本,分别是SSL2.0、SSL3.0、TLS1.0、TLS1.1和TLS1.2,这里的TLS(Transport Layer Security,传输层安全)协议是SSL协议的升级版。
在SSL协议曝出Poodle漏洞后,微信公众平台将取消对SSLv2、SSLv3两个版本的支持,浏览器及其他采用SSL协议的平台也会逐渐取消对SSLv2、SSLv3的支持,目前只建议使用TLSv1.0、TLSv1.1和TLSv1.2三个版本。
1.漏洞名称:SSL 3.0 POODLE攻击信息泄露漏洞(CVE-2014-3566)【原理扫描】
涉及设备漏洞服务
此次设备受此漏洞波及影响主要是因为上述系统部署了Tomcat 的https服务,而Tomcat https服务默认情况下支持SSL 3.0协议(目前在用的SSL协议主要有5个版本,分别是SSL2.0、SSL3.0、TLS1.0、TLS1.1和TLS1.2.SSL3.0是已过时且不安全的协议,目前已被TLS 1.0,TLS 1.1,TLS 1.2替代)
漏洞描述和利用
SSL3.0是已过时且不安全的协议,目前已被TLS 1.0,TLS 1.1,TLS 1.2替代,因为兼容性原因,大多数的TLS实现依然兼容SSL3.0。
为了通用性的考虑,目前多数浏览器版本都支持SSL3.0,TLS协议的握手阶段包含了版本协商步骤,一般来说,客户端和服务器端的最新的协议版本将会被使用。其在与服务器端的握手阶段进行版本协商的时候,首先提供其所支持协议的最新版本,若该握手失败,则尝试以较旧的协议版本协商。能够实施中间人攻击的攻击者通过使受影响版本浏览器与服务器端使用较新协议的协商的连接失败,可以成功实现降级攻击,从而使得客户端与服务器端使用不安全的SSL3.0进行通信,此时,由于SSL 3.0使用的CBC块加密的实现存在漏洞,攻击者可以成功破解SSL连接的加密信息,比如获取用户cookie数据。这种攻击被称为POODLE攻击(Padding Oracle On Downgraded Legacy Encryption)。此漏洞影响绝大多数SSL服务器和客户端,影响范围广泛。但攻击者如要利用成功,需要能够控制客户端和服务器之间的数据(执行中间人攻击)。
安卓抓包测试-之tPacketcapture
一. tPacketcapture工具
1.什么是Packetcapture?
tPacketCapture数据包捕获,而无需使用任何root权限,原理是构建了一个本地VPN服务,引导网络请求流经VPN服务,从而实现抓包。 tPacketCapture使用了Android OS VpnService提供。 捕获的数据保存在外部存储为PCAP文件格式。
2.操作步骤
1)安装tPacketCapture.apk
2)打开该APP,点击"Capture"按钮
3)触发需要抓包的操作
4)用以下命令,adb pull /mnt/shell/emulated/0/Android/data/jp.co.taosoftware.android.packetcapture/files/,把文件拷贝出来了
5).pcap会保存在adb的相同路径下
6)将.pcap取出后,用wireshark打开就能看到了相关信息
参考链接
Java Socket 实现HTTP与HTTPS协议发送POST/GET请求
哇,一看标题怎么这么长啊,其实意思很简单,哥讨厌用HTTP Client做POST与GET提交觉得那个毕竟是别人写得API库,所以我就自己实现了一个简单的HTTP客户端,支持POST方式提交数据,GET方式查询数据,是测试Restful API比较方便点,然后支持form与JSON两种方式提交数据,当然也支持返回数据为JSON格式.当然这些东西都是基于JAVA Socket直接完成的,不借助任何第三方的库,主要是JDK的API其实已经够用啦. 当然我也没有用URLConnect这个东西,毕竟它在Socket基础上又包装了一下,有违我写这篇文章的目的.
好啦,讲完理由,下面就说说要怎么样才能实现啊,光说不练假把式啊!大致分了几个步骤
一:当然是要知道HTTP协议,知道常用的HTTP请求头,比如Host, Accept, Content-Type 知道HTTP协议支持的方法,常用有GET/POST/PUT/DELETE等如果不知道,也不用担心,我保证你读完这篇文章,你就知道一些啦,当然有个最好的参考文档就是HTTP相关的RFC文档,认真读一下肯定解决你自己心中的HTTP那些疑惑
二: 知道发送HTTP GET与POST格式很重要, 固定的格式如下:
[REQUEST]<SP><URL><SP>[HTTP VERSION]<CLRF>
[REQUEST HEADER: ]<SP>[VALUE]<CLRF>
可以有多个请求头
最后<CLRF>
发送完HTTP请求头部以后, 针对不同请求如POST要发送内容部分,发送完成以后同样,以<CLRF>结尾.
解释: <SP>表示空格, <CLRF>表示回车换行JAVA中表示为”\r\n”REQUEST表示HTTP请求命令,可以为POST, GET, PUT, DELETE等之一,HTTP VERSION的常见可能值为HTTP/1.1或者HTTP/1.0
三: 如果1与2的知识你都具备了,下面就来介绍一下JAVA Socket的相关知识,如何创建一个JAVA客户端套接字Socket s = new Socket()如此即可,简单吧!如何连接到远程的主机与端口, 当提供URL字符串时候,可以这么做
1 2 3 4 5 6 7 8 9 |
URL url = new URL(“http://blog.csdn.net/jia20003”); String host = url.getHost; int port = url.getDefaultPort(); SocketAddress dest = new InetSocketAddress(this.host, this.port); s.connect(dest); |
即可连接到远程主机,下面就可以请求文章内容了, 也很容易的
1 2 3 4 5 6 7 8 9 |
String path = "/jia20003/article/details/16908661"; SocketAddress dest = new InetSocketAddress(this.host, this.port); socket.connect(dest); OutputStreamWriter streamWriter = new OutputStreamWriter(socket.getOutputStream()); bufferedWriter = new BufferedWriter(streamWriter); bufferedWriter.write("GET " + path + " HTTP/1.1\r\n"); bufferedWriter.write("Host: " + this.host + "\r\n"); bufferedWriter.write("\r\n"); bufferedWriter.flush(); |
最后也是最重要的一点,字符编码,尽量都用同一种字符编码来发 送请求数据,推荐使用utf-8测试程序, 我写两个简单的PHP文件放 在wamp server上,完成对全部代码的测试。
四:关于HTTP与HTTPS
HTTP协议是位于第四层协议TCP之上完成的应用层协议, 端到端都是明文传送,别人一旦网络抓包以后都可以看到你的提交与请求数据,这个好像不太安全. HTTP协议的默认端口是80这个是RFC文档声明的,属于官方标准,没什么道理可以讲.HTTPS是基于SSL加密传输的,这样别人截获你的数据包破解的概率要小一点,比HTTP安全一点,其默认端口是443, 好像QQ邮箱与谷歌的WEB Mail邮箱都是基于HTTPS. 但是HTTPS通信方式只是传输数据加密,都客户端来说是透明的,它还是一样要遵守HTTP协议规范来发送POST与GET请求等.完整的测试程序如下:
http协议实现:
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 |
package com.gloomyfish.http.client; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketAddress; import java.net.URLEncoder; public class TestDemo { private int port; private String host; private Socket socket; private BufferedReader bufferedReader; private BufferedWriter bufferedWriter; public TestDemo(String host, int port) { socket = new Socket(); this.host = host; this.port = port; } public void sendGet() throws IOException { String path = "/zhigang/getDemo.php"; SocketAddress dest = new InetSocketAddress(this.host, this.port); socket.connect(dest); OutputStreamWriter streamWriter = new OutputStreamWriter(socket.getOutputStream()); bufferedWriter = new BufferedWriter(streamWriter); bufferedWriter.write("GET " + path + " HTTP/1.1\r\n"); bufferedWriter.write("Host: " + this.host + "\r\n"); bufferedWriter.write("\r\n"); bufferedWriter.flush(); BufferedInputStream streamReader = new BufferedInputStream(socket.getInputStream()); bufferedReader = new BufferedReader(new InputStreamReader(streamReader, "utf-8")); String line = null; while((line = bufferedReader.readLine())!= null) { System.out.println(line); } bufferedReader.close(); bufferedWriter.close(); socket.close(); } public void sendPost() throws IOException { String path = "/zhigang/postDemo.php"; String data = URLEncoder.encode("name", "utf-8") + "=" + URLEncoder.encode("gloomyfish", "utf-8") + "&" + URLEncoder.encode("age", "utf-8") + "=" + URLEncoder.encode("32", "utf-8"); // String data = "name=zhigang_jia"; SocketAddress dest = new InetSocketAddress(this.host, this.port); socket.connect(dest); OutputStreamWriter streamWriter = new OutputStreamWriter(socket.getOutputStream(), "utf-8"); bufferedWriter = new BufferedWriter(streamWriter); bufferedWriter.write("POST " + path + " HTTP/1.1\r\n"); bufferedWriter.write("Host: " + this.host + "\r\n"); bufferedWriter.write("Content-Length: " + data.length() + "\r\n"); bufferedWriter.write("Content-Type: application/x-www-form-urlencoded\r\n"); bufferedWriter.write("\r\n"); bufferedWriter.write(data); bufferedWriter.write("\r\n"); bufferedWriter.flush(); BufferedInputStream streamReader = new BufferedInputStream(socket.getInputStream()); bufferedReader = new BufferedReader(new InputStreamReader(streamReader, "utf-8")); String line = null; while((line = bufferedReader.readLine())!= null) { System.out.println(line); } bufferedReader.close(); bufferedWriter.close(); socket.close(); } public static void main(String[] args) { TestDemo td = new TestDemo("127.0.0.1",8099); try { // td.sendGet(); //send HTTP GET Request td.sendPost(); // send HTTP POST Request } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } |
想要成为支持HTTPS客户端, 只要在创建Socket的时候如下:
1 |
socket = (SSLSocket)((SSLSocketFactory)SSLSocketFactory.getDefault()).createSocket(this.host, port); |
https协议实现:
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 |
package zmx.socket.http; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.Socket; import java.net.URLEncoder; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; public class SocketForHttpTest { private int port; private String host; private Socket socket; private BufferedReader bufferedReader; private BufferedWriter bufferedWriter; public SocketForHttpTest(String host,int port) throws Exception { this.host = host; this.port = port; /** * http协议 */ //socket = new Socket(this.host, this.port); /** * https协议 */ socket = (SSLSocket)((SSLSocketFactory)SSLSocketFactory.getDefault()).createSocket(this.host, this.port); } public void sendGet() throws IOException { //String requestUrlPath = "/z69183787/article/details/17580325"; String requestUrlPath = "/"; OutputStreamWriter streamWriter = new OutputStreamWriter(socket.getOutputStream()); bufferedWriter = new BufferedWriter(streamWriter); bufferedWriter.write("GET " + requestUrlPath + " HTTP/1.1\r\n"); bufferedWriter.write("Host: " + this.host + "\r\n"); bufferedWriter.write("\r\n"); bufferedWriter.flush(); BufferedInputStream streamReader = new BufferedInputStream(socket.getInputStream()); bufferedReader = new BufferedReader(new InputStreamReader(streamReader, "utf-8")); String line = null; while((line = bufferedReader.readLine())!= null){ System.out.println(line); } bufferedReader.close(); bufferedWriter.close(); socket.close(); } public void sendPost() throws IOException { String path = "/"; String data = URLEncoder.encode("name", "utf-8") + "=" + URLEncoder.encode("张三", "utf-8") + "&" + URLEncoder.encode("age", "utf-8") + "=" + URLEncoder.encode("32", "utf-8"); // String data = "name=zhigang_jia"; System.out.println(">>>>>>>>>>>>>>>>>>>>>"+data); OutputStreamWriter streamWriter = new OutputStreamWriter(socket.getOutputStream(), "utf-8"); bufferedWriter = new BufferedWriter(streamWriter); bufferedWriter.write("POST " + path + " HTTP/1.1\r\n"); bufferedWriter.write("Host: " + this.host + "\r\n"); bufferedWriter.write("Content-Length: " + data.length() + "\r\n"); bufferedWriter.write("Content-Type: application/x-www-form-urlencoded\r\n"); bufferedWriter.write("\r\n"); bufferedWriter.write(data); bufferedWriter.write("\r\n"); bufferedWriter.flush(); BufferedInputStream streamReader = new BufferedInputStream(socket.getInputStream()); bufferedReader = new BufferedReader(new InputStreamReader(streamReader, "utf-8")); String line = null; while((line = bufferedReader.readLine())!= null) { System.out.println(line); } bufferedReader.close(); bufferedWriter.close(); socket.close(); } public static void main(String[] args) throws Exception { /** * http协议测试 */ //SocketForHttpTest forHttpTest = new SocketForHttpTest("www.baidu.com", 80); /** * https协议测试 */ SocketForHttpTest forHttpTest = new SocketForHttpTest("www.baidu.com", 443); try { //forHttpTest.sendGet(); forHttpTest.sendPost(); } catch (IOException e) { e.printStackTrace(); } } } |
所以有时候离开apache的HTTP Client等第三方Jar,程序员也是一样活!
参考链接
Guava Cache用法
背景
缓存的主要作用是暂时在内存中保存业务系统的数据处理结果,并且等待下次访问使用。在日长开发有很多场合,有一些数据量不是很大,不会经常改动,并且访问非常频繁。但是由于受限于硬盘IO的性能或者远程网络等原因获取可能非常的费时。会导致我们的程序非常缓慢,这在某些业务上是不能忍的!而缓存正是解决这类问题的神器!