分类 默认分类 下的文章

终端中文乱码排查思路:

echo $LANG

locale

locale -a | grep zh

LANG=zh_CN.utf8

# 把gbk编码的转码成utf-8
cat info.log | iconv -f gbk -t utf-8

# 终端,系统,文件 三个编码一致才不会乱码

vim中的乱码排查思路:

关注这四个options (termencoding, encoding, fileencoding, fileencodings),
可以通过:set来查看,或者在~/.vimrc中查看。

显示/隐藏行号

:set nu
:set nonu

显示/设置文件编码

#vim保存文件时用的编码
:set fileencoding
:set fileencoding=gbk

#vim显示文件内容的编码
:set encoding
:set encoding=gbk

#vim在打开文件时会根据fileencodings选项来识别文件编码,
#可以同时设置多个编码,vim会根据顺序来猜测所打开文件的编码。
#因此,如果在vim出现中文乱码,可以检查下此选项。
:set fileencodings
:set fileencodings=ucs-bom,utf-8,default,latin1,gbk

显示所有set命令

:set
:set all

显示/隐藏不可见字符

:set list
:set nolist

以上命令在命令模式执行,如果希望持久生效,可以把上述命令(去掉前面的:)存到~/.vimrc文件里(如果没有,可以创建该文件),则不必每次都重新设置,每次启动vim时会读取此文件配置。

说到校验IP地址或者Domain的格式是否正确,很多人可能是想到用正则表达式来校验。
一种比较好的做法是使用现成的库来完成此功能,尤其是比较流行的类库,经过了大量的验证,比自己实现要安全可靠。
本文推荐使用Google的Guava库里的工具类来校验。

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>19.0</version>
</dependency>

校验IP地址格式的有效性

//com.google.common.net.InetAddresses#isInetAddress(String ipString)

InetAddresses.isInetAddress("1.1.1.1");//true
InetAddresses.isInetAddress("1.1.1");//false
InetAddresses.isInetAddress("127.0.0.1");//true
InetAddresses.isInetAddress("fc00::");//true
InetAddresses.isInetAddress("localhost");//false
InetAddresses.isInetAddress("256.256.1212.1");//false
InetAddresses.isInetAddress("256.256.257.1");//false
InetAddresses.isInetAddress("300.300.300.300");//false

//校验ip是ipv4地址还是ipv6地址
InetAddresses.forString("fc00::") instanceof Inet6Address;//true
InetAddresses.forString("127.0.0.1") instanceof Inet4Address;//true

校验Domain的合法性

//com.google.common.net.InternetDomainName#isValid(String name)

InternetDomainName.isValid("www.baidu.com"); //true
InternetDomainName.isValid("127.0.0.1"); //false
InternetDomainName.isValid("a.b.c.com"); //true
InternetDomainName.isValid("http://a.b.c.com"); //false
InternetDomainName.isValid("http://a.b.c.com/"); //false

Url的host部分合法性的校验

//com.google.common.net.HostSpecifier#isValid(String specifier)

//检查是否可以作为url的host部分,可能是domain或者ip
HostSpecifier.isValid("a.b.c.com");//true
HostSpecifier.isValid("a.b.c.com:90");//false
HostSpecifier.isValid("http://a.b.c.com");//false
HostSpecifier.isValid("127.0.0.1");//true
HostSpecifier.isValid("127.0.0.1:80");//false
HostSpecifier.isValid("111::");//true

今年的世界杯很早就关注了,可以说是眼巴巴的盼着世界杯的日子到来的。可是,回忆上一届世界杯,脑子里却没有任何印象。后来查了下,才发现是在巴西举行的,并且德国拿到了冠军。分析了下,之所以没印象是因为,那年夫人怀孕了,无暇顾及;另外,可能是时差的原因,大部分都是在凌晨举行的。再往前一届,2010年南非世界杯,那时候还在学校,关注的比较多,印象比较深刻的是南非的呜呜祖拉,当年看球赛被那个声音“余音绕梁三日不绝”。

索性,今年看了,就把一些想法记下,以飨未来的自己。

关于中国队

今年世界杯没有中国队,在可见的未来,应该也不会有。很多人在网上痛心疾首,我泱泱大国,竟然选不出11人来踢进世界杯,这个问题也一直困扰在自己的心头,看的多了,想的多了,发现这是个复杂的问题,不能单纯的说是由于某一方面导致,于是,归纳出几点:

  • 体质原因。这个从亚洲球队在世界杯上的表现可以看出,欧洲,美洲的球员确实比我们亚洲的球队要强壮,速度也比我们快。本届世界杯,沙特,韩国,日本,在面对其他洲的球队时,身体上的差异是显而易见的。
  • 足球氛围。这点在南美球员身上很突出,打小自带天赋,而且周围足球文化氛围强烈。
  • 经济发展水平。穷怕了的人知道踢球可以赚钱,但是却丢掉了对足球的喜爱。一如中国的互联网捞到钱却没有沉淀下技术,大部分技术都是从国外传过来的。只有经济水平达到一定水平并且持续很长时间,才能消除这种被贫穷打下的烙印,从而发自真心的喜欢并达到顶尖的水平。

坦白来讲,很多人把中国足球不行归结到球员本身,甚至是体制问题,都是有失公允的,足球运动本身是一个很复杂的东西,水平的高低,取决于球员身体素质,个人技术,教练的技战术,以及天时地利等种种因素。单纯归咎于某一方面都是不公平的。

