一、网络分层

image.png

二、socket

image.png

Server Demo

  1. package main
  2. import (
  3. "fmt"
  4. "net"
  5. "strings"
  6. )
  7. func main() {
  8. //创建监听
  9. ip := "127.0.0.1"
  10. port := 8848
  11. address := fmt.Sprintf("%s:%d", ip, port)
  12. //func Listen(network, address string) (Listener, error) {
  13. //net.Listen("tcp", ":8848") //简写,冒号前面默认是本机: 127.0.0.1
  14. listener, err := net.Listen("tcp", address)
  15. if err != nil {
  16. fmt.Println("net.Listen err:", err)
  17. return
  18. }
  19. fmt.Println("监听中...")
  20. //Accept() (Conn, error)
  21. conn, err := listener.Accept()
  22. if err != nil {
  23. fmt.Println("listener.Accept err:", err)
  24. return
  25. }
  26. fmt.Println("连接建立成功!")
  27. //创建一个容器,用于接收读取到的数据
  28. buf := make([]byte, 1024) //使用make来创建字节切片, byte ==> uint8
  29. //Read(b []byte) (n int, err error)
  30. //cnt:真正读取client发来的数据的长度
  31. cnt, err := conn.Read(buf)
  32. if err != nil {
  33. fmt.Println("conn.Read err:", err)
  34. return
  35. }
  36. fmt.Println("Client =====> Server, 长度:", cnt, ",数据:", string(buf[0:cnt]))
  37. //服务器对客户端请求进行响应 ,将数据转成大写 "hello" ==> HELLO
  38. //func ToUpper(s string) string {
  39. upperData := strings.ToUpper(string(buf[0:cnt]))
  40. //Write(b []byte) (n int, err error)
  41. cnt, err = conn.Write([]byte(upperData))
  42. fmt.Println("Client <====== Server, 长度:", cnt, ",数据:", upperData)
  43. //关闭连接
  44. conn.Close()
  45. }

client demo

  1. package main
  2. import (
  3. "fmt"
  4. "net"
  5. )
  6. func main() {
  7. conn, err := net.Dial("tcp", ":8848")
  8. if err != nil {
  9. fmt.Println("net.Dial err:", err)
  10. return
  11. }
  12. fmt.Println("cliet与server连接建立成功!")
  13. sendData := []byte("helloworld")
  14. //向服务器发送数据
  15. cnt, err := conn.Write(sendData)
  16. if err != nil {
  17. fmt.Println("conn.Write err:", err)
  18. return
  19. }
  20. fmt.Println("Client ===> Server cnt:", cnt, ", data:", string(sendData))
  21. //接收服务器返回的数据
  22. //创建buf,用于接收服务器返回的数据
  23. buf := make([]byte, 1024)
  24. cnt, err = conn.Read(buf)
  25. if err != nil {
  26. fmt.Println("conn.Read err:", err)
  27. return
  28. }
  29. fmt.Println("Client <==== Server , cnt:", cnt, ", data:", string(buf[0:cnt]))
  30. conn.Close()
  31. }

image.png
image.png

socket-server处理多个连接,每个连接可以接收多次数据

  1. package main
  2. import (
  3. "fmt"
  4. "net"
  5. "strings"
  6. )
  7. func main() {
  8. //创建监听
  9. ip := "127.0.0.1"
  10. port := 8848
  11. address := fmt.Sprintf("%s:%d", ip, port)
  12. //func Listen(network, address string) (Listener, error) {
  13. //net.Listen("tcp", ":8848") //简写,冒号前面默认是本机: 127.0.0.1
  14. listener, err := net.Listen("tcp", address)
  15. if err != nil {
  16. fmt.Println("net.Listen err:", err)
  17. return
  18. }
  19. //需求:
  20. // server可以接收多个连接, ====> 主go程负责监听,子go程负责数据处理
  21. // 每个连接可以接收处理多轮数据请求
  22. for {
  23. fmt.Println("监听中...")
  24. //Accept() (Conn, error)
  25. conn, err := listener.Accept()
  26. if err != nil {
  27. fmt.Println("listener.Accept err:", err)
  28. return
  29. }
  30. fmt.Println("连接建立成功!")
  31. go handleFunc(conn)
  32. }
  33. }
  34. //处理具体业务的逻辑,需要将conn传递进来,每一新连接,conn是不同的
  35. func handleFunc(conn net.Conn) {
  36. for { //这个for循环,保证每一个连接可以多次接收处理客户端请求
  37. //创建一个容器,用于接收读取到的数据
  38. buf := make([]byte, 1024) //使用make来创建字节切片, byte ==> uint8
  39. fmt.Println("准备读取客户端发送的数据....")
  40. //Read(b []byte) (n int, err error)
  41. //cnt:真正读取client发来的数据的长度
  42. cnt, err := conn.Read(buf)
  43. if err != nil {
  44. fmt.Println("conn.Read err:", err)
  45. return
  46. }
  47. fmt.Println("Client =====> Server, 长度:", cnt, ",数据:", string(buf[0:cnt]))
  48. //服务器对客户端请求进行响应 ,将数据转成大写 "hello" ==> HELLO
  49. //func ToUpper(s string) string {
  50. upperData := strings.ToUpper(string(buf[0:cnt]))
  51. //Write(b []byte) (n int, err error)
  52. cnt, err = conn.Write([]byte(upperData))
  53. fmt.Println("Client <====== Server, 长度:", cnt, ",数据:", upperData)
  54. }
  55. //关闭连接
  56. _ = conn.Close()
  57. }

