Skip to content

Commit

Permalink
Upload examples for articles 35~37.
Browse files Browse the repository at this point in the history
  • Loading branch information
hyper0x committed Nov 25, 2018
1 parent cc4a02c commit 2a1bb94
Show file tree
Hide file tree
Showing 16 changed files with 794 additions and 0 deletions.
22 changes: 22 additions & 0 deletions src/puzzlers/article35/q0/demo90.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package main

import (
"fmt"
"syscall"
)

func main() {
fd1, err := syscall.Socket(
syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
if err != nil {
fmt.Printf("socket error: %v\n", err)
return
}
defer syscall.Close(fd1)
fmt.Printf("The file descriptor of socket:%d\n", fd1)

// 省略若干代码。
// 如果真要完全使用syscall包中的程序实体建立网络连接的话,
// 过程太过繁琐而且完全没有必要。
// 所以,我在这里就不做展示了。
}
86 changes: 86 additions & 0 deletions src/puzzlers/article35/q1/demo91.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package main

import (
"bufio"
"crypto/tls"
"fmt"
"io"
"net"
"runtime"
)

func main() {
network := "tcp"
host := "google.cn"
reqStrTpl := `HEAD / HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Host: %s
User-Agent: Dialer/%s
`

// 示例1。
network1 := network + "4"
address1 := host + ":80"
fmt.Printf("Dial %q with network %q ...\n", address1, network1)
conn1, err := net.Dial(network1, address1)
if err != nil {
fmt.Printf("dial error: %v\n", err)
return
}
defer conn1.Close()

reqStr1 := fmt.Sprintf(reqStrTpl, host, runtime.Version())
fmt.Printf("The request:\n%s\n", reqStr1)
_, err = io.WriteString(conn1, reqStr1)
if err != nil {
fmt.Printf("write error: %v\n", err)
return
}
fmt.Println()

reader1 := bufio.NewReader(conn1)
line1, err := reader1.ReadString('\n')
if err != nil {
fmt.Printf("read error: %v\n", err)
return
}
fmt.Printf("The first line of response:\n%s\n", line1)
fmt.Println()

// 示例2。
tlsConf := &tls.Config{
InsecureSkipVerify: true,
MinVersion: tls.VersionTLS10,
}
network2 := network
address2 := host + ":443"
fmt.Printf("Dial %q with network %q ...\n", address2, network2)
conn2, err := tls.Dial(network2, address2, tlsConf)
if err != nil {
fmt.Printf("dial error: %v\n", err)
return
}
defer conn2.Close()

reqStr2 := fmt.Sprintf(reqStrTpl, host, runtime.Version())
fmt.Printf("The request:\n%s\n", reqStr2)
_, err = io.WriteString(conn2, reqStr2)
if err != nil {
fmt.Printf("write error: %v\n", err)
return
}

reader2 := bufio.NewReader(conn2)
line2, err := reader2.ReadString('\n')
if err != nil {
fmt.Printf("read error: %v\n", err)
return
}
fmt.Printf("The first line of response:\n%s\n", line2)
fmt.Println()
}
56 changes: 56 additions & 0 deletions src/puzzlers/article35/q2/demo92.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package main

import (
"fmt"
"net"
"time"
)

type dailArgs struct {
network string
address string
timeout time.Duration
}

func main() {
dialArgsList := []dailArgs{
{
"tcp",
"google.cn:80",
time.Millisecond * 500,
},
{
"tcp",
"google.com:80",
time.Second * 2,
},
{
// 如果在这种情况下发生的错误是:
// "connect: operation timed out",
// 那么代表着什么呢?
//
// 简单来说,此错误表示底层的socket在连接网络服务的时候先超时了。
// 这时抛出的其实是'syscall.ETIMEDOUT'常量代表的错误值。
"tcp",
"google.com:80",
time.Minute * 4,
},
}
for _, args := range dialArgsList {
fmt.Printf("Dial %q with network %q and timeout %s ...\n",
args.address, args.network, args.timeout)
ts1 := time.Now()
conn, err := net.DialTimeout(args.network, args.address, args.timeout)
ts2 := time.Now()
fmt.Printf("Elapsed time: %s\n", time.Duration(ts2.Sub(ts1)))
if err != nil {
fmt.Printf("dial error: %v\n", err)
fmt.Println()
continue
}
defer conn.Close()
fmt.Printf("The local address: %s\n", conn.LocalAddr())
fmt.Printf("The remote address: %s\n", conn.RemoteAddr())
fmt.Println()
}
}
36 changes: 36 additions & 0 deletions src/puzzlers/article36/q0/demo93.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package main

import (
"fmt"
"net/http"
)

func main() {
host := "google.cn"

// 示例1。
url1 := "http://" + host
fmt.Printf("Send request to %q with method GET ...\n", url1)
resp1, err := http.Get(url1)
if err != nil {
fmt.Printf("request sending error: %v\n", err)
return
}
defer resp1.Body.Close()
line1 := resp1.Proto + " " + resp1.Status
fmt.Printf("The first line of response:\n%s\n", line1)
fmt.Println()

// 示例2。
url2 := "https://golang." + host
fmt.Printf("Send request to %q with method GET ...\n", url2)
var httpClient1 http.Client
resp2, err := httpClient1.Get(url2)
if err != nil {
fmt.Printf("request sending error: %v\n", err)
return
}
defer resp2.Body.Close()
line2 := resp2.Proto + " " + resp2.Status
fmt.Printf("The first line of response:\n%s\n", line2)
}
78 changes: 78 additions & 0 deletions src/puzzlers/article36/q1/demo94.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package main

import (
"fmt"
"net"
"net/http"
"strings"
"sync"
"time"
)

// domains 包含了我们将要访问的一些网络域名。
// 你可以随意地对它们进行增、删、改,
// 不过这会影响到后面的输出内容。
var domains = []string{
"google.com",
"google.com.hk",
"google.cn",
"golang.org",
"golang.google.cn",
}

func main() {
// 你可以改变myTransport中的各个字段的值,
// 并观察后面的输出会有什么不同。
myTransport := &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 15 * time.Second,
KeepAlive: 15 * time.Second,
DualStack: true,
}).DialContext,
MaxConnsPerHost: 2,
MaxIdleConns: 10,
MaxIdleConnsPerHost: 2,
IdleConnTimeout: 30 * time.Second,
ResponseHeaderTimeout: 0,
ExpectContinueTimeout: 1 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
}
// 你可以改变myClient中的各个字段的值,
// 并观察后面的输出会有什么不同。
myClient := http.Client{
Transport: myTransport,
Timeout: 20 * time.Second,
}

