HTTP2简单教程

译自:http://qnimate.com/post-series/http2-complete-tutorial/

HTTP1.1是在1999年的时候被引入业界的。从当时到现在,WEB已经发生了相当大的变化。很多人开始用移动设备上网,有些移动环境下网络质量非常不稳定。另外,WEB页面的内容也相当的丰富了。这些都负面影响了WEB页面的加载时间。因此,需要一个新的协议来加速WEB的加载。

 

HTTP/2SPDY的区别:

SPDY是谷歌于2009年创造的协议,旨在WEB延迟以及增强安全性。

HTTP/2是SPDY的一个克隆。谷歌已经停止了继续开发SPDY,并且把工作转移到了HTTP/2上来。

 

HTTP/2的目标:

  1. 减少加载延时。
  2. 减少TCP连接数。
  3. 增强的网络安全。
  4. 保持和HTTP/1.1的兼容。

 

HTTP/2的功能:

  1. 多路复用Multiplexing:很多个异步的HTTP请求只使用一个TCP的连接。
  2. 服务端PUSH:对于一个请求可能有多个回复。
  3. 头部压缩:压缩头部(headers)。
  4. 请求分级:对同一个域名的请求可作优先级分级。
  5. 二进制协议:HTTP/2是一个二进制协议,而HTTP/1.1是一个文本协议。

 

HTTP/2解决了HTTP/1.1的哪些问题:

  1. HTTP管道(HTTP pipelining):一些HTTP1.1客户端使用了HTTP管道技术来减少TCP连接数。HTTP管道指的是多个HTTP请求被发往同一个TCP连接,而不等待前一个请求的回复。但是服务端发送回复时的顺序和收到的请求的顺序一样。因此,造成了HOL阻塞问题(head-of-line blocking)。HOL Blocking简单地说是一个回复的延迟会使得排在它后面的回复都会延迟。HTTP/2透过引用多路复用(Multiplexing)解决了这个问题。多个HTTP请求和多个HTTP回复都使用同一个TCP连续,而且HTTP回复的顺序不需要保持HTTP请求到达的顺序。
  2. 同一个域名的多个TCP连接:在使用HTTP/1.1时,需要对同一个域名起多个TCP连接来增加吞吐量。而使用HTTP/2时,对于每个域名,只充许有一个TCP连接(only one TCP connection per domain is allowed)。所有的请求和回复都通过唯一的一个TCP连接进行。
  3. TCP连接的关闭时间:使用HTTP1.1时,一个TCP连接在请求完成时立刻被关闭。但对于HTTP/2,TCP连接能存活相当成地一段时间。
  4. 头部压缩:HTTP/1.1是没有头部压缩的,但HTTP/2引入头部压缩进一步的减少了延时。

 

HTTP/2如何兼容HTT/1?

HTTP/2客户端在不确定对方服务器是否支持HTTP/2时,会用HTTP/1协议向服务器发送请求时带上Upgrade: HTTP/2.0这个头部。如果对方服务器支持HTTP/2,就会回复HTTP1.1 101,表示切换协议(Switching Protocols status)。而对于老旧的服务器则简单地返回HTTP/1.1的状态。

HTTP/2客户端可以记下哪些服务器支持HTTP/2,这样,下次就可以直接发起HTTP/2请求,而不必再经过一个询问和回复(Upgrade Round Trip).

 

HTTP/2中的多路复用是怎么回事?

HTTP/2使用多路复用,多个请求和回复全异步的通过同一个TCP连接收发。

HTTP/2是一个二进制的协议。每一个请求和回复都被分配了一个流标志(Stream ID),而且请求和回复被切成了很多帧(Frame),属于同一个请求或回复帧的Stream ID相同。因为有了这个Stream ID和Frame的机制,请求和回复可以在服务器和客户程序两端同时全异步的发送。因此,HTTP2没有了Head-of-line Blocking的问题。

每一个Stream还可以被赋于一个优先级,服务器可以分配更多的系统资源处理高优级的Stream。

HTTP/2还有一个传输层级别的流控。它协商了客户程序和服务器两端最多使用多少个Stream,服务器能处理的最大尺寸的Stream以及整个连接最多处理多少字节,以及数据被发送的速率。

 

HTTP/2中的服务器推送(Server Push)和承诺(Promise)