image.png
当client多次发送数据时:
image.png

三、http

1. 概述

编写web语言:

  1. java
  2. php, 现在都在尝试使用go语言重写
  3. python,豆瓣
  4. go语言 ===》 beego,gin两个主流的web框架

https协议:我们使用浏览器访问的时候发送的就是http请求

  1. http是应用层的协议,底层还是依赖传输层:tcp(短连接),网络层(ip)
  2. 无状态的,每一次请求都是独立的,下次请求需要重新建立连接
  3. https:

    1. http是标准协议 ==》 明文传输,不安全
    2. https不是标准协议 ===》 https = http + ssl(非对称加密,数字证书)—》加密的
    3. 现在所有网站都会尽量要求使用https开发:安全

      2. http请求报文格式

      image.png
      一个http请求可以分为4部分:
  4. 请求行:包含三部分

    1. 格式: 方法 + URL + 协议版本号
    2. 实例: POST + /chapter17/user + HTTP/1.1
    3. 请求方法:
      1. GET:获取数据
      2. POST:上传数据(表单格式,json格式)
      3. PUT:修改数据
      4. DELETE: 用于删除数据
  5. 请求头
    1. 格式: key :value
    2. 可以有很多个键值对(包含协议自带,也包含用户自定义的)
    3. 常见重要头:
      1. Accept : 接收数据的格式
      2. User-Agent : 描述用户浏览器的信息
      3. Connection: Keep-Alive (长链接), Close(短连接)
      4. Aceept-Encoding : gzip, xxx , 描述可以接受的编码
      5. Cookie: 由服务器设置的key=value数据,客户端下次请求的时候可以携带过来
      6. Content-Type:
        1. appliction/-form(表示上传的数据是表单格式),
        2. application/json(表示body的数据是json格式)
      7. 用户可以自定义的:
        1. name : Duke
        2. age : 18
  6. 空行
    1. 告诉服务器,请求头结束了,用于分割
  7. 请求包体(可选的)
    1. 一般在POST方法时,会配套提供BODY
    2. 在GET的时候也可以提供BODY,但是这样容易让人混淆,建议不要这样使用
    3. 上传两种数据格式:
      1. 表单: 姓名,性别,年龄
      2. json数据格式

启动tcp的server案例,使用postman发起http请求,查看http请求头信息:

  1. package main
  2. import (
  3. "fmt"
  4. "net"
  5. "strings"
  6. )
  7. func main() {
  8. //创建监听
  9. ip := "127.0.0.1"
  10. port := 8848
  11. address := fmt.Sprintf("%s:%d", ip, port)
  12. //func Listen(network, address string) (Listener, error) {
  13. //net.Listen("tcp", ":8848") //简写,冒号前面默认是本机: 127.0.0.1
  14. listener, err := net.Listen("tcp", address)
  15. if err != nil {
  16. fmt.Println("net.Listen err:", err)
  17. return
  18. }
  19. fmt.Println("server start ...")
  20. //需求:
  21. // server可以接收多个连接, ====> 主go程负责监听,子go程负责数据处理
  22. // 每个连接可以接收处理多轮数据请求
  23. for {
  24. fmt.Println("监听中...")
  25. //Accept() (Conn, error)
  26. conn, err := listener.Accept()
  27. if err != nil {
  28. fmt.Println("listener.Accept err:", err)
  29. return
  30. }
  31. fmt.Println("连接建立成功!")
  32. go handleFunc(conn)
  33. }
  34. }
  35. //处理具体业务的逻辑,需要将conn传递进来,每一新连接,conn是不同的
  36. func handleFunc(conn net.Conn) {
  37. for { //这个for循环,保证每一个连接可以多次接收处理客户端请求
  38. //创建一个容器,用于接收读取到的数据
  39. buf := make([]byte, 1024) //使用make来创建字节切片, byte ==> uint8
  40. fmt.Println("准备读取客户端发送的数据....")
  41. //Read(b []byte) (n int, err error)
  42. //cnt:真正读取client发来的数据的长度
  43. cnt, err := conn.Read(buf)
  44. if err != nil {
  45. fmt.Println("conn.Read err:", err)
  46. return
  47. }
  48. fmt.Println("Client =====> Server, 长度:", cnt, ",数据:", string(buf[0:cnt]))
  49. //服务器对客户端请求进行响应 ,将数据转成大写 "hello" ==> HELLO
  50. //func ToUpper(s string) string {
  51. upperData := strings.ToUpper(string(buf[0:cnt]))
  52. //Write(b []byte) (n int, err error)
  53. cnt, err = conn.Write([]byte(upperData))
  54. fmt.Println("Client <====== Server, 长度:", cnt, ",数据:", upperData)
  55. }
  56. //关闭连接
  57. _ = conn.Close()
  58. }