从这届世界杯的小组赛来看,即使中国达到亚洲顶级水准,也难在世界杯上能有很大作为。所以,在中国足球这件事情上,还是应该持一种重在参与的心态,保持体育竞技精神,像韩国那样拼搏,像日本那样扬长避短,赛出自己的风采即可。但这条路会很长,需要几代人的努力,毕竟还是要考虑“历史的进程”。

关于梅西与C罗

梅西与C罗两人统治了世界足坛近乎10年,并且都有大量的粉丝,双方的粉丝经常在网上互喷。而他们两人在足坛的地位也都因对方的存在显得更加伟大。两人基本拿到了足球界所有的荣誉,除了在世界杯上。因此,这届世界杯作为可能是他们两人最后一届世界杯,大家也是非常期待。正如大家所看到的,葡萄牙和阿根廷小组都被淘汰,双双提前回家。有些遗憾。也许,这也预示者,属于他们的时代已经要过去了,下一个足球时代即将到来吧。至于我们,又何尝不是呢?

C罗的时代,留给我们这些粉丝的,大概就是他对胜利的渴望,以及为了这份渴望对自己的严格要求与奋斗吧。

这也是体育竞技给我们带来的生活的启迪。

最近在使用Java的HttpsURLConnection来访问https网站时,抛出了一个异常:
javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure

代码大致如下:

URL url = new URL("https://www.google.com/");
HttpsURLConnection connection = (HttpsURLConnection)url.openConnection();
connection.setHostnameVerifier((t, v) -> true);
BufferedInputStream inputStream = new BufferedInputStream(connection.getInputStream());
int respInt = inputStream.read();
while (respInt != -1) {
respInt = inputStream.read();
}
inputStream.close();

网上查了下,发现很多情况都可能导致抛出这个异常,一种可行的排查方法是,在执行Java程序时,加
-Djavax.net.debug=all 选项打开SSL连接时的debug开关,这样就会把建立连接的信息打印到控制台。

这样执行程序时可以在控制台打印类似的消息(部分摘取):

keyStore is :
keyStore type is : jks
keyStore provider is :
init keystore
init keymanager of type SunX509
trustStore is: /Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/security/cacerts
trustStore type is : jks
trustStore provider is :
init truststore
adding as trusted cert:
......
*** ClientHello, TLSv1.2
RandomCookie:  GMT: 1527255937 bytes = { 72, 31, 116, 116, 76, 130, 134, 23, 59, 194, 6, 185, 160, 110, 14, 131, 74, 175, 192, 56, 83, 130, 176, 102, 53, 50, 126, 139 }
Session ID:  {}
Cipher Suites: [TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, ...]
Compression Methods:  { 0 }
Extension elliptic_curves, curve names: {secp256r1, sect163k1, sect163r2, secp192r1, secp224r1, sect233k1, sect233r1, sect283k1, sect283r1, secp384r1, sect409k1, ...}
Extension ec_point_formats, formats: [uncompressed]
Extension signature_algorithms, signature_algorithms: SHA512withECDSA, SHA512withRSA, SHA384withECDSA, SHA384withRSA, SHA256withECDSA, SHA256withRSA, SHA224withECDSA, SHA224withRSA, SHA1withECDSA, SHA1withRSA, SHA1withDSA, MD5withRSA
***
......
[Raw read]: length = 5
0000: 15 03 03 00 02                                     .....
[Raw read]: length = 2
0000: 02 28                                              .(
main, READ: TLSv1.2 Alert, length = 2
main, RECV TLSv1.2 ALERT:  fatal, handshake_failure
main, called closeSocket()
main, handling exception: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure

也就是在clientHello阶段,服务器端返回了握手失败。而这一步握手失败有很多原因:

  • 客户端和服务器端的cipher suites不兼容
  • 客户端和服务器端使用的ssl协议版本不一致。(sslv2, sslv3, tlsv1.0, tlsv1.1, tlsv1.2)
  • 服务器的根证书不在客户端的可信目录中

然而,在尝试了以上各种原因后,依然不行,正要崩溃时,在网上看到了关于JDK bug导致此错误的案例。

简单来说就是,JDK1.8的特定版本,如果自定义HttpsURLConnection的HostnameVerifier(类似上面的代码中的connection.setHostnameVerifier((t, v) -> true);)会导致握手的ClientHello阶段,client不发送SNI extension。(注意,可能你的代码中没有自定义,但是可能依赖的jar包中有自定义的操作)

正常情况下在ClientHello阶段,会发送:

Extension server_name, server_name: [type=host_name (0), value=www.google.com]

而在上面的出错的信息中,没有这个extension。

解决办法,升级到JDK1.8的8u152版本或者以上,或者降低到u66版本以下。

另外,如果不升级JDK,可以参见(https://stackoverflow.com/questions/41692736/all-trusting-hostnameverifier-causes-ssl-errors-with-httpurlconnection)来暂时规避掉此BUG.

另外,不是所有的网站都会出现这个问题,理论上来说与SNI相关的网站出问题的可能比较大。关于SNI(Server Name Indication),可以参考下:(https://en.wikipedia.org/wiki/Server_Name_Indication)

参考文档:

1)介绍了这类问题的参考思路:

https://stackoverflow.com/questions/6353849/received-fatal-alert-handshake-failure-through-sslhandshakeexception?utm_medium=organic&;utm_source=google_rich_qa&utm_campaign=google_rich_qa

2)分析并解决了我这种情况的问题:

https://stackoverflow.com/questions/41692736/all-trusting-hostnameverifier-causes-ssl-errors-with-httpurlconnection

3)JDK bug详情:

https://bugs.java.com/view_bug.do?bug_id=JDK-8144566