当服务器“感觉”到客户程序需要更多的资源时,就会给客户程序发送一个Promise Stream ,提醒客户程序需要什么资源。这些stream比一般的stream的优先级要高。

这项技术在加载网页时非常用户,因为网页的大量css和javascrpit可能通过这种方式发送。而HTTP/1的作法是把css和js都combine到一起。

服务器发送Promise Stream时提醒的资源遵守同源策略(same origin policy)

HTTP/2中的头部压缩:

HTTP/2的头部压缩和HTTP/1.1中早有的用gzip压缩Body不一样。使用HTTP/2时,在客户程序和服务器两端,都会保存一张表,记录了收到过的头部。它们在发送HTTP时不会重复发送一样的头部,当再次发送相同的头部时,只会发送该头部对应的表的索引号。

copyright ykyi.net

git小技巧,gitignore只忽略当前目录下的某文件等等

#. 编写git的gitignore文件时如何只忽略当前目录下的某文件呢

在文件名前加斜线 / 即可

 

# git pull如何从remote中拉下来所有分支

git pull –all

git branch -a 查看目前所有分支,包括tracking branch

再用 git branch –track branch_name start_point 开一个本地分支,并且设好该本地分支的upstream

 

# git 10小时免输git密码

git config --global credential.helper "cache --timeout=36000"

如果需要清空存下来的密码:

git config –global –unset credential.helper

Linux 如何知道有哪些硬件,该装哪个驱动

Linux 如何知道有哪些硬件,该装哪个驱动

公司明文不准安装虚拟机了,被查到就是死罪。于是我在自己的笔记本上安装了Debian Linux。

结果无线网卡识别不了~~

次奥~

怎么破呢?

这么破:

# lspci

该命令列出了所有PCI总线上的硬件设备,稍微看看就能看到自己的无线网卡了。

05:00.0 Network Controller: Atheros Communication Inc. AR9287 Wireless Network Adaptor (PCI-Express)(rev 01)

注意前面的坐标05:00.0,再到/sys下面找对应哪个驱动:

find /sys | grep drivers.*05:00.0

/sys/bus/pci/drivers/ath9k/0000:05:00.0

其实,还有更简单的办法:

#lspci -v 

就搞定了。

用lsmod 和 dmesg | grep ath9k 可以看到网卡对应模块的一些信息.

再编辑 /etc/network/interface

auto wlan0

iface wlan0 inet dhcp

wpa-ssid Tencent-FreeWiFi

wpa-psk xxxxxx

最后: iwconfig wlan up

搞定!!!

用boost的方式在预编译期检查编译器和平台以及其它

用boost的方式在预编译期检查编译器和平台以及其它

一般C/C++程序员都知道用一些编译器预编译宏来判断编译器的种类的操作系统。

一般有:
_WIN32 ,不管windows x86 还是 x86_64 都会有_WIN32,注意前面的下划线,如果没有下划线,MSDN的官方定义有这个酷酷的下划线.
__unix__ 是否 unix OS
__linux__ 是否是 linux
__apple__ 是否是平台

原来咧,BOOST库提供了一个相当丰富的库来做这件事:
这个BOOST库是 Predef http://www.boost.org/doc/libs/1_55_0/libs/predef/doc/html/index.html

#include <boost/predef.h>

看看官方文档的目录:
Table of Contents

Introduction
Using the predefs
Adding new predefs
Reference
BOOST_ARCH architecture macros
BOOST_COMP compiler macros
BOOST_LANG language standards macros
BOOST_LIB library macros
BOOST_OS operating system macros
Other macros
Version definition macros
Acknoledgements

包括处理器架构,编译器相关宏,语言相关宏,库相关宏,操作系统相关宏~~~

I like boost ~~~

copyright ykyi.net

用Range Adaptor,废弃replace_copy_if之类的函数

看看replace_copy_if的一个简单的例子:

std::vector<int> vec;

boost::replace_copy_if(rng, std::back_inserter(vec), pred, new_value);

对于这种写法,如果使用Range Adaptor的话,则可以写成:

std::vector<int> vec;

boost::push_back(vec, rng | boost::adaptors::replaced_if(pred, new_value));

第二种写法有什么好处呢,

1. 更高效率。因为std::back_inserter有额外的内存分配开销。

