目录
server代码
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
// See page 222.
// Clock is a TCP server that periodically writes the time.
package main
import (
"flag"
"io"
"log"
"net"
"time"
)
var port = flag.String("port", "8000", "tcp port")
func handleConn(c net.Conn) {
defer c.Close()
for {
_, err := io.WriteString(c, time.Now().Format("15:04:05\n"))
if err != nil {
return // e.g., client disconnected
}
time.Sleep(1 * time.Second)
}
}
func main() {
flag.Parse()
listener, err := net.Listen("tcp", "localhost:"+(*port))
if err != nil {
log.Fatal(err)
}
//!+
for {
conn, err := listener.Accept()
if err != nil {
log.Print(err) // e.g., connection aborted
continue
}
go handleConn(conn) // handle connections concurrently
}
//!-
}
client代码
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
// See page 221.
//!+
// Netcat1 is a read-only TCP client.
package main
import (
"bufio"
"fmt"
"io"
"log"
"net"
"os"
"strings"
"time"
)
func main() {
aeras := []string{}
hosts := []string{}
for _, arg := range os.Args[1:] {
i := strings.Index(arg, "=")
aeras = append(aeras, arg[0:i])
hosts = append(hosts, arg[i+1:])
}
results := make([]string, len(aeras))
for i := 0; i < len(results); i++ {
i := i
go func() {
conn, err := net.Dial("tcp", hosts[i])
defer conn.Close()
if err != nil {
log.Fatal(err)
}
s := bufio.NewScanner(conn)
for s.Scan() {
results[i] = s.Text()
}
}()
}
time.Sleep(500 * time.Millisecond)
for {
for i := 0; i < len(results); i++ {
t := aeras[i] + "=" + hosts[i] + ":" + results[i]
fmt.Println(t)
}
time.Sleep(1 * time.Second)
fmt.Println()
}
}
func mustCopy(dst io.Writer, src io.Reader) {
if _, err := io.Copy(dst, src); err != nil {
log.Fatal(err)
}
}
//!-
注意服务端必须使用reverb1,也就是echo函数不能放在goroutine中
package main
import (
"io"
"log"
"net"
"os"
)
func main() {
conn, err := net.Dial("tcp", "localhost:8000")
if err != nil {
log.Fatal(err)
}
done := make(chan struct{})
go func() {
_, err := io.Copy(os.Stdout, conn)
if err != nil {
log.Print(err)
}
log.Println("done")
done <- struct{}{}
}()
MustCopy(conn, os.Stdin)
tcpc := conn.(*net.TCPConn)
err = tcpc.CloseWrite()
if err != nil {
log.Print(err)
}
<-done
tcpc.Close()
}
func MustCopy(out io.Writer, in io.Reader) {
if _, err := io.Copy(out, in); err != nil {
log.Fatal(err)
}
log.Print("终止读入")
}
package main
import (
"bufio"
"fmt"
"log"
"net"
"strings"
"sync"
"time"
)
func echo(c net.Conn, text string, delay time.Duration) {
fmt.Fprintln(c, "\t", strings.ToUpper(text))
time.Sleep(delay)
fmt.Fprintln(c, "\t", text)
time.Sleep(delay)
fmt.Fprintln(c, "\t", strings.ToLower(text))
}
func handleConn(c net.Conn) {
defer c.Close()
sc := bufio.NewScanner(c)
var wg sync.WaitGroup
for sc.Scan() {
wg.Add(1)
go func(str string) {
defer wg.Done()
echo(c, str, 2*time.Second)
}(sc.Text())
}
wg.Wait()
}
func main() {
listener, err := net.Listen("tcp", "localhost:8000")
if err != nil {
log.Fatal(err)
}
for {
conn, err := listener.Accept()
if err != nil {
log.Print(err)
continue
}
go handleConn(conn)
}
}
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
// See page 58.
//!+
// Surface computes an SVG rendering of a 3-D surface function.
package main
import (
"fmt"
"math"
"sync"
"time"
)
const (
width, height = 600, 320 // canvas size in pixels
cells = 100 // number of grid cells
xyrange = 30.0 // axis ranges (-xyrange..+xyrange)
xyscale = width / 2 / xyrange // pixels per x or y unit
zscale = height * 0.4 // pixels per z unit
angle = math.Pi / 6 // angle of x, y axes (=30°)
)
var sin30, cos30 = math.Sin(angle), math.Cos(angle) // sin(30°), cos(30°)
func main() {
doPaint(1)
doPaint(4)
doPaint(8)
doPaint(16)
doPaint(32)
doPaint(64)
doPaint(128)
}
func doPaint(routinues int) {
work := make(chan int, cells)
var wg sync.WaitGroup
for r := routinues; r > 0; r-- {
wg.Add(1)
go func() {
for i := range work {
for j := 0; j < cells; j++ {
corner(i+1, j)
corner(i, j)
corner(i, j+1)
corner(i+1, j+1)
}
}
wg.Done()
}()
}
start := time.Now()
for i := 0; i < cells; i++ {
work <- i
}
close(work)
wg.Wait()
fmt.Printf("routines is %d, timecostis %s\n ", routinues, time.Since(start).String())
}
func corner(i, j int) (float64, float64) {
// Find point (x,y) at corner of cell (i,j).
x := xyrange * (float64(i)/cells - 0.5)
y := xyrange * (float64(j)/cells - 0.5)
// Compute surface height z.
z := f(x, y)
// Project (x,y,z) isometrically onto 2-D SVG canvas (sx,sy).
sx := width/2 + (x-y)*cos30*xyscale
sy := height/2 + (x+y)*sin30*xyscale - z*zscale
return sx, sy
}
func f(x, y float64) float64 {
r := math.Hypot(x, y) // distance from (0,0)
return math.Sin(r) / r
}
//!-
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
// See page 139.
// Findlinks3 crawls the web, starting with the URLs on the command line.
package main
import (
"fmt"
"log"
"os"
"gopl.io/ch5/links"
)
var tokens = make(chan struct{}, 20)
type linkT struct {
url string
depth int
}
//!+crawl
func crawl(link linkT) []linkT {
fmt.Println(link.url, " ...... ", link.depth)
tokens <- struct{}{}
list, err := links.Extract(link.url)
ls := []linkT{}
<-tokens
if err != nil {
log.Print(err)
}
for _, l := range list {
ls = append(ls, linkT{l, link.depth + 1})
}
return ls
}
//!-crawl
//!+main
func main() {
workList := make(chan []linkT)
seen := map[string]bool{}
go func() {
links := []linkT{}
for _, l := range os.Args[1:] {
links = append(links, linkT{l, 0})
}
workList <- links
}()
for n := 1; n > 0; n-- {
list := <-workList
for _, link := range list {
if !seen[link.url] && link.depth <= 3 {
seen[link.url] = true
n++
go func(link linkT) {
workList <- crawl(link)
}(link)
}
}
}
}
//!-main
package main
import (
"bufio"
"fmt"
"log"
"net"
"strings"
"sync"
"time"
)
func echo(c net.Conn, text string, delay time.Duration) {
fmt.Fprintln(c, "\t", strings.ToUpper(text))
time.Sleep(delay)
fmt.Fprintln(c, "\t", text)
time.Sleep(delay)
fmt.Fprintln(c, "\t", strings.ToLower(text))
}
func handleConn(c net.Conn) {
defer c.Close()
sc := bufio.NewScanner(c)
var wg sync.WaitGroup
tick := make(chan struct{})
go func() {
for {
select {
case <-time.After(10 * time.Second):
log.Printf("客户%s端超过10s未发送消息,准备关闭连接\n", c.RemoteAddr())
wg.Wait()
c.Close()
log.Printf("服务端主动断开连接 客户地址%s\n", c.RemoteAddr())
return
case _, ok := <-tick:
if !ok { // 退出主循环使得goroutine得以被释放
log.Printf("客户端主动断开连接 客户地址%s\n", c.RemoteAddr())
return
}
}
}
}()
for sc.Scan() {
wg.Add(1)
tick <- struct{}{}
go func(str string) {
defer wg.Done()
echo(c, str, 2*time.Second)
}(sc.Text())
}
close(tick) // 注意关闭通道防止goroutine泄漏
wg.Wait()
}
func main() {
listener, err := net.Listen("tcp", "localhost:8000")
if err != nil {
log.Fatal(err)
}
for {
conn, err := listener.Accept()
if err != nil {
log.Print(err)
continue
}
go handleConn(conn)
}
}
package main
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"sync"
"time"
)
func walkDir(dir string, n *sync.WaitGroup, fileSize chan<- int64) {
defer n.Done()
for _, entry := range decent(dir) {
if entry.IsDir() {
subdir := filepath.Join(dir, entry.Name())
n.Add(1)
go walkDir(subdir, n, fileSize)
} else {
fileSize <- entry.Size()
}
}
}
func decent(dir string) []os.FileInfo {
entrys, err := ioutil.ReadDir(dir)
if err != nil {
fmt.Fprintf(os.Stderr, "du:%v\n", err)
return nil
}
return entrys
}
func main() {
root := []string{"/usr"}
for {
fileSize := make(chan int64)
var n sync.WaitGroup
go func() {
for _, r := range root {
n.Add(1)
go walkDir(r, &n, fileSize)
}
n.Wait()
close(fileSize)
}()
var nfiles, nbytes int64
start := time.Now()
fmt.Printf("\n开始计算%s目录的大小\n", root[0])
for size := range fileSize {
nfiles++
nbytes += size
}
printDiskUsage(nfiles, nbytes)
fmt.Printf("共耗时 %s\n", time.Since(start).String())
time.Sleep(5 * time.Second)
}
}
func printDiskUsage(nfiles, nbytes int64) {
fmt.Printf("%d files %.1fGB\n", nfiles, float64(nbytes)/1e9)
}
findlinks.go
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
// See page 243.
// Crawl3 crawls web links starting with the command-line arguments.
//
// This version uses bounded parallelism.
// For simplicity, it does not address the termination problem.
//
package main
import (
"context"
"fmt"
"log"
"os"
)
func crawl(url string, ctx context.Context) []string {
fmt.Println(url)
list, err := Extract(url, ctx)
if err != nil {
log.Print(err)
}
return list
}
//!+
func main() {
worklist := make(chan []string) // lists of URLs, may have duplicates
unseenLinks := make(chan string) // de-duplicated URLs
// Add command-line arguments to worklist.
go func() { worklist <- os.Args[1:] }()
context, cancle := context.WithCancel(context.Background())
go func() {
os.Stdin.Read(make([]byte, 1))
cancle()
}()
// Create 20 crawler goroutines to fetch each unseen link.
for i := 0; i < 20; i++ {
go func() {
for link := range unseenLinks {
foundLinks := crawl(link, context)
go func() { worklist <- foundLinks }()
}
}()
}
// The main goroutine de-duplicates worklist items
// and sends the unseen ones to the crawlers.
seen := make(map[string]bool)
for n := 1; n > 0; n-- {
list := <-worklist
for _, link := range list {
if !seen[link] {
seen[link] = true
n++
unseenLinks <- link
}
}
}
}
//!-
links.go
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
// See page 138.
//!+Extract
// Package links provides a link-extraction function.
package main
import (
"context"
"fmt"
"net/http"
"golang.org/x/net/html"
)
// Extract makes an HTTP GET request to the specified URL, parses
// the response as HTML, and returns the links in the HTML document.
func Extract(url string, ctx context.Context) ([]string, error) {
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return nil, err
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
resp.Body.Close()
return nil, fmt.Errorf("getting %s: %s", url, resp.Status)
}
doc, err := html.Parse(resp.Body)
resp.Body.Close()
if err != nil {
return nil, fmt.Errorf("parsing %s as HTML: %v", url, err)
}
var links []string
visitNode := func(n *html.Node) {
if n.Type == html.ElementNode && n.Data == "a" {
for _, a := range n.Attr {
if a.Key != "href" {
continue
}
link, err := resp.Request.URL.Parse(a.Val)
if err != nil {
continue // ignore bad URLs
}
links = append(links, link.String())
}
}
}
forEachNode(doc, visitNode, nil)
return links, nil
}
//!-Extract
// Copied from gopl.io/ch5/outline2.
func forEachNode(n *html.Node, pre, post func(n *html.Node)) {
if pre != nil {
pre(n)
}
for c := n.FirstChild; c != nil; c = c.NextSibling {
forEachNode(c, pre, post)
}
if post != nil {
post(n)
}
}
package main
import (
"context"
"fmt"
"io/ioutil"
"net/http"
"time"
)
func mirroredQuery() {
responses := make(chan string)
getRes := func(req *http.Request) {
resp, err := http.DefaultClient.Do(req)
if err != nil {
fmt.Println(err.Error())
} else {
text, _ := ioutil.ReadAll(resp.Body)
select {
case responses <- string(text):
default:
}
resp.Body.Close()
}
fmt.Printf("go %s finished\n", req.URL.String())
}
ctx, cancle := context.WithCancel(context.Background())
resq1, _ := http.NewRequestWithContext(ctx, "GET", "http://www.golang.com", nil)
resq2, _ := http.NewRequestWithContext(ctx, "GET", "http://www.baidu.com", nil)
resq3, _ := http.NewRequestWithContext(ctx, "GET", "http://www.sina.com", nil)
go getRes(resq1)
go getRes(resq2)
go getRes(resq3)
body := <-responses
cancle()
fmt.Println(body)
time.Sleep(1 * time.Second)
}
func main() {
mirroredQuery()
}
package main
import (
"bufio"
"fmt"
"log"
"net"
)
type client struct {
name string
ch chan<- string
}
var (
massages = make(chan string)
entering = make(chan client)
leaving = make(chan client)
)
func main() {
listener, err := net.Listen("tcp", "localhost:8000")
if err != nil {
log.Fatal(err)
}
go broadcast()
for {
conn, err := listener.Accept()
if err != nil {
log.Print(err)
continue
}
go handleConn(conn)
}
}
func broadcast() {
clients := make(map[client]bool)
for {
select {
case msg := <-massages:
for cli := range clients {
cli.ch <- msg
}
case cli := <-entering:
{
clients[cli] = true
online := []string{}
for c := range clients {
online = append(online, c.name)
}
s := fmt.Sprintf("当前在线成员%v\n", online)
cli.ch <- s
}
case cli := <-leaving:
{
delete(clients, cli)
close(cli.ch)
}
}
}
}
func handleConn(conn net.Conn) {
ch := make(chan string)
go clientWriter(conn, ch)
who := conn.RemoteAddr().String()
ch <- "you are " + who
massages <- who + " has arrived"
entering <- client{who, ch}
input := bufio.NewScanner(conn)
for input.Scan() {
massages <- input.Text()
}
leaving <- client{who, ch}
massages <- who + " has left"
conn.Close()
}
func clientWriter(conn net.Conn, ch <-chan string) {
for msg := range ch {
fmt.Fprintln(conn, msg)
}
}
package main
import (
"bufio"
"fmt"
"log"
"net"
"time"
)
type client struct {
name string
ch chan<- string
}
var (
massages = make(chan string)
entering = make(chan client)
leaving = make(chan client)
)
func main() {
listener, err := net.Listen("tcp", "localhost:8000")
if err != nil {
log.Fatal(err)
}
go broadcast()
for {
conn, err := listener.Accept()
if err != nil {
log.Print(err)
continue
}
go handleConn(conn)
}
}
func broadcast() {
clients := make(map[client]bool)
for {
select {
case msg := <-massages:
for cli := range clients {
cli.ch <- msg
}
case cli := <-entering:
{
clients[cli] = true
online := []string{}
for c := range clients {
online = append(online, c.name)
}
s := fmt.Sprintf("当前在线成员%v\n", online)
cli.ch <- s
}
case cli := <-leaving:
{
delete(clients, cli)
close(cli.ch)
}
}
}
}
func handleConn(conn net.Conn) {
ch := make(chan string)
go clientWriter(conn, ch)
who := conn.RemoteAddr().String()
ch <- "you are " + who
massages <- who + " has arrived"
entering <- client{who, ch}
input := bufio.NewScanner(conn)
alive := make(chan struct{})
go func() {
for {
select {
case <-time.After(10 * time.Second):
conn.Close()
return
case _, ok := <-alive:
if !ok {
return
}
}
}
}()
for input.Scan() {
massages <- input.Text()
alive <- struct{}{}
}
leaving <- client{who, ch}
massages <- who + " has left"
conn.Close()
}
func clientWriter(conn net.Conn, ch <-chan string) {
for msg := range ch {
fmt.Fprintln(conn, msg)
}
}
package main
import (
"bufio"
"fmt"
"log"
"net"
"time"
)
type client struct {
name string
ch chan<- string
}
var (
massages = make(chan string)
entering = make(chan client)
leaving = make(chan client)
)
func main() {
listener, err := net.Listen("tcp", "localhost:8000")
if err != nil {
log.Fatal(err)
}
go broadcast()
for {
conn, err := listener.Accept()
if err != nil {
log.Print(err)
continue
}
go handleConn(conn)
}
}
func broadcast() {
clients := make(map[client]bool)
for {
select {
case msg := <-massages:
for cli := range clients {
cli.ch <- msg
}
case cli := <-entering:
{
clients[cli] = true
online := []string{}
for c := range clients {
online = append(online, c.name)
}
s := fmt.Sprintf("当前在线成员%v\n", online)
cli.ch <- s
}
case cli := <-leaving:
{
delete(clients, cli)
close(cli.ch)
}
}
}
}
func handleConn(conn net.Conn) {
ch := make(chan string)
go clientWriter(conn, ch)
input := bufio.NewScanner(conn)
input.Scan()
who := input.Text()
ch <- "you are " + who
massages <- who + " has arrived"
entering <- client{who, ch}
alive := make(chan struct{})
go func() {
for {
select {
case <-time.After(10 * time.Second):
conn.Close()
return
case _, ok := <-alive:
if !ok {
return
}
}
}
}()
for input.Scan() {
massages <- input.Text()
alive <- struct{}{}
}
leaving <- client{who, ch}
massages <- who + " has left"
conn.Close()
}
func clientWriter(conn net.Conn, ch <-chan string) {
for msg := range ch {
fmt.Fprintln(conn, msg)
}
}
package main
import (
"bufio"
"fmt"
"log"
"net"
"time"
)
type client struct {
name string
ch chan<- string
}
var (
massages = make(chan string)
entering = make(chan client)
leaving = make(chan client)
)
func main() {
listener, err := net.Listen("tcp", "localhost:8000")
if err != nil {
log.Fatal(err)
}
go broadcast()
for {
conn, err := listener.Accept()
if err != nil {
log.Print(err)
continue
}
go handleConn(conn)
}
}
func broadcast() {
clients := make(map[client]bool)
for {
select {
case msg := <-massages:
for cli := range clients {
sendMsg(msg, cli)
}
case cli := <-entering:
{
clients[cli] = true
online := []string{}
for c := range clients {
online = append(online, c.name)
}
s := fmt.Sprintf("当前在线成员%v\n", online)
sendMsg(s, cli)
}
case cli := <-leaving:
{
delete(clients, cli)
close(cli.ch)
}
}
}
}
func sendMsg(msg string, cli client) {
if cap(cli.ch) < 1 {
log.Print("调用错误,请传入缓存通道")
return
}
select {
case cli.ch <- msg:
default:
log.Print("%s通道阻塞,消息%s丢失", cli.name, msg)
}
}
func handleConn(conn net.Conn) {
ch := make(chan string, 10)
go clientWriter(conn, ch)
input := bufio.NewScanner(conn)
input.Scan()
who := input.Text()
ch <- "you are " + who
massages <- who + " has arrived"
entering <- client{who, ch}
alive := make(chan struct{})
go func() {
for {
select {
case <-time.After(10 * time.Second):
conn.Close()
return
case _, ok := <-alive:
if !ok {
return
}
}
}
}()
for input.Scan() {
massages <- input.Text()
alive <- struct{}{}
}
leaving <- client{who, ch}
massages <- who + " has left"
conn.Close()
}
func clientWriter(conn net.Conn, ch <-chan string) {
for msg := range ch {
fmt.Fprintln(conn, msg)
}
}