【Go语言圣经1.5】

embedded/2025/3/18 4:15:52/

目标

概念

要点(案例)

实现了一个简单的 HTTP 客户端程序,主要功能是:

  • 读取命令行参数:程序从命令行获取一个或多个 URL。
  • 发送 HTTP GET 请求:使用 Go 内置的 net/http 包,通过 http.Get 函数向每个 URL 发送请求。
  • 读取并输出响应内容:利用 io.ReadAll 读取服务器返回的响应体,将其作为字符串输出到标准输出(屏幕)。
  • 错误处理:如果请求过程中出现错误,程序会在标准错误输出(os.Stderr)中打印错误信息,并以错误状态码退出程序。

这段代码的设计思路与 Unix 下的 curl 工具有相似之处,展示了如何用 Go 编写一个最简单的 HTTP 请求工具。

  1. 包导入

    // Fetch prints the content found at a URL.
    package mainimport ("fmt""io/ioutil""net/http" // 实现了 HTTP 客户端和服务端的基本功能,这里主要用来发起 GET 请求。"os"
    )
    
  2. 程序入口和获取命令行参数

    func main() {for _, url := range os.Args[1:] {// ...}
    }
  3. 发起 HTTP GET 请求

    resp, err := http.Get(url)
    if err != nil {fmt.Fprintf(os.Stderr, "fetch: %v\n", err)os.Exit(1)
    }
    
    • 使用 fmt.Fprintf 将错误信息写入标准错误流,并通过 os.Exit(1) 退出程序,状态码 1 表示出现错误。
  4. 读取响应体

    b, err := io.ReadAll(resp.Body)
    resp.Body.Close()
    if err != nil {fmt.Fprintf(os.Stderr, "fetch: reading %s: %v\n", url, err)os.Exit(1)
    }
    • 服务器返回的响应体是一个流
    • 用io.ReadAll全部读取出来,并存入变量 b 中。
      • b 是由 io.ReadAll 返回的一个 []byte,它用来存储从 resp.Body 中读取到的所有数据。从这个角度看,b 就充当了一个缓冲区,其主要特点和作用如下:
        • 当你调用 io.ReadAll(resp.Body) 时,Go 语言会从网络流(resp.Body)中一次性读取所有数据,并将这些数据存储到一个新的切片 b 中。
        • 这个切片 b 就起到了“缓冲”的作用:它暂时保存了从网络中读取到的数据,方便程序后续操作(比如打印到标准输出)。
        • 为了存储数据,程序必须从操作系统申请一块内存区域,这就是“申请缓冲区”。在这里,io.ReadAll 内部会动态分配足够大小的内存来保存整个响应体的数据。
    • 关闭响应体:调用 resp.Body.Close() 释放与响应相关的资源,防止内存或文件描述符泄露。
    • 很多程序会使用 defer resp.Body.Close() 来确保函数退出时自动关闭流,这也是 Go 中的一个重要惯用法。
      • 在更多场景下推荐使用 defer 来保证资源释放,即使函数中途发生错误也能正确关闭资源。
  5. 输出结果

    fmt.Printf("%s", b)
    

语言特性

  1. 检查错误:Go 语言没有异常机制,而是通过返回值来处理错误。每一步操作(如 HTTP 请求、读取流)都需要检查错误,这种显式的错误处理方式有助于写出健壮的代码。
  2. 资源管理:及时释放资源:读取完响应体后调用 resp.Body.Close() 是非常重要的,防止资源泄露。理解这点对于编写网络或文件 I/O 程序尤为关键。
  3. 标准库的强大支持
    • net/http 包:Go 语言的标准库提供了对 HTTP 的强大支持,不仅能发起请求,还可以构建服务器,这让开发者能快速实现网络应用。
    • io 包:提供了对 I/O 操作的统一抽象,如 io.ReadAll 能简化从流中读取数据的操作。

总结

  1. 这段代码是串行处理每个 URL,但 Go 内置的并发机制(goroutine、channel)可以很方便地改造此程序
  2. 为何在操作结束后需要关闭流(I/O流:文件流,网络流)
    • 操作系统为每个进程分配的文件描述符和网络连接数量是有限的。如果程序中频繁打开流而不关闭,长时间运行后会耗尽这些资源,导致后续无法打开新的文件或建立新的网络连接。
    • 不及时关闭流会导致资源泄露(Resource Leak),即占用的内存和其他系统资源不能被其他部分程序或其他程序使用。这种泄露会降低程序的性能,甚至引发系统崩溃或不稳定。
    • 某些流在写操作时会进行缓冲,如果不调用关闭操作,缓冲区中的数据可能没有及时刷新到磁盘或发送到网络端,可能破坏数据完整性。通过关闭流,系统会自动刷新缓冲区,确保所有数据都已经正确写入或传输。
    • 文件在打开时可能被操作系统或其他程序加锁,防止数据被同时修改。关闭文件流可以释放这些锁定,使其他进程能够正常访问文件,保障系统的安全性和数据的一致性。
  3. 缓冲区
    • 定义:在程序设计中,缓冲区是一个抽象概念,用来描述数据暂存的位置。它帮助平滑数据流的传输,比如在从磁盘读取数据或向网络发送数据时,不必每次都进行低效的逐字节操作,而是先把数据存入缓冲区,再统一处理。

题目

练习 1.7: 函数调用io.Copy(dst, src)会从src中读取内容,并将读到的结果写入到dst中,使用这个函数替代掉例子中的ioutil.ReadAll来拷贝响应结构体到os.Stdout,避免申请一个缓冲区(例子中的b)来存储。记得处理io.Copy返回结果中的错误。

// 使用 io.Copy 将响应体直接写入标准输出,避免申请额外的缓冲区
_, err = io.Copy(os.Stdout, resp.Body)
  • 调用 io.Copy 函数,将数据从 src(这里是 resp.Body)流式复制到 dst(这里是 os.Stdout
    • io.Copy 的工作原理是创建一个固定大小(比如32KB)的缓冲区,在循环中不断地从源(src)读取一块数据,然后立即写入目标(dst)。这样,只需要维持这块缓冲区的内存,而不必为整个数据内容分配一大块连续内存区域。
    • 分块,chunk
  • 对于大文件或长响应数据,直接拷贝可以减少内存占用。流式处理:不需要一次性把所有数据加载到内存中,有助于处理大数据流。

练习 1.8: 修改fetch这个范例,如果输入的url参数没有 http:// 前缀的话,为这个url加上该前缀。你可能会用到strings.HasPrefix这个函数。

  	// 如果 URL 没有 "http://" 前缀,则自动添加if !strings.HasPrefix(url, "http://") {url = "http://" + url}

练习 1.9: 修改fetch打印出HTTP协议的状态码,可以从resp.Status变量得到该状态码。

// 打印出 HTTP 协议的状态码
fmt.Fprintf(os.Stdout, "HTTP status: %s\n", resp.Status)
文章来源:https://blog.csdn.net/Pyroyster/article/details/146215816
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ppmy.cn/embedded/173486.html

相关文章

软著申请流程图

从准备申报材料开始,这是申请的基础步骤。随后,需要提交计算机软件著作权的申请,其中包括软件的说明、源代码以及登记申请表和登记申请信息表等相关文档。这些材料准备好后,由申请人或代理人负责提交登记申请材料。 接下来&#…

蓝桥杯备赛-贪心-区间合并

题目描述 已知有 N 个区间,每个区间的范围是 [si​,ti​],请求出区间覆盖后的总长。 输入格式 第一行一个正整数 N,表示区间个数。 接下来 N 行,每行两个正整数,表示 si​ 和 ti​。 输出格式 共一行&#xff0c…

【Leetcode 每日一题】3110. 字符串的分数

问题背景 给你一个字符串 s s s。一个字符串的 分数 定义为相邻字符 ASCII 码差值绝对值的和。 请你返回 s s s 的 分数 。 数据约束 2 ≤ s . l e n g t h ≤ 100 2 \le s.length \le 100 2≤s.length≤100 s s s 只包含小写英文字母。 解题过程 按题目要求遍历累加就可…

树莓科技集团董事长:第五代产业园运营模式的深度剖析与展望​

第五代产业园运营模式,以创新为核心驱动,强调数字化、网络化和资源整合。树莓科技集团在这一领域具有代表性,其运营模式值得深入剖析。 核心特征 数字化转型:第五代产业园高度重视数字化技术的应用,通过构建数字化平…

CSS中绝对定位

1.如何设置绝对定位? 给元素设置postition: absolute 即可实现绝对定位 可以使用left,right,top,bottom四个属性调整位置 2.绝对定位的参考点在哪里? 参考他的包含块. 什么是包含块? 1.对于没有脱离文档流的元素:包含块就是父元素; 2.对于脱离文档流的元素:包含块是第一个拥…

uniapp-x 子组件样式覆盖

不支持scoped 默认不支持scoped,所以写也没用 那如果我想修改子孙节点的样式是不是很方便,不需要v-deep了? 的确如此 自带页面样式隔离 在 uni-app x 中,不支持 css scoped,样式的作用范围遵循以下规则:…

Springboot+mybait查询功能撰写

我们写查询首先要考虑&#xff0c;我们需不需要传值&#xff0c;返回些什么&#xff1f;想哈~我们首先前端不需要传入任何东西过来&#xff0c;所以我们方法后面不需要写任何的参数&#xff01;其次&#xff0c;我们要返回什么回去&#xff1f;我们肯定要返回一个List<user&…

【无标题】ffmpeg 合并文件夹下所有视频

(Get-Content "F:\33333333333333\1146523396\videos.txt") | ForEach-Object { "file $_" } | Set-Content "F:\33333333333333\1146523396\videos.txt"这个会把目录下的所有MP4视频文件按文件名写到一个文件。 powershell ffmpeg -f concat -…