postman:
image.png
结果:
image.png
前端与后台传输数据方法:

  1. 放在请求头中 (常用)
  2. 放在请求包体中
  3. 放在url中: GET /user?id=1001&score=90&school=middleschool (常用)

    1. ?分割参数和uri
    2. 多个参数之间使用&分割,每一个参数数据都是一个键值对

      3. http响应消息格式

      http响应格式也分为4部分:
  4. 第一部分:状态行

    1. 协议格式: 协议版本号 + 状态码 + 状态描述
    2. 实例1:HTTP/1.1 + 200 + OK
    3. 实例2:HTTP/1.1 +404 + not found
    4. 常用的状态码:
      • 1xx ===》客户端可以即系发送请求(一般感知不到)
      • 2xx ===》正常访问, 200
      • 3xx ===》重定向
      • 4xx
        • 401 ===》 未授权 not authorized
        • 404 ===> Not found
      • 5xx
        • 501 ==> Internal Error (服务器内部错误)
  5. 第二部分:响应头
    1. Content-Type : application/json
    2. Server: Apache
    3. Data : Mon, 12 Sep …
    4. ….
  6. 第三部分:空行
    1. 用于分割,表示下面没有响应头了
  7. 第四部分:响应包体
    1. 通常是返回json数据

image.png

4. http client

  1. package main
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "net/http"
  6. )
  7. func main() {
  8. //http包
  9. client := http.Client{}
  10. //func (c *Client) Get(url string) (resp *Response, err error) {
  11. resp, err := client.Get("https://www.baidu.com")
  12. if err != nil {
  13. fmt.Println("client.Get err:", err)
  14. return
  15. }
  16. //放在上面,内容很多
  17. body := resp.Body
  18. fmt.Println("body 111:", body)
  19. //func ReadAll(r io.Reader) ([]byte, error) {
  20. readBodyStr, err := ioutil.ReadAll(body)
  21. if err != nil {
  22. fmt.Println("read body err:", err)
  23. return
  24. }
  25. fmt.Println("body string:", string(readBodyStr))
  26. //beego, gin ==> web框架
  27. ct := resp.Header.Get("Content-Type")
  28. date := resp.Header.Get("Date")
  29. server := resp.Header.Get("Server")
  30. fmt.Println("content-type:", ct)
  31. fmt.Println("date:", date)
  32. //BWS是Baidu Web Server,是百度开发的一个web服务器 大部分百度的web应用程序使用的是BWS
  33. fmt.Println("server:", server)
  34. url := resp.Request.URL
  35. code := resp.StatusCode
  36. status := resp.Status
  37. fmt.Println("url:", url) //https://www.baidu.com
  38. fmt.Println("code:", code) //200
  39. fmt.Println("status:", status) //OK
  40. }

image.png

5. http server

  1. package main
  2. import (
  3. "fmt"
  4. "io"
  5. "net/http"
  6. )
  7. func main() {
  8. //注册路由 router
  9. //xxxx/user ===> func1
  10. //xxxx/name ===> func2
  11. //xxxx/id ===> func3
  12. //https://127.0.0.1:8080/user, func是回调函数,用于路由的响应,这个回调函数原型是固定
  13. http.HandleFunc("/user", func(writer http.ResponseWriter, request *http.Request) {
  14. //request : ===> 包含客户端发来的数据
  15. fmt.Println("用户请求详情:")
  16. fmt.Println("request:", request)
  17. //这里是具体处理业务逻辑xxx
  18. //writer : ===> 通过writer将数据返回给客户端
  19. _, _ = io.WriteString(writer, "这是/user请求返回的数据!")
  20. })
  21. //https://127.0.0.1:8080/name
  22. http.HandleFunc("/name", func(writer http.ResponseWriter, request *http.Request) {
  23. _, _ = io.WriteString(writer, "这是/name请求返回的数据!")
  24. })
  25. //https://127.0.0.1:8080/id
  26. http.HandleFunc("/id", func(writer http.ResponseWriter, request *http.Request) {
  27. _, _ = io.WriteString(writer, "这是/id请求返回的数据!")
  28. })
  29. fmt.Println("Http Server start ...")
  30. //func ListenAndServe(addr string, handler Handler) error {
  31. if err := http.ListenAndServe("127.0.0.1:8080", nil); err != nil {
  32. fmt.Println("http start failed, err:", err)
  33. return
  34. }
  35. //if err != nil {
  36. // fmt.Println("http start failed, err:", err)
  37. // return
  38. //}
  39. }

