Golang-C1-2

作业点评

完善http.go和c10k.go

要求:

  1. 编译通过
  2. 编译成windows,linux和mac三个平台的文件,有条件的可以运行观察结果。二进制不用提交。

分析课堂上的代码,说明原因

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
func main() {
	var x int
	var y int
	x = 1
	y = 1
	swap(&x, &y)
	fmt.Println("x=", x, "y=", y)
}
func swap(p *int, q *int) {
	var t int
	t = *p
	*p =*q
	*q = t
}

参考分析

https://github.com/51reboot/golang-01-homework/blob/master/lesson1/CHECK.md

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main

import "fmt"

func main() {
	var x int
	x = 1 //1赋给变量x
	var y int
	y = 2        //2赋给变量y
	swap(&x, &y) //x和y的内存地址作为参数传给swap函数
	fmt.Println(x, y)
}

func swap(p *int, q *int) {
	//指针变量p里面存的是变量x的内存地址,指针变量q存的是y的内存地址
	var t = *p
	//p里面存的是x的内存地址*p是取指针指向的内容*p也就是变量x里面存的值11赋给t
	*p = *q
	//q里面存的y的内存地址*q是变量y里面存的值2,把2赋值给x变量里面的值x里面的值变成了2
	*q = t
	//变量t里面存的值是11赋给了y里面的值y的值变成了1
	//最终结果,x是2y是1
}

https://github.com/cnjllin/golang-01-homework/blob/master/lesson1/wujianjian/pointer.png

pointer.png

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
mulinux@WIN-MANAGER:~$ python
Python 2.7.6 (default, Oct 26 2016, 20:30:19)
[GCC 4.8.4] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> a = 1
>>> b = 1
>>> id(a)
16445784
>>> id(b)
16445784
>>> a="hello"
>>> for i in range(10):
...     print i
...
0
1
2
3
4
5
6
7
8
9

学员博客

www.cnblogs.com/yinzhengjie/p/6482675.html

指针解析

  • &符号的意思是对变量取地址,如:变量a的地址是&a
  • *符号的意思是对指针取值,如:*&a,就是a变量所在地址的值,当然也就是a的值了
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import "fmt"

func main() {
        var x int
        x = 1 //1赋给变量x
        var y int
        y = 2        //2赋给变量y
        swap(&x, &y) //x和y的内存地址作为参数传给swap函数
        fmt.Println(x, y)
}

func swap(p *int, q *int) {
        //指针变量p里面存的是变量x的内存地址,指针变量q存的是y的内存地址
        var t = *p
        //p里面存的是x的内存地址*p是取指针指向的内容*p也就是变量x里面存的值11赋给t
        *p = *q
        //q里面存的y的内存地址*q是变量y里面存的值2,把2赋值给x变量里面的值
x里面的值变成了2
        *q = t
        //变量t里面存的值是11赋给了y里面的值y的值变成了1
        //最终结果,x是2y是1
}
 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
package main

import "fmt"

func main() {
	var x int
	x = 1
	var y int
	y = 2

	fmt.Println(x, y)
	//x和y的初始化指针
	fmt.Println(&x, &y)
	swap(&x, &y)
	fmt.Println(x, y)
	//x和y的值对调后
	fmt.Println(&x, &y)

	swap_a(&x, &y)
	//x和y的指针对调后
	fmt.Println(&x, &y)
	fmt.Println(x, y)
}

//

//x和y的值对调
func swap(p *int, q *int) {
	var t = *p
	*p = *q
	*q = t
}

//x和y的指针对调,值不变
func swap_a(p *int, q *int) {
	var t = p
	p = q
	q = t
}

package

go程序的结构

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package main

import (
	"fmt"
	"os"
)

func main() {
	var s, sep string
	for i := 1; i < len(os.Args); i++ {
		s += sep + os.Args[i]
		sep = " "
	}
	fmt.Println(s)
}

go通过package组织

  • package关键字
  • 放在程序的第一行
  • 两种package,一种是库package,一种是二进制package
  • 二进制package使用main来表示,库package的名字跟go文件所在的目录名一样

二进制package

  • package main作为文件的第一行
  • 有且只有一个main函数,如echo.go所表示

引入package

  • 通过关键字import来引入其他package
  • 多个package可以用括号包含起来
  • 引入但没有使用的package会引起编译错误

thirdlib/main.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
package main

import (
	"fmt"
	"github.com/icexin/golib"
)

func main() {
	fmt.Println(golib.Add(1, 2))
}

查看包函数方式

  • https://godoc.org/fmt
  • https://godoc.org/github.com/go-redis/redis

go doc

1
2
3
go install golang.org/x/tools/cmd/godoc
godoc -http=:9000
访问:ip:port/pkg/github.com/mulinux/golib/

faq

  • go run echo.go #提示找不到X变量

go run 跟go build或go install区别

  • 在myecho目录下go install出错,提示no install location for directory GOPATH
  • go install 成功,但执行myecho出错

