zoukankan      html  css  js  c++  java
  • golang(9):网络编程 & redis

    网络编程

    TCP/IP 协议:

    1. TCP(传输控制协议) -- 应用程序之间通信
    2. UDP(用户数据包协议)-- 应用程序之间的简单通信
    3. IP(网际协议) -- 计算机之间的通信
    4. DHCP(动态主机配置协议) -- 针对动态寻址

    TCP 编程

    go服务端的处理流程:

    a. 监听端口
    b. 接收客户端的连接
    c. 创建 goroutine,处理该连接

    示例代码:

    package main
    
    import (
        "fmt"
        "net"        // 网络相关的包都在这个 net 包里面
    )
    
    func main(){
        fmt.Println("start server...")
    
        // 1. 监听ip和端口
        listen,err := net.Listen("tcp","0.0.0.0:50000")        // 监听端口;第一个参数是监听什么类型的协议,第二个参数是监听的 端口, 0.0.0.0 表示监听这台机器上的所有 ip (监听所有的网卡)
        if err != nil {
            fmt.Println("listen failed,err:",err)
            return
        }
    
    
        // 2. 接收连接
        for {
            conn,err := listen.Accept()        // 接收连接;conn 表示与当前用户之间的通信的管道(连接)
            if err != nil {
                fmt.Println("accept failed,err:",err)
                continue
            }
    
            // 3. 创建 goroutine 去处理该连接
            go process(conn)        // 新起一个 goroutine 去处理这个连接
        }
    }
    
    func process(conn net.Conn){        // net.Conn 类型
        defer conn.Close()        // 执行完后要把连接关掉,要不然会造成资源泄露
        for {
            buf := make([]byte,512)        // 网络连接发过来的是字节流,所以定义一个 byte 切片
            _ , err := conn.Read(buf)        // conn.Read()接收的是一个 byte切片; 从 conn 这个对象里面 读取客户端发来的数,保存到 buf数组 中; conn.Read() 返回两个参数:第一个参数是读取到了多少个字节,第二个是 error 
            if err != nil {
                fmt.Println("read err:",err)
                return
            }
            fmt.Print("read:",string(buf))        // 把 byte切片 转换为 字符串
        }
    }

    go客户端的处理流程:

    a. 建立与服务端的连接
    b. 进行数据接收
    c. 关闭连接

    示例代码:

    // 示例代码:
    package main
    
    import (
        "bufio"
        "fmt"
        "net"
        "os"
        "strings"
    )
    
    func main(){
        // 1. 建立连接
        conn,err := net.Dial("tcp","127.0.0.1:50000")        // 建立连接;net.Dial() 参数:第一个是指定协议类型,第二个 ip 和 端口
        if err != nil {
            fmt.Println("dial err:",err)
            return
        }
    
        // 3. 关闭连接
        defer conn.Close()
    
        // 2. 接收数据
        inputReader := bufio.NewReader(os.Stdin)    
        // 从终端读取用户输入
        for {
            input, _ := inputReader.ReadString('
    ')
            trimmedInput := strings.Trim(input,"
    ")        // 去掉 
    
            if trimmedInput == "Q"{        // 退出
                return
            }
    
            _, err := conn.Write([]byte(trimmedInput))        // 把读取到的用户输入的内容写入到 conn 进行传输;传输是 字节流 byte
            if err != nil {
                return
            }
        }
        
    }

    发送HTTP请求

    1. HTTP协议是基于TCP协议之上的文本协议
    2. 每行文本使用 
     结尾,当连续两个 
     时,表示整个数据包结束

    示例代码:

    package main
    
    import (
        "fmt"
        //"io"
        "net"
    )
    
    func main(){
        conn,err := net.Dial("tcp","www.baidu.com:80")
        if err != nil{
            fmt.Println("Error dialing:",err)
        }
    
        defer conn.Close()
        msg := "GET / HTTP/1.1
    "        // HTTP版本号
        msg += "HOST: www.baidu.com
    "
        msg += "Connection: close
    "    // 该行表示是 短连接
        msg += "
    
    "        // 结束
    
        // _,err = io.WriteString(conn,msg)        // 把msg写入到 conn中并发送(给百度发送GET请求)
        _, err = conn.Write([]byte(msg))        // 这两种发送方式都可以
        if err != nil {
            fmt.Println("write string failed,",err)
            return
        }
    
        buf := make([]byte,4096)
        for {
            n,err := conn.Read(buf)        // 读取从百度返回的数据(html代码),给buf
            if err != nil {
                fmt.Println("err:",err)
                break
            }
            fmt.Println(string(buf[:n]))        // 不明白为啥一定 buf 要加上 [:n] ,要不然打印出来的数据(html代码)不完整 。。。(Aug 12 2019)
        }
    }

    UDP协议:

    1. 用户数据报协议
    2. 无连接,直接进行数据发送
    3. 不可靠、没有时序
    4. 实时性比较好,通常用于视频、直播相关领域

    redis

    redis是个开源的高性能的key-value的内存数据库,可以把它当成远程的数据结构。
    支持的value类型非常多,比如string、list(链表)、set(集合)、hash表等等
    redis性能非常高,单机能够达到15w qps,通常适合做缓存。

    go中redis 的使用

    使用第三方开源的redis库: github.com/garyburd/redigo/redis
    
    import(
        "github.com/garyburd/redigo/redis"
    )

    安装 redis 包

    利用 go get 命令,如下:
    go get github.com/garyburd/redigo/redis

    1. 连接redis

    // 示例代码:
    package main
    
    import (
        "fmt"
        "github.com/garyburd/redigo/redis"
    )
    
    func main(){
        redisConn,err := redis.Dial("tcp","localhost:6379")        // 连接redis 
        if err != nil{
            fmt.Println("connect redis failed,err:",err)
            return
        }
        defer redisConn.Close()        // 关闭redis连接
    }

    2. 字符串类型 : Set 和 Get 接口

    // 示例代码:
    package main
    
    import (
        "fmt"
        "github.com/garyburd/redigo/redis"
    )
    
    func main(){
        redisConn,err := redis.Dial("tcp","localhost:6379")
        if err != nil{
            fmt.Println("connect redis failed,err:",err)
            return
        }
        defer redisConn.Close()
    
        // 往 redis 中写入字符串型数据
        _,err = redisConn.Do("Set","abc",100)        // 在 redis中存入字符串
        if err != nil{
            fmt.Println("set failed,err:",err)
            return 
        }
    
        // 从 redis 中读取数据
        read,err := redis.Int(redisConn.Do("Get","abc"))        // redisConn.Do("Get",key) ---> 从redis 中读取数据;redis.Int() --> 把读取出来的数据转化为 int 类型
        if err != nil {
            fmt.Println("read from redis failed, err:",err)
            return
        }
    
        fmt.Printf("read:%v
    ",read)
    }
    
    
    // 运行结果:
    [root@NEO example02_redis_set_get]# go run main/main.go
    read:100
    [root@NEO example02_redis_set_get]# 

    2.2  批量 set

    package main
    
    import (
        "fmt"
        "github.com/garyburd/redigo/redis"
    )
    
    func main(){
        redisConn,err := redis.Dial("tcp","localhost:6379")
        if err != nil {
            fmt.Println("connect redis failed,err:",err)
            return
        }
        defer redisConn.Close()
    
        _,err = redisConn.Do(
                    "MSet",
                    "name","neo",
                    "age","18")        // 这个小括号不能写到下一行,要不然语法报错
        if err != nil {
            fmt.Println("MSet failed,err:",err)
            return
        }
    
        read,err := redis.Strings(redisConn.Do("MGet","name","age"))        // "MGet" 返回的是 type []interface {}
        if err != nil {
            fmt.Println("MGet failed,err:",err)
            return
        }
        fmt.Printf("MGet read:%v
    ",read)
    
        for k,v := range read{
            fmt.Printf("read[%d]=%v
    ",k,v)
        }
    }
    
    // 运行结果:
    [root@NEO example02_redis_hash_mset]# go run main/main.go
    MGet read:[neo 18]
    read[0]=neo
    read[1]=18
    [root@NEO example02_redis_hash_mset]# 

    3. 散列类型hash

    package main
    
    import (
        "fmt"
        "github.com/garyburd/redigo/redis"
    )
    
    func main(){
        redisConn,err := redis.Dial("tcp","localhost:6379")
        if err != nil {
            fmt.Println("connect redis failed,err:",err)
            return
        }
    
        defer redisConn.Close()
    
        _,err = redisConn.Do("HSet","book","price",100)            //  在 redis 中存储 hash类型的值
        if err != nil {
            fmt.Println("set hash failed,err:",err)
            return
        }
    
        read,err := redis.Int(redisConn.Do("HGet","book","price"))        // 从 redis 中获取 hash 类型的值
        if err != nil{
            fmt.Println("read redis hash failed,err:",err)
            return
        }
        fmt.Printf("readMap:%v
    ",read)
    }
    
    
    // 运行结果:
    [root@NEO example02_redis_hash]# go run main/main.go
    read:100
    [root@NEO example02_redis_hash]# 

    4. list 列表操作

    // 示例代码:
    package main
    
    import (
        "fmt"
        "github.com/garyburd/redigo/redis"
    )
    
    func main(){
        redisConn,err := redis.Dial("tcp","localhost:6379")
        if err != nil {
            fmt.Println("dial redis failed,err:",err)
            return
        }
        defer redisConn.Close()
    
        _,err = redisConn.Do("lpush","book_list","abc","d","ef")    // 列表操作 : lpush; key --> book_list;
        if err != nil{
            fmt.Println("lpush failed,err:",err)
            return
        }
    
        read,err := redis.String(redisConn.Do("lpop","book_list"))        // 列表操作: lpop
        if err != nil{
            fmt.Println("lpop failed,err:",err)
            return
        }
    
        fmt.Println("read:",read)
    }
    
    
    // 运行结果:
    [root@NEO example02_redis_list_lpush]# go run main/main.go 
    read: ef
    [root@NEO example02_redis_list_lpush]# 

    5. 设置过期时间

    package main
    
    import (
        "fmt"
        "github.com/garyburd/redigo/redis"
    )
    
    func main(){
        redisConn,err := redis.Dial("tcp","localhost:6379")
        if err != nil {
            fmt.Println("dial failed,err:",err)
            return
        }
        defer redisConn.Close()
    
        _,err = redisConn.Do("expire","name",30)    // 设置过期时间
        if err != nil {
            fmt.Println("set expire failed,err:",err)
            return
        }
    
    }
  • 相关阅读:
    react_瞎敲
    linux 删除类似文件
    mysql建立dblink 视图,无法查询到数据的问题
    Guava-Retrying 请求重试机制
    Command line is too long. Shorten command line for WebServiceUtilsTest.callMethod or also for JUnit default
    @Scheduled 定时任务注解不能运行
    jq拷贝表单$("#searchForm").clone(true),无法将select2数据value拷贝的问题
    正则表达式的lookaround(lookahead/lookbehind)以及密码复杂度检查
    MYSQL列的长度,NUMERIC_PRECISION和COLUMN_TYPE
    Qira-docker安装与使用
  • 原文地址:https://www.cnblogs.com/neozheng/p/11337540.html
Copyright © 2011-2022 走看看