image.png

6. JSON

  1. {
  2. "name":"矮大紧",
  3. "sex":"man",
  4. "age":131,
  5. "girls":["金莲","凤姐","码蓉","春哥"],
  6. "成绩": [2, 14, 9, 78, 96],
  7. "家电":{"彩电":"海尔", "洗衣机":"三星"},
  8. "stars": [
  9. { "name":"Faye" ,"address":" 北京 " },
  10. { "name":"andy" ,"address":" 香港 " },
  11. { "name":"eddie","address":" 台湾 " }
  12. ]
  13. }

记住,json语法要求最后一个元素后面,不能加逗号

json编解码

在网络中传输的时候,把Student结构体,编码成json字符串,传输 ===》 结构体 ==》 字符串 ==》 编码
接收字符串,需要将字符串转换成结构体,然后操作 ==》 字符串 ==》 结构体 ==》解密

  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. )
  6. type Student struct {
  7. Id int
  8. Name string
  9. Age int
  10. //注意,gender是小写的, 小写字母开头的,在json编码时会忽略掉
  11. gender string
  12. }
  13. func main() {
  14. //在网络中传输的时候,把Student结构体,编码成json字符串,传输 ===》 结构体 ==》 字符串 ==》 编码
  15. //接收字符串,需要将字符串转换成结构体,然后操作 ==》 字符串 ==》 结构体 ==》解密
  16. lily := Student{
  17. Id: 1,
  18. Name: "Lily",
  19. Age: 20,
  20. gender: "女士",
  21. }
  22. //编码(序列化),结构=》字符串
  23. //func Marshal(v interface{}) ([]byte, error) {
  24. encodeInfo, err := json.Marshal(&lily)
  25. if err != nil {
  26. fmt.Println("json.Marshal err:", err)
  27. return
  28. }
  29. fmt.Println("encodeInfo:", string(encodeInfo))
  30. //对端接收到数据
  31. //反序列化(解码): 字符串=》结构体
  32. var lily2 Student
  33. //func Unmarshal(data []byte, v interface{}) error {
  34. if err := json.Unmarshal([]byte(encodeInfo), &lily2); err != nil {
  35. fmt.Println("json.Unmarshal err:", err)
  36. return
  37. }
  38. fmt.Println("name:", lily2.Name)
  39. fmt.Println("gender:", lily2.gender)
  40. fmt.Println("age:", lily2.Age)
  41. fmt.Println("id:", lily2.Id)
  42. }

image.png

结构体标签

  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. )
  6. type Teacher struct {
  7. Name string `json:"-"` //==> 在使用json编码时,这个编码不参与
  8. Subject string `json:"Subject_name"` //==> 在json编码时,这个字段会编码程Subject_name
  9. Age int `json:"age,string"` //==>在json编码时,将age转成程string类型, 一定要两个字段:名字,类型,中间不能有空格
  10. Address string `json:"address,omitempty"` //==》在json编码时,如果这个字段是空的,那么忽略掉,不参与编码
  11. //注意,gender是小写的, 小写字母开头的,在json编码时会忽略掉
  12. gender string
  13. }
  14. type Master struct {
  15. Name string
  16. Subject string
  17. Age int
  18. Address string
  19. gender string
  20. }
  21. func main() {
  22. t1 := Teacher{
  23. Name: "Duke",
  24. Subject: "Golang",
  25. Age: 18,
  26. gender: "Man",
  27. Address: "北京",
  28. }
  29. fmt.Println("t1:", t1)
  30. encodeInfo, _ := json.Marshal(&t1)
  31. fmt.Println("encodeInfo:", string(encodeInfo))
  32. //解码
  33. t2 := Teacher{}
  34. _ = json.Unmarshal(encodeInfo, &t2)
  35. fmt.Println("t2:", t2.Subject)
  36. m1 := Master{}
  37. _ = json.Unmarshal(encodeInfo, &m1)
  38. fmt.Println("m1:", m1)
  39. }

image.png
总结:

  1. 对于结构体进行编码时(json): 字段的首字母必须大写,否则无法编码
  2. 如果json格式要求key小写,那么可以通过标签(tag)来解决
  3. tag细节: ``go //==> 在使用json编码时,这个编码不参与 Name stringjson:”-“`

//==> 在json编码时,这个字段会编码程Subject_name Subject string json:"Subject_name"

//==>在json编码时,将age转成程string类型, 一定要两个字段:名字,类型,中间不能有空格 Age int json:"age,string"
```