变量声明和指针

main函数

  • 二进制程序的入口
  • main函数结束了整个程序就结束了
  • 整个main package只能有一个

代码风格

  • 所有的代码只有一种,gofmt的风格
  • gofmt不是任何人的风格,但所有人都喜欢gofmt的风格
  • 使用gofmt -w xx.go来格式化代码

变量:godoc_ip:port/pkg/os/#pkg-variables

练习:编写cat命令

cat辅助函数

1
2
3
4
5
6
7
8
func printFile(name string) {
	buf, err := ioutil.ReadFile(name)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(string(buf))
}

变量声明

1
2
3
4
5
6
7
8
var x int
var y string = "hello"
var x, y int
var (
	x int
	y int
	z string
)

零值初始化

  • 每个类型都有默认的零值
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package main

import "fmt"

func main() {
	var (
		x int
		y float32
		z string
		p *int
	)
	fmt.Printf("%v\n", x)
	fmt.Printf("%v\n", y)
	fmt.Printf("%v\n", z)
	fmt.Printf("%v\n", p)
}
  • 初始化为零值,安全!

短变量

1
2
3
i := 0	//int
s := "hello"	//string
i, j := 0, 1 	//批量初始化

指针

  • *T即为类型T的指针
  • &t即为去变量t的地址
  • *p即为取指针变量所指向的内容
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package main

import "fmt"

func main() {
	var x int
	var p *int
	
	fmt.Println("&x=", &x)
	p = &x
	fmt.Println("p=", p)
	fmt.Println("*p=", *p)
}

命令行参数

args.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package main

import (
	"fmt"
	"os"
)

func main() {
	var s, sep string
	for i := 1; i < len(os.Args); i++ {
		s += sep + os.Args[i]
		sep = " "
	}
	fmt.Println(s)
}

echo.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

import (
	"fmt"
	"os"
)

var i int

func main() {
	var s, sep string
	//var i int
	for i := 1; i < len(os.Args); i++ {
		s += sep + os.Args[i]
		sep = " "
	}
	if true {
		i := 4
		fmt.Println(i)
	}
	fmt.Println(s)
}

echo2.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
package main

import (
	"flag"
	"fmt"
	"strings"
)

var sep = flag.String("s", " ", "separator")
var newline = flag.Bool("n", false, "append newline")

func main() {
	flag.Parse()
	fmt.Print(strings.Join(flag.Args(), *sep))
	if *newline {
		fmt.Println()
	}
}

程序分享

spider.go(初版)

 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 main

import (
	"container/list"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"strings"

	"github.com/PuerkitoBio/goquery"
)

func isurlok(uri *url.URL) bool {
	if strings.HasPrefix(uri.Hostname(), "www.zhihu.com") {
		return true
	}
	return false
}

func fetch(target string) ([]string, string, error) {
	uri, err := url.Parse(target)
	if err != nil {
		return nil, "", err
	}

	if !isurlok(uri) {
		return nil, "", errors.New("skip " + target)
	}

	resp, err := http.Get(target)
	if err != nil {
		return nil, "", err
	}
	defer resp.Body.Close()
	doc, err := goquery.NewDocumentFromResponse(resp)
	if err != nil {
		return nil, "", err
	}
	var urls []string
	doc.Find("a").Each(func(i int, s *goquery.Selection) {
		link, ok := s.Attr("href")
		if !ok {
			return
		}
		if len(link) > 0 && link[0] == '/' {
			u := uri
			u.Path = link
			u.RawQuery = ""
			u.Fragment = ""
			urls = append(urls, u.String())
		} else {
			urls = append(urls, link)
		}
	})
	body, err := doc.Html()
	if err != nil {
		return urls, "", nil
	}
	return urls, body, nil
}

func main() {
	root := os.Args[1]
	visited := make(map[string]bool)
	l := list.New()
	l.PushBack(root)
	for l.Len() != 0 {
		front := l.Front()
		l.Remove(front)
		url := front.Value.(string)

		if visited[url] {
			continue
		}
		visited[url] = true

		urls, body, err := fetch(url)
		if err != nil {
			//log.Print(err)
			continue
		}
		fmt.Printf("%s %0.2fk\n", url, float32(len(body))/1024.0)

		for _, url := range urls {
			l.PushBack(url)
		}
	}
}

spider.go(改版)

  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
package main
 
import (
	"github.com/PuerkitoBio/goquery"  //解析html
	"os"
	"container/list"
	"github.com/gogather/com/log"
	"fmt"
	"strings"
	"net/url"
	"net/http"
	"errors"
)

func isurlok(uri *url.URL,prefix string) bool {
	if strings.HasPrefix(uri.Hostname(),prefix) {
		return true
	}
	return false
}