2. 更灵活。因为第二种写法可以应用更多的适配器,比如: boost::push_back(vec, rng | boost::adaptors::replaced_if(pred, new_value) | boost::adaptors::reversed);

3. 更安全。因为第二种写法没有用unbounded output iterator。

boost::io_service的poll_one和run_one有什么区别

boost::io_service的poll_one会立即返回(non-blocking),不管有没有已经被触发的处理函数(ready handler)。

而run_one有可能阻塞。如果还没有被触发的处理函数,run_one会阻塞到有一个handler被调用为止。

更多资料: http://think-async.com/

呃。这篇日志也太短了!!!

BOOST库真是个好东西啊~~好多写法很有现代脚本语言的taste。~~而且,那么丰富的库让生产效率大大提高了。

 

copyright ykyi.net

“右值引用”不是”右值”,而是”左值”

亮瞎你的狗眼了吧~这么拗口。 !来补一下C++基础吧~~

右值引用 rvalue reference

右值 rvalue

左值 lvalue

什么是左值 lvalue呢。

本质上,左值是一个可以拿到其地址的表达式。左值lvalue里的l,本质上是locatable的缩写,因为lvalue通常可以放置于表达式的左边,于是就有了左left值的说法。

In C++, an lvalue is an expression whose address can be taken, essentially it's a locator value.

class MetaData
{
public:
	MetaData (int size, const std::string& name)
		: _name( name )
		, _size( size )
	{}

	// copy constructor
	MetaData (const MetaData& other)
		: _name( other._name )
		, _size( other._size )
	{
		cout << "copy\n";
	}

	// move constructor
	MetaData (MetaData&& other)
		: _name( other._name )
		, _size( other._size )
	{
		cout << "move\n";
	}

	std::string getName () const { return _name; }
	int getSize () const { return _size; }
private:
	std::string _name;
	int _size;
};

class ArrayWrapper
{
public:
	// default constructor produces a moderately sized array
	ArrayWrapper ()
		: _p_vals( new int[ 64 ] )
		, _metadata( 64, "ArrayWrapper" )
	{}

	ArrayWrapper (int n)
		: _p_vals( new int[ n ] )
		, _metadata( n, "ArrayWrapper" )
	{}

	// move constructor
	ArrayWrapper (ArrayWrapper&& other)
		: _p_vals( other._p_vals  )
		, _metadata( move(other._metadata ))
	{
		other._p_vals = NULL;
	}

	// copy constructor
	ArrayWrapper (const ArrayWrapper& other)
		: _p_vals( new int[ other._metadata.getSize() ] )
		, _metadata( other._metadata )
	{
		for ( int i = 0; i < _metadata.getSize(); ++i )
		{
			_p_vals[ i ] = other._p_vals[ i ];
		}
	}
	~ArrayWrapper ()
	{
		delete [] _p_vals;
	}
private:
	int *_p_vals;
	MetaData _metadata;
};

ArrayWrapper get()
{
	ArrayWrapper aw;
	return aw;
}

int main()
{
	ArrayWrapper aw(get());
	return 0;
}

这段代码中的ArrayWrapper的移动构造函数能工作吗?

错!答案是不能~ 更详细,请参考:http://www.cprogramming.com/c++11/rvalue-references-and-move-semantics-in-c++11.html

概括地讲,本质原因是因为:右值引用不是右值,而是左值。所以ArrayWrapper里的移动构造函数不会调用ArrayWrapper内嵌的MetaData的移动构造函数,而是调用它MetaData的复制构造函数。为了得到期望的行为,需要使用std::move把lvalue转换成rvalue.

 

为什么指针可以做为迭代器使用呢

写了十几年代码了,今天才知道*为什么*指针可以做为迭代器使用。

这是因为:

首先,标准库里有这样一个template,它用来得到特定迭代器的相关特性:

    namespace std {
       template <class T>
       struct iterator_traits {
           typedef typename T::value_type            value_type;
           typedef typename T::difference_type       difference_type;
           typedef typename T::iterator_category     iterator_category;
           typedef typename T::pointer               pointer;
           typedef typename T::reference             reference;
       };
   } 

而对于指针,又有这样一个模板特例化,

   namespace std {
       template <class T>
       struct iterator_traits<T*> {
           typedef T                          value_type;
           typedef ptrdiff_t                  difference_type;
           typedef random_access_iterator_tag iterator_category;
           typedef T*                         pointer;
           typedef T&                         reference;
       };
   }