var wg sync.WaitGroup
wg.Add(len(domains))
for _, domain := range domains {
go func(domain string) {
var logBuf strings.Builder
var diff time.Duration
defer func() {
logBuf.WriteString(
fmt.Sprintf("(elapsed time: %s)\n", diff))
fmt.Println(logBuf.String())
wg.Done()
}()
url := "https://" + domain
logBuf.WriteString(
fmt.Sprintf("Send request to %q with method GET ...\n", url))
t1 := time.Now()
resp, err := myClient.Get(url)
diff = time.Now().Sub(t1)
if err != nil {
logBuf.WriteString(
fmt.Sprintf("request sending error: %v\n", err))
return
}
defer resp.Body.Close()
line2 := resp.Proto + " " + resp.Status
logBuf.WriteString(
fmt.Sprintf("The first line of response:\n%s\n", line2))
}(domain)
}
wg.Wait()
}
63 changes: 63 additions & 0 deletions src/puzzlers/article36/q2/demo95.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package main

import (
"fmt"
"log"
"net/http"
"sync"
)

func main() {
var wg sync.WaitGroup
wg.Add(2)

// 示例1。
go startServer1(&wg)

// 示例2。
go startServer2(&wg)

wg.Wait()
}

func startServer1(wg *sync.WaitGroup) {
defer wg.Done()
var httpServer1 http.Server
httpServer1.Addr = "127.0.0.1:8080"
// 由于我们没有定制handler,所以这个网络服务对任何请求都只会响应404。
if err := httpServer1.ListenAndServe(); err != nil {
if err == http.ErrServerClosed {
log.Println("HTTP server 1 closed.")
} else {
log.Printf("HTTP server 1 error: %v\n", err)
}
}
}

func startServer2(wg *sync.WaitGroup) {
defer wg.Done()
mux1 := http.NewServeMux()
mux1.HandleFunc("/hi", func(w http.ResponseWriter, req *http.Request) {
if req.URL.Path != "/hi" {
http.NotFound(w, req)
return
}
name := req.FormValue("name")
if name == "" {
fmt.Fprint(w, "Welcome!")
} else {
fmt.Fprintf(w, "Welcome, %s!", name)
}
})
httpServer2 := http.Server{
Addr: "127.0.0.1:8081",
Handler: mux1,
}
if err := httpServer2.ListenAndServe(); err != nil {
if err == http.ErrServerClosed {
log.Println("HTTP server 2 closed.")
} else {
log.Printf("HTTP server 2 error: %v\n", err)
}
}
}
52 changes: 52 additions & 0 deletions src/puzzlers/article37/common/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package common

import (
"errors"
"fmt"
"os"
"path/filepath"
"time"
)

// OpFunc 代表包含高负载操作的函数。
type OpFunc func() error

// Execute 用于执行可以产生高负载的操作。
func Execute(op OpFunc, times int) (err error) {
if op == nil {
return errors.New("nil operation function")
}
if times <= 0 {
return fmt.Errorf("invalid times: %d", times)
}
var t1 time.Time
defer func() {
diff := time.Now().Sub(t1)
fmt.Printf("(elapsed time: %s)\n", diff)
if p := recover(); p != nil {
err = fmt.Errorf("fatal error: %v", p)
}
}()
t1 = time.Now()
for i := 0; i < times; i++ {
if err = op(); err != nil {
return
}
time.Sleep(time.Microsecond)
}
return
}

// CreateFile 用于在当前目录下创建一个指定名称的文件。
// 若同名文件已存在,则清空并复用。
func CreateFile(dir, name string) (*os.File, error) {
if dir == "" {
var err error
dir, err = os.Getwd()
if err != nil {
return nil, err
}
}
path := filepath.Join(dir, name)
return os.Create(path)
}
Loading

0 comments on commit 2a1bb94

Please sign in to comment.