func fetch(target,prefix string) ([]string, string, error) {
	uri, err := url.Parse(target)
	if err != nil {
		return nil, "", err
	}
 
	if !isurlok(uri,prefix) {
		return nil, "", errors.New("skip " + target)
	}
 
	resp, err := http.Get(target)
	if err != nil {
		return nil, "", err
	}
	defer resp.Body.Close()
	doc, err := goquery.NewDocumentFromResponse(resp)
	if err != nil {
		return nil, "", err
	}
	var urls []string
	doc.Find("a").Each(func(i int, s *goquery.Selection) {
		link, ok := s.Attr("href")
		if !ok {
			return
		}
		if len(link) > 0 && link[0] == '/' {
			u := uri
			u.Path = link
			u.RawQuery = ""
			u.Fragment = ""
			urls = append(urls, u.String())
		} else {
			urls = append(urls, link)
		}
	})
	body, err := doc.Html()
	if err != nil {
		return urls, "", nil
	}
	return urls, body, nil
}

func main()  {
	//广度优先搜索
	//爬到一个网页后,爬该页面的所有的子链接。优先搜索本站,再搜索外站。大部分使用广度优先。
	webroot:=os.Args[1]
	prefix:=os.Args[2]
	visited :=make(map[string]bool)
	//创建一个双向链表
	lst:=list.New()
	lst.PushBack(webroot)
	for lst.Len()!=0{
		front := lst.Front()
		lst.Remove(front)
		url:=front.Value.(string)
 
		if visited[url]{
			continue
		}
		visited[url] =true
 
		urls,body,err:=fetch(url,prefix)
 
		if err!=nil{
			log.Println(err)
			continue
		}
 
		fmt.Printf("%s %0.2fk\n", url, float32(len(body))/1024.0)
 
		for _, url := range urls {
			lst.PushBack(url)
		}
 
	}
}


编译好的可执行程序:https://download.csdn.net/download/mypc2010/10407523
用法:spider.exe http://www.jd.com  jd.com
拓展阅读:https://www.zhihu.com/question/23011311

gotty

https://github.com/yudai/gotty

1
2
3
tar zxf gotty_linux_amd64.tar.gz
./gotty -w bash
./gotty -p 8090 -w bash

https://github.com/ghantoos/lshell

1
2
lshell
./gotty -p 8090 -w lshell
1
2
go get github.com/icexin/markdown
markdown -server -addr=:9091

gossh

说明

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 百行代码实现的ssh客户端和服务端
## 编译
`go get github.com/cnjllin/golang-01-homework/lesson2/binggan/gossh`

**提示**:需要翻墙

## 使用
服务端:
`gossh -addr=:8080 -s`

客户端:
`gossh -addr=:8080`

或者:
`nc 127.0.0.1 8080`

**注意**:nc作为客户端top,vi之类的命令不能用

## 致有志于hack的同学

- 增加只读功能
- 增加替换shell程序的功能

gossh.go

 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
package main

import (
	"flag"
	"io"
	"log"
	"net"
	"os"
	"os/exec"

	"golang.org/x/crypto/ssh/terminal"

	"github.com/kr/pty"
)

var (
	addr     = flag.String("addr", ":8080", "address")
	isserver = flag.Bool("s", false, "run as server")
)

func handle(conn net.Conn) {
	defer conn.Close()
	log.Printf("[login] %s", conn.RemoteAddr())
	cmd := exec.Command("bash")
	tty, err := pty.Start(cmd)
	if err != nil {
		log.Print(err)
		return
	}
	go io.Copy(tty, conn)
	go io.Copy(conn, tty)
	cmd.Wait()
	log.Printf("[logout] %s", conn.RemoteAddr())
}

func server() {
	l, err := net.Listen("tcp", *addr)
	if err != nil {
		log.Fatal(err)
	}
	for {
		conn, err := l.Accept()
		if err != nil {
			log.Fatal(err)
		}
		go handle(conn)
	}
}

func client() {
	conn, err := net.Dial("tcp", *addr)
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()

	oldState, err := terminal.MakeRaw(0)
	if err != nil {
		panic(err)
	}
	defer terminal.Restore(0, oldState)
	go io.Copy(conn, os.Stdin)
	io.Copy(os.Stdout, conn)
}

func main() {
	flag.Parse()

	if *isserver {
		server()
		return
	}
	client()
}

使用

1
2
sockget go get -v
gossh -addr=127.0.0.1:8080

homework

完成mycat

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package main

import (
        "fmt"
        "io/ioutil"
)

func printFile(name string) {
        buf, err := ioutil.ReadFile(name)
        if err != nil {
                fmt.Println(err)
                return
        }
        fmt.Println(string(buf))
}

func main() {
        // 补全缺失的代码完成cat命令
}

要求:

  • 程序放到个人的mycat目录下

提示:

  • 使用os.Args获取命令行参数,具体代码参照myecho

判断如下程序的输出,并说明原因

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main

import "fmt"

var x = 200

func localFunc() {
	fmt.Println(x)
}

func main() {
	x := 1

	localFunc()
	fmt.Println(x)
	if true {
		x := 100
		fmt.Println(x)
	}

	localFunc()
	fmt.Println(x)
}