Go 网络编程

122 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第29天,点击查看活动详情

三、http

1. 概述

编写web语言:

  1. java
  2. go语言 ===》 beego,gin两个主流的web框架

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

  1. http是应用层的协议,底层还是依赖传输层:tcp(短连接),网络层(ip)

  2. 无状态的,每一次请求都是独立的,下次请求需要重新建立连接

  3. https:

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

2. http请求报文格式

image.png

一个http请求可以分为4部分:

  1. 请求行:包含三部分
  • 格式: 方法 + URL + 协议版本号

  • 实例: POST + /chapter17/user + HTTP/1.1

  • 请求方法:

    1. GET:获取数据
    2. POST:上传数据(表单格式,json格式)
    3. PUT:修改数据
    4. DELETE: 用于删除数据
  • 请求头

    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
  • 空行

    1. 告诉服务器,请求头结束了,用于分割
  • 请求包体(可选的)

    1. 一般在POST方法时,会配套提供BODY

    2. 在GET的时候也可以提供BODY,但是这样容易让人混淆,建议不要这样使用

    3. 上传两种数据格式:

      1. 表单: 姓名,性别,年龄
      2. json数据格式

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

package main
​
import (
    "fmt"
    "net"
    "strings"
)
​
func main() {
    //创建监听
    ip := "127.0.0.1"
    port := 8848
    address := fmt.Sprintf("%s:%d", ip, port)
​
    //func Listen(network, address string) (Listener, error) {
    //net.Listen("tcp", ":8848") //简写,冒号前面默认是本机: 127.0.0.1
    listener, err := net.Listen("tcp", address)
​
    if err != nil {
        fmt.Println("net.Listen err:", err)
        return
    }
    fmt.Println("server start ...")
​
    //需求:
    // server可以接收多个连接, ====> 主go程负责监听,子go程负责数据处理
    // 每个连接可以接收处理多轮数据请求for {
        fmt.Println("监听中...")
​
        //Accept() (Conn, error)
        conn, err := listener.Accept()
        if err != nil {
            fmt.Println("listener.Accept err:", err)
            return
        }
​
        fmt.Println("连接建立成功!")
​
        go handleFunc(conn)
    }
}
​
//处理具体业务的逻辑,需要将conn传递进来,每一新连接,conn是不同的
func handleFunc(conn net.Conn) {
    for { //这个for循环,保证每一个连接可以多次接收处理客户端请求
        //创建一个容器,用于接收读取到的数据
        buf := make([]byte, 1024) //使用make来创建字节切片, byte ==> uint8
​
        fmt.Println("准备读取客户端发送的数据....")
​
        //Read(b []byte) (n int, err error)
        //cnt:真正读取client发来的数据的长度
        cnt, err := conn.Read(buf)
        if err != nil {
            fmt.Println("conn.Read err:", err)
            return
        }
​
        fmt.Println("Client =====> Server, 长度:", cnt, ",数据:", string(buf[0:cnt]))
​
        //服务器对客户端请求进行响应 ,将数据转成大写 "hello" ==> HELLO
        //func ToUpper(s string) string {
        upperData := strings.ToUpper(string(buf[0:cnt]))
​
        //Write(b []byte) (n int, err error)
        cnt, err = conn.Write([]byte(upperData))
        fmt.Println("Client  <====== Server, 长度:", cnt, ",数据:", upperData)
    }
​
    //关闭连接
    _ = conn.Close()
}
​

结果:

image.png

前端与后台传输数据方法:

  1. 放在请求头中 (常用)

  2. 放在请求包体中

  3. 放在url中: GET /user?id=1001&score=90&school=middleschool (常用)

    1. ?分割参数和uri

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

    3. 一般在url中传输参数的时候,参数值一般会做base64处理,为了防止与url特殊字符混淆

      1. 0~9
      2. a-zA-Z
      3. _/