[日常] Go语言圣经--示例: 并发的Echo服务

最简单的回声服务器:

package main

import (
        "io"
        "net"
        "log"
)

func main() {
        listener, err := net.Listen("tcp", ":8040")
        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) //新建goroutines处理连接
        }
}

func handleConn(c net.Conn) {
    io.Copy(c, c) // NOTE: ignoring errors
    c.Close()
}

原理:

1.io.Copy()方法
func Copy(dst Writer, src Reader) (written int64, err error)

2.net.Conn类型
type Conn interface {
Read(b []byte) (n int, err error)
Write(b []byte) (n int, err error)
...
}
一个类型如果拥有一个接口需要的所有方法,那么这个类型就实现了这个接口

3.io.Writer
type Writer interface {
Write(p []byte) (n int, err error)
}
4.io.Reader
type Reader interface {
Read(p []byte) (n int, err error)
}

升级版,每条连接一个goroutine,每个goroutine中分出多个输出goroutine

package main

import (
        "bufio"
        "fmt"
        "log"
        "net"
        "strings"
        "time"
)

func main() {
        listener, err := net.Listen("tcp", ":8040")
        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) //新建goroutines处理连接
        }
}

func handleConn(c net.Conn) {
        input := bufio.NewScanner(c)
        for input.Scan() {
                go echo(c, input.Text(), 1*time.Second)
        }
        // NOTE: ignoring potential errors from input.Err()
        c.Close()
}
func echo(c net.Conn, shout string, delay time.Duration) {
        fmt.Fprintln(c, "\t", strings.ToUpper(shout))
        time.Sleep(delay)
        fmt.Fprintln(c, "\t", shout)
        time.Sleep(delay)
        fmt.Fprintln(c, "\t", strings.ToLower(shout))
}

  

1.fmt.Fprintln()
func Fprintln(w io.Writer, a ...interface{}) (n int, err error)

2.bufio.NewScanner()
func NewScanner(r io.Reader) *Scanner
func (s *Scanner) Scan() bool
func (s *Scanner) Text() string

也用到了大量的7.3节 实现接口的条件

  

原文地址:https://www.cnblogs.com/taoshihan/p/8964902.html

时间: 2024-07-29 18:08:12

[日常] Go语言圣经--示例: 并发的Echo服务的相关文章

[日常] Go语言圣经--示例: 并发的Clock服务习题

练习 8.1: 修改clock2来支持传入参数作为端口号,然后写一个clockwall的程序,这个程序可以同时与多个clock服务器通信,从多服务器中读取时间,并且在一个表格中一次显示所有服务传回的结果,类似于你在某些办公室里看到的时钟墙.如果你有地理学上分布式的服务器可以用的话,让这些服务器跑在不同的机器上面:或者在同一台机器上跑多个不同的实例,这些实例监听不同的端口,假装自己在不同的时区.像下面这样: $ TZ=US/Eastern ./clock2 -port 8010 & $ TZ=As

[日常] Go语言圣经--作用域,基础数据类型,整型

go语言圣经-作用域 1.一个声明语句将程序中的实体和一个名字关联,比如一个函数或一个变量 2.一个变量的生命周期是指程序运行时变量存在的有效时间段;声明语句的作用域对应的是一个源代码的文本区域,它是一个编译时的属性 3.句法块是由花括弧所包含;我们可以把块(block)的概念推广到包括其他声明的群组,这些声明在代码中并未显式地使用花括号包裹起来,我们称之为词法块. 4.全局作用域 包级语法域 源文件级的作用域 局部作用域 函数级的作用域 5.从最内层的词法域向全局的作用域查找.内部的会覆盖外部

[日常] Go语言圣经--复数,布尔值,字符串习题

go语言圣经-复数 1.我们把形如a+bi(a,b均为实数)的数称为复数,其中a称为实部,b称为虚部,i称为虚数单位.两种精度的复数类型:complex64和complex128,分别对应float32和float64两种浮点数精度 2.complex函数用于构建复数,real和imag函数分别返回复数的实部和虚部 go语言圣经-布尔型 1.布尔值可以和&&(AND)和||(OR)操作符结合,并且有短路行为 2.&&的优先级比||高 go语言圣经-字符串 1.一个字符串是一

[日常] Go语言圣经--接口约定习题

Go语言圣经-接口1.接口类型是对其它类型行为的抽象和概括2.Go语言中接口类型的独特之处在于它是满足隐式实现的3.Go语言中还存在着另外一种类型:接口类型.接口类型是一种抽象的类型4.一个类型可以自由的使用另一个满足相同接口的类型来进行替换被称作可替换性(LSP里氏替换) 练习 7.1: 使用来自ByteCounter的思路,实现一个针对对单词和行数的计数器.你会发现bufio.ScanWords非常的有用. package main import ( "bufio" "f

[日常] Go语言圣经-基于select的多路复用习题

练习 8.8: 使用select来改造8.3节中的echo服务器,为其增加超时,这样服务器可以在客户端10秒中没有任何喊话时自动断开连接. reverb3.go package main import ( "bufio" "fmt" "log" "net" "strings" "sync" "time" ) func main() { listener, err :=

[日常] Go语言圣经-匿名函数习题2

练习5.13: 修改crawl,使其能保存发现的页面,必要时,可以创建目录来保存这些页面.只保存来自原始域名下的页面.假设初始页面在golang.org下,就不 要保存vimeo.com下的页面. package main import ( "fmt" "io" "io/ioutil" "links" "log" "net/http" "net/url" "

[日常] Go语言圣经-指针对象的方法-bit数组习题2

练习 6.3: (*IntSet).UnionWith会用|操作符计算两个集合的交集,我们再为IntSet实现另外的几个函数IntersectWith(交集:元素在A集合B集合均出现),DifferenceWith(差集:元素出现在A集合,未出现在B集合),SymmetricDifference(并差集:元素出现在A但没有出现在B,或者出现在B没有出现在A). *练习6.4: 实现一个Elems方法,返回集合中的所有元素,用于做一些range之类的遍历操作. 练习 6.2: 定义一个变参方法(*

[日常] Go语言圣经--接口约定习题2

练习 7.3: 为在gopl.io/ch4/treesort (§4.4)的*tree类型实现一个String方法去展示tree类型的值序列. package main import( "fmt" "bytes" ) func main(){ //定义数组 arr:=[...]int{6,2,1,3,4,5} var t *tree for i:=0;i<len(arr);i++{ t=add(t,arr[i]) } //t1:=Sort(arr[:],t)

[日常] Go语言圣经--Channel习题

练习 8.3: 在netcat3例子中,conn虽然是一个interface类型的值,但是其底层真实类型是*net.TCPConn,代表一个TCP连接.一个TCP连接有读和写两个部分,可以使用CloseRead和CloseWrite方法分别关闭它们.修改netcat3的主goroutine代码,只关闭网络连接中写的部分,这样的话后台goroutine可以在标准输入被关闭后继续打印从reverb1服务器传回的数据.(要在reverb2服务器也完成同样的功能是比较困难的:参考练习 8.4.) 1.n