这个模板特例化的意思是:对于每一个指向类型T的指针,都被定义为一个可随机访问的迭代器。

更详细的,参见文章:介绍iterator_trait http://www.codeproject.com/Articles/36530/An-Introduction-to-Iterator-Traits

copyright ykyi.net

C++准标准库boost转向github,并发布模块化boost(modular Boost)

难道是中文社区首次报道吗?我用中文搜索引擎没有搜索到modular boost的文章。

昨天想在STL的map容器上增加一个特性,即把std::map中所有的元素按照加入的先后时间用链表链接起来,以保存元素之间的时序关系。

为了这个基于std::map和std::list的新的容器能够兼容STL的算法,于是想实现stl compatible iterator。一口气读了几十页资料,发现最酷最好的方法是用boost的迭代器模板实现。纠结了很久,最终决定在我的工程中引入boost。为了获得快速开发的好处,还是加入更多的开源库吧。毕竟,如果自己造一个轮子,阅读者仍然要花时间理解你发明的轮子,还不如用已经广泛使用的轮子。况且,计划要引入boost中asio的proactor I/O模型。所以,今天就开始引入boost吧。

于是,发现boost早就发起了modular boost项目并把boost host在github.com上,如下地址:

https://github.com/boostorg

在boost的官方网站上有模块化boost的FAQ:

https://svn.boost.org/trac/boost/wiki/ModCvtFAQ

还有它的简单使用教程:

https://svn.boost.org/trac/boost/wiki/TryModBoost#InstallingModularBoost

教程上说,如果把整个module boost git clone到本地,需要花费45分钟,如果下行网速是3Mbps,而且需要占用1.5G磁盘!

我晕~~

另外FAQ上说:没办法只检出一个子库,暂时不能自动地解决依赖性问题。

原文:

Is it possible to checkout only one library and its prerequisites?

Not automatically. Automatic dependency resolution is planned for the future, but not as a part of the conversion to Git and Modular Boost.

但是,github.com/boostorg上为各个子库分别建了一个仓库喔。通过这些创库检出boost子库,不行吗?

没有兴趣去验证我的想法。

最后,我还是用最原始的方法使用boost库吧~~就是下载整个库的压缩包到我的机器上再解开。

等boost解决了检出子库时的自动依赖性检查的问题,再迁移到git submodule管理boost库。

copyright ykyi.net

怎么知道gcc搜索头文件的路径

每个C/C++程序员都知道,对于#include directive,一般,以双引号引用的头文件是程序号自定义的头文件。而用尖括号引用的头文件是系统的头文件或其它库文件。

更详细的,编译器先在当前目录找用双引号""引用的头文件,如果找不到再到系统目录找这个头文件。而对于尖括号引用的头文件,编译器就只在系统文件中搜索了。

那么,到底是在哪些目录中搜索头文件呢?

可以用cpp查看,如下:

root@vicarious:/home/kamus/projs/horoscope/boost# cpp -v
Using built-in specs.
COLLECT_GCC=cpp
Target: x86_64-linux-gnu
Thread model: posix
gcc version 4.7.2 (Debian 4.7.2-5)
COLLECT_GCC_OPTIONS='-E' '-v' '-mtune=generic' '-march=x86-64'
 /usr/lib/gcc/x86_64-linux-gnu/4.7/cc1 -E -quiet -v -imultiarch x86_64-linux-gnu - -mtune=generic -march=x86-64
ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../../x86_64-linux-gnu/include"
#include "..." search starts here:
#include <...> search starts here:
 /usr/lib/gcc/x86_64-linux-gnu/4.7/include
 /usr/local/include
 /usr/lib/gcc/x86_64-linux-gnu/4.7/include-fixed
 /usr/include/x86_64-linux-gnu
 /usr/include
End of search list.

 

一般编写makefile时给gcc指定 -I选项,-I的参数代表的目录是搜索系统头文件时使用的目录。可以试试# cpp -I/tmp -v 

/tmp目录会被添加为搜索系统头文件时的第一个目录。

如果想要添加搜索双引号""引用的头文件使用的目录呢,用 -iquote 选项指定更多的目录。

copyright ykyi.net