网络编程
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 } }