From 7407eedbf604008d047ab99748640cef8b7885f0 Mon Sep 17 00:00:00 2001 From: xiemengjun Date: Wed, 16 Jan 2013 23:11:49 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=9B=BE=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 01.1.md | 10 ++++++---- 01.3.md | 2 ++ 01.4.md | 39 +++++++++++++++++++++++++++++++++++---- 02.2.md | 10 ++++++++++ 02.3.md | 2 ++ 02.4.md | 2 ++ 02.5.md | 4 ++++ 03.1.md | 13 +++++++++++++ 03.2.md | 2 ++ 03.3.md | 4 ++++ 04.1.md | 4 ++++ 04.3.md | 2 ++ 04.4.md | 2 ++ 04.5.md | 1 + 05.6.md | 2 ++ 06.1.md | 6 ++++++ 06.4.md | 8 ++++++++ 07.4.md | 2 ++ 08.1.md | 2 ++ 08.2.md | 6 ++++++ 08.3.md | 6 ++++++ 08.4.md | 2 ++ 09.1.md | 2 ++ 13.1.md | 8 +++++++- 14.1.md | 6 ++++++ 14.4.md | 9 ++++++++- 14.6.md | 6 ++++++ 27 files changed, 152 insertions(+), 10 deletions(-) diff --git a/01.1.md b/01.1.md index 8ab62171..b6a70cc0 100755 --- a/01.1.md +++ b/01.1.md @@ -40,6 +40,8 @@ Go使用[Mercurial][hg]进行版本管理,首先你必须安装了Mercurial, ![](images/1.1.mac.png?raw=true) +图1.1 源码安装之后执行Go命令的图 + 如果出现Go的Usage信息,那么说明Go已经安装成功了;如果出现该命令不存在,那么可以检查一下自己的PATH环境变中是否包含了Go的安装目录。 @@ -74,9 +76,7 @@ Linux系统用户可通过在Terminal中执行命令`uname -a`来查看系统信 访问[下载地址][downlink],32位系统下载go1.0.3.darwin-386.pkg,64位系统下载go1.0.3.darwin-amd64.pkg,双击下载文件,一路默认安装点击下一步,这个时候go已经安装到你的系统中,默认已经在PATH中增加了相应的`~/go/bin`,这个时候打开终端,输入`go` -看到如下图片说明已经安装成功 - -![](images/1.1.mac.png?raw=true) +看到类似上面源码安装成功的图片说明已经安装成功 如果出现go的Usage信息,那么说明go已经安装成功了;如果出现该命令不存在,那么可以检查一下自己的PATH环境变中是否包含了go的安装目录。 @@ -92,7 +92,9 @@ Linux系统用户可通过在Terminal中执行命令`uname -a`来查看系统信 然后执行`go` -看到类似上面mac安装成功的图片说明已经安装成功 +![](images/1.1.linux.png?raw=true) + +图1.2 Linux系统下安装成功之后执行go显示的信息 如果出现go的Usage信息,那么说明go已经安装成功了;如果出现该命令不存在,那么可以检查一下自己的PATH环境变中是否包含了go的安装目录。 diff --git a/01.3.md b/01.3.md index a647f86c..d515f441 100755 --- a/01.3.md +++ b/01.3.md @@ -6,6 +6,8 @@ ![](images/1.3.go.png?raw=true) +图1.3 Go命令显示详细的信息 + 这些命令对于我们平时编写的代码非常有用,接下来就让我们了解一些常用的命令。 ## go build diff --git a/01.4.md b/01.4.md index 7d036dc8..5a54bd43 100755 --- a/01.4.md +++ b/01.4.md @@ -8,6 +8,8 @@ ![](images/1.4.liteide.png?raw=true) +图1.4 LiteIDE主界面 + **LiteIDE主要特点:** * 支持主流操作系统 @@ -100,15 +102,23 @@ 这里将介绍Sublime Text 2(以下简称Sublime)+GoSublime+gocode+MarGo的组合,那么为什么选择这个组合呢? - 自动化提示代码,如下图所示 - ![](images/1.4.sublime1.png?raw=true) + + ![](images/1.4.sublime1.png?raw=true) + + 图1.5 sublime自动化提示界面 - 保存的时候自动格式化代码,让您编写的代码更加美观,符合Go的标准。 - 支持项目管理 - ![](images/1.4.sublime2.png?raw=true) + + ![](images/1.4.sublime2.png?raw=true) + + 图1.6 sublime项目管理界面 + - 支持语法高亮 - Sublime Text 2可免费使用,只是保存次数达到一定数量之后就会提示是否购买,点击取消继续用,和正式注册版本没有任何区别。 - 接下来就开始讲如何安装,下载[Sublime](http://www.sublimetext.com/) + +接下来就开始讲如何安装,下载[Sublime](http://www.sublimetext.com/) 根据自己相应的系统下载相应的版本,然后打开Sublime,对于不熟悉Sublime的同学可以先看一下这篇文章[Sublime Text 2 入门及技巧](http://lucifr.com/139225/sublime-text-2-tricks-and-tips/) @@ -120,6 +130,8 @@ ![](images/1.4.sublime3.png?raw=true) + 图1.7 sublime包管理 + 2. 接下来安装gocode和MarGo。 打开终端运行如下代码(需要git) @@ -134,6 +146,8 @@ ![](images/1.4.sublime4.png?raw=true) + 图1.8 sublime安装插件界面 + 这个时候输入GoSublime,按确定就开始安装了。同理应用于SidebarEnhancements和Go Build。 4. 验证是否安装成功,你可以打开Sublime,打开main.go,看看语法是不是高亮了,输入`import`是不是自动化提示了,`import "fmt"`之后,输入`fmt.`是不是自动化提示有函数了。 @@ -146,7 +160,9 @@ ## Vim Vim是从vi发展出来的一个文本编辑器, 代码补全、编译及错误跳转等方便编程的功能特别丰富,在程序员中被广泛使用。 - ![](images/1.4.vim.png?raw=true) +![](images/1.4.vim.png?raw=true) + +图1.9 VIM编辑器自动化提示Go界面 1. 配置vim高亮显示 @@ -190,6 +206,8 @@ Emacs传说中的神器,她不仅仅是一个编辑器,它是一个整合环 ![](images/1.4.emacs.png?raw=true) +图1.10 Emacs编辑Go主界面 + 1. 配置Emacs高亮显示 cp $GOROOT/misc/emacs/* ~/.emacs.d/ @@ -325,6 +343,8 @@ Eclipse也是非常常用的开发利器,以下介绍如何使用Eclipse来编 ![](images/1.4.eclipse1.png?raw=true) +图1.11 Eclipse编辑Go的主界面 + 1. 首先下载并安装好[Eclipse](http://www.eclipse.org/) 2. 下载[goeclipse](https://code.google.com/p/goclipse/)插件 @@ -355,13 +375,20 @@ Eclipse也是非常常用的开发利器,以下介绍如何使用Eclipse来编 ![](images/1.4.eclipse2.png?raw=true) + 图1.12 设置Go的一些基础信息 + + (2).配置Gocode(可选,代码补全),设置Gocode路径为之前生成的gocode.exe文件 ![](images/1.4.eclipse3.png?raw=true) + 图1.13 设置gocode信息 + (3).配置GDB(可选,做调试用),设置GDB路径为MingW安装目录下的gdb.exe文件 ![](images/1.4.eclipse4.png?raw=true) + + 图1.14 设置GDB信息 6. 测试是否成功 @@ -369,9 +396,13 @@ Eclipse也是非常常用的开发利器,以下介绍如何使用Eclipse来编 ![](images/1.4.eclipse5.png?raw=true) + 图1.15 新建项目编辑文件 + 调试如下(要在console中用输入命令来调试): ![](images/1.4.eclipse6.png?raw=true) + + 图1.16 调试Go程序 ## links diff --git a/02.2.md b/02.2.md index 8e7ed051..c30c4ed3 100755 --- a/02.2.md +++ b/02.2.md @@ -182,6 +182,8 @@ Go内置有一个`error`类型,专门用来处理错误信息,Go的`package` ![](images/2.2.basic.png?raw=true) +图2.1 Go数据格式的存储 + ## 一些技巧 ### 分组声明 @@ -277,6 +279,8 @@ Go之所以会那么简洁,是因为它有一些默认的行为: ![](images/2.2.array.png?raw=true) +图2.2 多维数组的映射关系 + ### slice @@ -313,6 +317,8 @@ Go之所以会那么简洁,是因为它有一些默认的行为: ![](images/2.2.slice.png?raw=true) +图2.3 slice和array的对应关系图 + slice有一些简便的操作 - `slice`的默认开始位置是0,`ar[:n]`等价于`ar[0:n]` @@ -352,6 +358,8 @@ slice有一些简便的操作 ![](images/2.2.slice2.png?raw=true) +图2.4 slice对应数组的信息 + 对于`slice`有几个有用的内置函数: - `len` 获取`slice`的长度 @@ -428,6 +436,8 @@ slice有一些简便的操作 ![](images/2.2.makenew.png?raw=true) +图2.5 make和new对应底层的内存分配 + 关于“零值”,所指并非是空值,而是一种“变量未填充前”的默认值,通常为0。 此处罗列 部分类型 的 “零值” diff --git a/02.3.md b/02.3.md index 2c7d08ba..4f4166a8 100755 --- a/02.3.md +++ b/02.3.md @@ -455,6 +455,8 @@ Go程序会自动调用`init()`和`main()`,所以你不需要在任何地方 ![](images/2.3.init.png?raw=true) +图2.6 main函数引入包初始化流程图 + ### import 我们在写Go代码的时候经常用到import这个命令用来导入包文件,而我们经常看到的方式参考如下: diff --git a/02.4.md b/02.4.md index 230df21e..717bb3f6 100755 --- a/02.4.md +++ b/02.4.md @@ -126,6 +126,8 @@ Go语言中,也和C或者其他语言一样,我们可以声明新的类型 ![](images/2.4.student_struct.png?raw=true) +图2.7 Student和Human的方法继承 + 我们看到Student访问属性age和name的时候,就像访问自己所有用的字段一样,对,匿名字段就是这样,能够实现字段的继承。是不是很酷啊?还有比这个更酷的呢,那就是student还能访问Human这个字段作为字段名。请看下面的代码,是不是更酷了。 mark.Human = Human{"Marcus", 55, 220} diff --git a/02.5.md b/02.5.md index f6039e58..df698343 100755 --- a/02.5.md +++ b/02.5.md @@ -30,6 +30,8 @@ ![](images/2.5.rect_func_without_receiver.png?raw=true) +图2.8 方法和struct的关系图 + 很显然,这样的实现并不优雅,并且从概念上来说"面积"是"形状"的一个属性,它是属于这个特定的形状的,就像长方形的长和宽一样。 基于上面的原因所以就有了`method`的概念,`method`是附属在一个给定的类型上的,他的语法和函数的声明语法几乎一样,只是在`func`后面增加了一个receiver(也就是method所依从的主体)。 @@ -95,6 +97,8 @@ method的语法如下: ![](images/2.5.shapes_func_with_receiver_cp.png?raw=true) +图2.9 不同struct的method不同 + 在上例,method area() 分别属于Rectangle和Circle, 于是他们的 Receiver 就变成了Rectangle 和 Circle, 或者说,这个area()方法 是由 Rectangle/Circle 发出的。 >值得说明的一点是,图示中method用虚线标出,意思是此处方法的Receiver是以值传递,而非引用传递,是的,Receiver还可以是指针, 两者的差别在于, 指针作为Receiver会对实例对象的内容发生操作,而普通类型作为Receiver仅仅是以副本作为操作对象,并不对原实例对象发生操作。后文对此会有详细论述。 diff --git a/03.1.md b/03.1.md index f02f584a..5cc52d6b 100755 --- a/03.1.md +++ b/03.1.md @@ -6,6 +6,8 @@ ![](images/3.1.web2.png?raw=true) +图3.1 用户访问一个Web站点的过程 + 一个Web服务器也被称为HTTP服务器,它通过HTTP协议与客户端通信。这个客户端通常指的是Web浏览器(其实手机端客户端内部也是浏览器实现的)。 Web服务器的工作原理可以简单地归纳为: @@ -34,6 +36,8 @@ URL(Uniform Resource Locator)是“统一资源定位符”的英文缩写,用 ![](images/3.1.dns_hierachy.png?raw=true) +图3.2 DNS工作原理 + 更详细的DNS解析的过程如下,这个过程有助于我们理解DNS的工作模式 1. 在浏览器中输入www.qq.com域名,操作系统会先检查自己本地的hosts文件是否有这个网址映射关系,如果有,就先调用这个IP地址映射,完成域名解析。 @@ -50,6 +54,8 @@ URL(Uniform Resource Locator)是“统一资源定位符”的英文缩写,用 ![](images/3.1.dns_inquery.png?raw=true) +图3.3 DNS解析的整个流程 + > 所谓 `递归查询过程` 就是 “查询的递交者” 更替, 而 `迭代查询过程` 则是 “查询的递交者”不变。 > > 举个例子来说,你想知道某个一起上法律课的女孩的电话,并且你偷偷拍了她的照片,回到寝室告诉一个很仗义的哥们儿,这个哥们儿二话没说,拍着胸脯告诉你,甭急,我替你查(此处完成了一次递归查询,即,问询者的角色更替)。然后他拿着照片问了学院大四学长,学长告诉他,这姑娘是xx系的;然后这哥们儿马不停蹄又问了xx系的办公室主任助理同学,助理同学说是xx系yy班的,然后很仗义的哥们儿去xx系yy班的班长那里取到了该女孩儿电话。(此处完成若干次迭代查询,即,问询者角色不变,但反复更替问询对象)最后,他把号码交到了你手里。完成整个查询过程。 @@ -83,8 +89,12 @@ HTTP协议是无状态的,同一个客户端的这次请求和上次请求是 ![](images/3.1.http.png?raw=true) +图3.4 fiddler抓取的GET信息 + ![](images/3.1.httpPOST.png?raw=true) +图3.5 fiddler抓取的POST信息 + **我们可以看到GET请求消息体为空,POST请求带有消息体**。 HTTP协议定义了很多与服务器交互的请求方法,最基本的有4种,分别是GET,POST,PUT,DELETE. 一个URL地址用于描述一个网络上的资源,而HTTP中的GET, POST, PUT, DELETE就对应着对这个资源的查,改,增,删4个操作。 我们最常见的就是GET和POST了。GET一般用于获取/查询资源信息,而POST一般用于更新资源信息. @@ -120,6 +130,7 @@ Response包中的第一行叫做状态行,由HTTP协议版本号, 状态码 ![](images/3.1.response.png?raw=true) +图3.6 访问一次网站的全部请求信息 ### HTTP协议是无状态的和Connection: keep-alive的区别 无状态是指协议对于事务处理没有记忆能力,服务器不知道客户端是什么状态。从另一方面讲,打开一个服务器上的网页和你之前打开这个服务器上的网页之间没有任何联系。 @@ -134,6 +145,8 @@ Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同 ![](images/3.1.web.png?raw=true) +图3.7 一次请求的request和response + 上面这张图我们可以了解到整个的通讯过程,同时细心的读者是否注意到了一点,一个URL请求但是左边栏里面为什么会有那么多的资源请求(这些都是静态文件,go对于静态文件有专门的处理方式)。 这个就是浏览器的一个功能,第一次请求url,服务器端返回的是html页面,然后浏览器开始渲染HTML:当解析到HTML DOM里面的图片连接,css脚本和js脚本的链接,浏览器就会自动发起一个请求静态资源的HTTP请求,获取相对应的静态资源,然后浏览器就会渲染出来,最终将所有资源整合、渲染,完整展现在我们面前的屏幕上。 diff --git a/03.2.md b/03.2.md index df1c6f3f..4cd310d5 100755 --- a/03.2.md +++ b/03.2.md @@ -48,6 +48,8 @@ ![](images/3.2.goweb.png?raw=true) +图3.8 用户访问Web之后服务器端打印的信息 + 我们看到上面的代码,要编写一个web服务器很简单,只要调用http包的两个函数就可以了。 >如果你以前是PHP程序员,那你也许就会问,我们的nginx、apache服务器不需要吗?Go就是不需要这些,因为他直接就监听tcp端口了,做了nginx做的事情,然后sayhelloName这个其实就是我们写的逻辑函数了,也就是php里面的控制层(controller)函数类似。 diff --git a/03.3.md b/03.3.md index 09bccbea..2adc5ce8 100755 --- a/03.3.md +++ b/03.3.md @@ -19,6 +19,8 @@ Handler:处理请求和生成返回信息的处理逻辑 ![](images/3.3.http.png?raw=true) +图3.9 http包执行流程 + 1. 创建Listen Socket, 监听指定的端口, 等待客户端请求到来。 2. Listen Socket接受客户端的请求, 得到Client Socket, 接下来通过Client Socket与客户端通信。 @@ -41,6 +43,8 @@ Handler:处理请求和生成返回信息的处理逻辑 ![](images/3.3.illustrator.png?raw=true) +图3.10 一个http连接处理流程 + 至此我们的三个问题已经全部得到了解答,你现在对于Go如何让Web跑起来的是否已经基本了解呢? diff --git a/04.1.md b/04.1.md index 1fb0945d..7c1cdf1d 100755 --- a/04.1.md +++ b/04.1.md @@ -74,6 +74,8 @@ login函数中我们根据`r.Method`来判断是显示登录界面还是处理 ![](images/4.1.login.png?raw=true) +图4.1 用户登录界面 + 我们输入用户名和密码之后发现在服务器端是不会打印出来任何输出的,为什么呢?默认情况下,Handler里面是不会自动解析form的,必须显式的调用`r.ParseForm()`后,你才能对这个表单数据进行操作。我们修改一下代码,在`fmt.Println("username:", r.Form["username"])`之前加一行`r.ParseForm()`,重新编译,再次测试输入递交,现在是不是在服务器端有输出你的输入的用户名和密码了。 `r.Form`里面包含了所有请求的参数,比如URL中query-string、POST的数据、PUT的数据,所有当你在URL的query-string字段和POST冲突时,会保存成一个slice,里面存储了多个值,Go官方文档中说在接下来的版本里面将会把POST、GET这些数据分离开来。 @@ -82,6 +84,8 @@ login函数中我们根据`r.Method`来判断是显示登录界面还是处理 ![](images/4.1.slice.png?raw=true) +图4.2 服务器端打印接受到的信息 + `request.Form`是一个url.Values类型,里面存储的是对应的类似`key=value`的信息,下面展示了可以对form数据进行的一些操作: v := url.Values{} diff --git a/04.3.md b/04.3.md index a39ee0ab..e0c350e2 100755 --- a/04.3.md +++ b/04.3.md @@ -23,6 +23,8 @@ ![](images/4.3.escape.png?raw=true) +图4.3 Javascript过滤之后的输出 + Go的html/template包默认帮你过滤了html标签,但是有时候你只想要输出这个``看起来正常的信息,该怎么处理?请使用text/template。请看下面的例子: import "text/template" diff --git a/04.4.md b/04.4.md index f96e32de..ea6801e0 100755 --- a/04.4.md +++ b/04.4.md @@ -46,6 +46,8 @@ ![](images/4.4.token.png?raw=true) +图4.4 增加token之后在客户端输出的源码信息 + 我们看到token已经有输出值,你可以不断的刷新,可以看到这个值在不断的变化。这样就保证了每次显示form表单的时候都是唯一的,用户递交的表单保持了唯一性。 我们的解决方案可以防止非恶意的攻击,并能使恶意用户暂时不知所措,然后,它却不能排除所有的欺骗性的动机,对此类情况还需要更复杂的工作。 diff --git a/04.5.md b/04.5.md index 4702d865..51b4c0f6 100755 --- a/04.5.md +++ b/04.5.md @@ -78,6 +78,7 @@ ![](images/4.5.upload2.png?raw=true) +图4.5 打印文件上传后服务器端接受的信息 ## 客户端上传文件 diff --git a/05.6.md b/05.6.md index ee1cb87c..81c6bff5 100755 --- a/05.6.md +++ b/05.6.md @@ -60,6 +60,8 @@ MongoDB是一个高性能,开源,无模式的文档型数据库,是一个 ![](images/5.6.mongodb.png?raw=true) +图5.1 MongoDB和Mysql的操作对比图 + 目前Go支持mongoDB最好的驱动就是[mgo](http://labix.org/mgo),这个驱动目前最有可能成为官方的pkg。 下面我将演示如果通过Go来操作mongoDB: diff --git a/06.1.md b/06.1.md index 534c3720..a2e07365 100755 --- a/06.1.md +++ b/06.1.md @@ -11,15 +11,21 @@ cookie,简而言之就是在本地计算机保存一些用户操作的历史 ![](images/6.1.cookie2.png?raw=true) +图6.1 cookie的原理图 + session,简而言之就是在服务器上保存用户操作的历史信息。服务器使用session id来标识session,session id由服务器负责产生,保证随机性与唯一性,相当于一个随机密钥,避免在握手或传输中暴露用户真实密码。但该方式下,仍然需要将发送请求的客户端与session进行对应,所以可以借助cookie机制来获取客户端的标识(即session id),也可以通过GET方式将id提交给服务器。 ![](images/6.1.session.png?raw=true) +图6.2 session的原理图 + ## cookie Cookie是由浏览器维持的,存储在客户端的一小段文本信息,伴随着用户请求和页面在Web服务器和浏览器之间传递。用户每次访问站点时,Web应用程序都可以读取cookie包含的信息。浏览器设置里面有cookie隐私数据选项,打开它,可以看到很多已访问网站的cookies,如下图所示: ![](images/6.1.cookie.png?raw=true) +图6.3 浏览器端保存的cookie信息 + cookie是有时间限制的,根据生命期不同分成两种:会话cookie和持久cookie; 如果不设置过期时间,则表示这个cookie生命周期为从创建到浏览器关闭止,只要关闭浏览器窗口,cookie就消失了。这种生命期为浏览会话期的cookie被称为会话cookie。会话cookie一般不保存在硬盘上而是保存在内存里。 diff --git a/06.4.md b/06.4.md index 8e95a83d..6216c7a6 100755 --- a/06.4.md +++ b/06.4.md @@ -27,19 +27,27 @@ count.gtpl的代码如下所示: ![](images/6.4.hijack.png?raw=true) +图6.4 浏览器端显示count数 + 随着刷新,数字将不断增长,当数字显示为6的时候,打开浏览器(以chrome为例)的cookie管理器,可以看到类似如下的信息: ![](images/6.4.cookie.png?raw=true) +图6.5 获取浏览器端保存的cookie + 下面这个步骤最为关键: 打开另一个浏览器(这里我打开了firefox浏览器),复制chrome地址栏里的地址到新打开的浏览器的地址栏中。然后打开firefox的cookie模拟插件,新建一个cookie,把按上图中cookie内容原样在firefox中重建一份: ![](images/6.4.setcookie.png?raw=true) +图6.6 模拟cookie + 回车后,你将看到如下内容: ![](images/6.4.hijacksuccess.png?raw=true) +图6.7 劫持session成功 + 可以看到虽然换了浏览器,但是我们却获得了sessionID,然后模拟了cookie存储的过程。这个例子是在同一台计算机上做的,不过即使换用两台来做,其结果仍然一样。此时如果交替点击两个浏览器里的链接你会发现它们其实操纵的是同一个计数器。不必惊讶,此处firefox盗用了chrome和goserver之间的维持会话的钥匙,即gosessionid,这是一种类型的“会话劫持”。在goserver看来,它从http请求中得到了一个gosessionid,由于HTTP协议的无状态性,它无法得知这个gosessionid是从chrome那里“劫持”来的,它依然会去查找对应的session,并执行相关计算。与此同时 chrome也无法得知自己保持的会话已经被“劫持”。 ## session劫持防范 ### cookieonly和token diff --git a/07.4.md b/07.4.md index 2110c77b..0d76cf73 100755 --- a/07.4.md +++ b/07.4.md @@ -6,6 +6,8 @@ ![](images/7.4.template.png?raw=true) +图7.1 模板机制图 + Web应用反馈给客户端的信息中的大部分内容是静态的,不变的,而另外少部分是根据用户的请求来动态生成的,例如要显示用户的访问记录列表。用户之间只有记录数据是不同的,而列表的样式则是固定的,此时采用模板可以复用很多静态代码。 ## Go模板使用 diff --git a/08.1.md b/08.1.md index c19918e1..68d77079 100755 --- a/08.1.md +++ b/08.1.md @@ -10,6 +10,8 @@ Socket起源于Unix,而Unix基本哲学之一就是“一切皆文件”,都 ![](images/8.1.socket.png?raw=true) +图8.1 七层网络协议图 + 使用TCP/IP协议的应用程序通常采用应用编程接口:UNIX BSD的套接字(socket)和UNIX System V的TLI(已经被淘汰),来实现网络进程之间的通信。就目前而言,几乎所有的应用程序都是采用socket,而现在又是网络时代,网络中进程通信是无处不在,这就是为什么说“一切皆Socket”。 ## Socket基础知识 diff --git a/08.2.md b/08.2.md index 778356ff..ba45b866 100755 --- a/08.2.md +++ b/08.2.md @@ -13,6 +13,8 @@ WebSocket URL的起始输入是ws://或是wss://(在SSL上)。下图展示 ![](images/8.2.websocket.png?raw=true) +图8.2 WebSocket原理图 + ## WebSocket原理 WebSocket的协议颇为简单,在第一次handshake通过以后,连接便建立成功,其后的通讯数据都是以”\x00″开头,以”\xFF”结尾。在客户端,这个是透明的,WebSocket组件会自动将原始数据“掐头去尾”。 @@ -20,6 +22,8 @@ WebSocket的协议颇为简单,在第一次handshake通过以后,连接便 ![](images/8.2.websocket2.png?raw=true) +图8.3 WebSocket的request和response信息 + 在请求中的"Sec-WebSocket-Key"是随机的,对于整天跟编码打交到的程序员,一眼就可以看出来:这个是一个经过base64编码后的数据。服务器端接收到这个请求之后需要把这个字符串连接上一个固定的字符串: 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 @@ -136,6 +140,8 @@ WebSocket分为客户端和服务端,接下来我们将实现一个简单的 ![](images/8.2.websocket3.png?raw=true) +图8.4 WebSocket服务器端接收到的信息 + 通过上面的例子我们看到客户端和服务器端实现WebSocket非常的方便,Go的源码net分支中已经实现了这个的协议,我们可以直接拿来用,目前随着HTML5的发展,我想未来WebSocket会是Web开发的一个重点,我们需要储备这方面的知识。 diff --git a/08.3.md b/08.3.md index 34d009dd..0e88ff6c 100755 --- a/08.3.md +++ b/08.3.md @@ -37,17 +37,23 @@ Web应用要满足REST最重要的原则是:客户端和服务器之间的交互 ![](images/8.3.rest2.png?raw=true) +图8.5 REST架构图 + 当REST架构的约束条件作为一个整体应用时,将生成一个可以扩展到大量客户端的应用程序。它还降低了客户端和服务器之间的交互延迟。统一界面简化了整个系统架构,改进了子系统之间交互的可见性。REST简化了客户端和服务器的实现,而且对于使用REST开发的应用程序更加容易扩展。 下图展示了REST的扩展性: ![](images/8.3.rest.png?raw=true) +图8.6 REST的扩展性 + ## RESTful的实现 Go没有为REST提供直接支持,但是因为RESTful是基于HTTP协议实现的,所以我们可以利用`net/http`包来自己实现,当然需要针对REST做一些改造,REST是根据不同的method来处理相应的资源,目前已经存在的很多自称是REST的应用,其实并没有真正的实现REST,我暂且把这些应用根据实现的method分成几个级别,请看下图: ![](images/8.3.rest3.png?raw=true) +图8.7 REST的level分级 + 上图展示了我们目前实现REST的三个level,我们在应用开发的时候也不一定全部按照RESTful的规则全部实现他的方式,因为有些时候完全按照RESTful的方式未必是可行的,RESTful服务充分利用每一个HTTP方法,包括`DELETE`和`PUT`。可有时,HTTP客户端只能发出`GET`和`POST`请求: - HTML标准只能通过链接和表单支持`GET`和`POST`。在没有Ajax支持的网页浏览器中不能发出`PUT`或`DELETE`命令 diff --git a/08.4.md b/08.4.md index 608bb283..d2da4fb2 100755 --- a/08.4.md +++ b/08.4.md @@ -9,6 +9,8 @@ RPC(Remote Procedure Call Protocol)——远程过程调用协议,是一 ![](images/8.4.rpc.png?raw=true) +图8.8 RPC工作流程图 + 运行时,一次客户机对服务器的RPC调用,其内部操作大致有如下十步: - 1.调用客户端句柄;执行传送参数 diff --git a/09.1.md b/09.1.md index 771d4029..6f7c95af 100755 --- a/09.1.md +++ b/09.1.md @@ -12,6 +12,8 @@ CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也 ![](images/9.1.csrf.png?raw=true) +图9.1 CSRF的攻击过程 + 从上图可以看出,要完成一次CSRF攻击,受害者必须依次完成两个步骤 : - 1.登录受信任网站A,并在本地生成Cookie 。 diff --git a/13.1.md b/13.1.md index 1135cdfc..0abd0432 100755 --- a/13.1.md +++ b/13.1.md @@ -5,8 +5,12 @@ ![](images/13.1.gopath.png?raw=true) +图13.1 环境变量GOPATH设置 + ![](images/13.1.gopath2.png?raw=true) +图13.2 工作目录在$gopath/src下 + ## 应用程序流程图 博客系统是基于模型-视图-控制器这一设计模式的。MVC是一种将应用程序的逻辑层和表现层进行分离的结构方式。在实践中,由于表现层从Go中分离了出来,所以它允许你的网页中只包含很少的脚本。 @@ -14,10 +18,12 @@ - 视图 (View) 是展示给用户的信息的结构及样式。一个视图通常是一个网页,但是在Go中,一个视图也可以是一个页面片段,如页头、页尾。它还可以是一个 RSS 页面,或其它类型的“页面”,Go实现的template包已经很好的实现了View层中的部分功能。 - 控制器 (Controller) 是模型、视图以及其他任何处理HTTP请求所必须的资源之间的中介,并生成网页。 -下图显示了项目设计中博客系统的数据流是如何贯穿整个系统: +下图显示了项目设计中框架的数据流是如何贯穿整个系统: ![](images/13.1.flow.png?raw=true) +图13.3 框架的数据流 + 1. main.go作为应用入口,初始化一些运行博客所需要的基本资源,配置信息,监听端口。 2. 路由功能检查HTTP请求,根据URL以及method来确定谁(控制层)来处理请求的转发资源。 3. 如果缓存文件存在,它将绕过通常的流程执行,被直接发送给浏览器。 diff --git a/14.1.md b/14.1.md index 195f5709..b76a0070 100755 --- a/14.1.md +++ b/14.1.md @@ -36,11 +36,15 @@ Bootstrap是Twitter推出的一个开源的用于前端开发的工具包。Boot ![](images/14.1.bootstrap.png?raw=true) +图14.1 bootstrap站点 + 接下来我们利用bootstrap集成到beego框架里面来,快速的建立一个漂亮的站点。 1. 首先把下载的bootstrap目录放到我们的项目目录,取名为static,如下截图所示 ![](images/14.1.bootstrap2.png?raw=true) + + 图14.2 项目中静态文件目录结构 2. 因为beego默认设置了StaticDir的值,所以如果你的静态文件目录是static的话就无须再增加了: @@ -61,6 +65,8 @@ Bootstrap是Twitter推出的一个开源的用于前端开发的工具包。Boot ![](images/14.1.bootstrap3.png?raw=true) +图14.3 构建的基于bootstrap的站点界面 + 这些模板和格式bootstrap官方都有提供,这边就不在重复贴代码,大家可以上bootstrap官方网站学习如何编写这样的模板。 diff --git a/14.4.md b/14.4.md index 13a68c1b..14706e83 100755 --- a/14.4.md +++ b/14.4.md @@ -131,13 +131,20 @@ oauth和oauth2是目前比较流行的两种认证方式,还好第三方有一 ![](images/14.4.github.png?raw=true) +图14.4 显示带有登录按钮的首页 + 然后点击链接出现如下界面: ![](images/14.4.github2.png?raw=true) +图14.5 点击登录按钮后显示github的授权页 + 然后点击Authorize app就出现如下界面: -![](images/14.4.github3.png?raw=true) +![](images/14.4.github3.png?raw=true) + +图14.6 授权登录之后显示的获取到的github信息页 + ## 自定义认证 自定义的认证一般都是和session结合的验证的,如下代码来源于一个基于beego的开源博客: diff --git a/14.6.md b/14.6.md index 8c76f35c..6a705621 100755 --- a/14.6.md +++ b/14.6.md @@ -55,10 +55,14 @@ Go语言有一个非常棒的设计就是标准库里面带有代码的性能监 然后你就可以在浏览器中打开如下URL就看到如下界面: ![](images/14.6.pprof.png?raw=true) +图14.7 系统当前goroutine、heap、thread信息 + 点击goroutine我们可以看到很多详细的信息: ![](images/14.6.pprof2.png?raw=true) +图14.8 显示当前goroutine的详细信息 + 我们还可以通过命令行获取更多详细的信息 go tool pprof http://localhost:8080/debug/pprof/profile @@ -93,6 +97,8 @@ Go语言有一个非常棒的设计就是标准库里面带有代码的性能监 ![](images/14.6.pprof3.png?raw=true) +图14.9 展示的执行流程信息 + ## links * [目录]() * 上一节: [多语言支持](<14.5.md>)