GO语言基础笔记(七):网络编程

目录

Go语言网络协议基础   

协议

实现

跨平台网络抽象

简单代码展示

服务端

客户端

服务端客户端通信实战

Go Linux服务端

Go Linux客户端

Windows C++ 客户端

总结 


Go语言网络协议基础   

        在 Go 语言中,net/http 包提供了强大的工具来创建 HTTP 服务器。以下是创建基本服务器的步骤:

  net/http 包是 Go 语言用于网络编程,特别是用于构建和处理 HTTP 和 HTTPS 协议的应用程序的标准库。我们来探讨一下它的实现方式和它所遵循的协议。

协议

  1. HTTP(超文本传输协议)net/http 包最主要的功能是支持 HTTP,这是一种应用层协议,用于分布式、协作性和超媒体信息系统。HTTP 是一个无状态的请求-响应协议,通常运行在 TCP/IP 协议之上。

  2. HTTPS(安全的 HTTP):HTTPS 是 HTTP 的安全版本,它在传输层使用 SSL/TLS 协议来提供加密通信和安全的身份认证。net/http 包通过内置的 crypto/tls 包支持 HTTPS。

实现

  1. HTTP 服务器

    • net/http 包通过提供 http.ListenAndServe 函数来启动 HTTP 服务器。这个函数内部创建了一个 net.Listener,通常是一个 TCP 监听器,用于监听传入的 HTTP 请求。
    • 当接收到 HTTP 请求时,它将请求分派给注册的处理函数(使用 http.HandleFunchttp.Handle 注册)。
    • 这些处理函数接收 http.ResponseWriterhttp.Request 对象,用于构造响应和解析请求。
  2. HTTP 客户端

    • net/http 包提供了一个默认的客户端(http.DefaultClient),该客户端使用 http.Transport 来管理 HTTP 请求的底层细节。
    • http.Transport 管理连接池,处理请求的发送,以及接收响应。
    • 它通过 TCP 连接发送 HTTP 请求,并接收响应。对于 HTTPS,它还负责处理 TLS 握手过程。
  3. Request 和 Response 处理

    • HTTP 请求和响应都被抽象为 http.Requesthttp.Response 类型。
    • 这些类型提供了丰富的方法和字段,用于访问和修改 HTTP 请求和响应的各个部分,如 URL、头部、主体等。
  4. 扩展性和灵活性

    • net/http 包设计灵活,易于扩展。开发者可以自定义处理函数、中间件、客户端的行为等。
    • 它还允许开发者替换或增强底层的传输机制,例如通过实现自定义的 http.RoundTripper

跨平台网络抽象

  1. 网络 I/Onet/http 包的底层网络 I/O 操作(如 TCP 连接)主要依赖于 Go 语言的 net 包。net 包提供了一个平台独立的接口来处理网络 I/O 操作。

  2. Go 调度器:Go 的运行时包括一个高效的调度器,用于调度 Go 程程(goroutines)。这个调度器是独立于操作系统线程的,但会与之交互,以高效地利用多核心处理器。网络 I/O 操作通常在 Go 程程中异步执行。

简单代码展示

服务端


import ("net/http"
)//创建处理函数
func handler(w http.ResponseWriter, r *http.Request) {w.Write([]byte("Hello, 世界"))
}//注册处理函数
http.HandleFunc("/", handler)//启动
http.ListenAndServe(":8080", nil)

客户端

//发送请求
resp, err := http.Get("http://localhost:8080")
if err != nil {// 处理错误
}
defer resp.Body.Close()//读取响应
body, err := ioutil.ReadAll(resp.Body)
if err != nil {// 处理错误
}
fmt.Println(string(body))

 

服务端客户端通信实战

Go Linux服务端

package mainimport ("encoding/json""fmt""io/ioutil""log""net/http""strings"
)// Message 结构体用于 JSON 响应和请求
type Message struct {Text string `json:"text"`
}func main() {http.HandleFunc("/echo", echoHandler) // 处理 /echo 路径http.HandleFunc("/post", postHandler) // 处理 /post 路径fmt.Println("服务器启动在 http://localhost:8080")log.Fatal(http.ListenAndServe(":8080", nil)) // 启动服务器
}// echoHandler 用于回应客户端发送的消息
func echoHandler(w http.ResponseWriter, r *http.Request) {// 只接受 GET 请求if r.Method != http.MethodGet {http.Error(w, "只支持 GET 请求", http.StatusMethodNotAllowed)return}message := r.URL.Query().Get("message")response := Message{Text: message}jsonResponse, err := json.Marshal(response)if err != nil {http.Error(w, err.Error(), http.StatusInternalServerError)return}w.Header().Set("Content-Type", "application/json")w.Write(jsonResponse)
}// postHandler 接受 JSON 数据并返回
func postHandler(w http.ResponseWriter, r *http.Request) {// 只接受 POST 请求if r.Method != http.MethodPost {http.Error(w, "只支持 POST 请求", http.StatusMethodNotAllowed)fmt.Println("ERROR 1")return}var message Messagebody, err := ioutil.ReadAll(r.Body)if err != nil {http.Error(w, "无法读取 body", http.StatusBadRequest)fmt.Println("ERROR 2")return}defer r.Body.Close()var cleanedJSON stringjsonString := string(body)if !isValidJSON(jsonString) {cleanedJSON = removeInvalidChars(jsonString)}err = json.Unmarshal([]byte(cleanedJSON), &message)if err != nil {http.Error(w, "无法解析 JSON", http.StatusBadRequest)fmt.Println("ERROR 3")return}jsonResponse, err := json.Marshal(message)if err != nil {http.Error(w, err.Error(), http.StatusInternalServerError)fmt.Println("ERROR 4")return}w.Header().Set("Content-Type", "application/json")w.Write(jsonResponse)
}func isValidJSON(jsonString string) bool {// 检查JSON字符串是否包含特殊字符return strings.IndexFunc(jsonString, func(r rune) bool {return r < 32 || r >= 127}) == -1
}func removeInvalidChars(jsonString string) string {var validChars []runefor _, r := range jsonString {if r >= 32 && r < 127 {validChars = append(validChars, r)}}return string(validChars)
}

解释一下 http.HandleFunc 的工作原理:

  1. 函数签名http.HandleFunc 需要两个参数:一个字符串(表示 URL 路径)和一个处理函数。这个处理函数必须符合特定的签名:它接受一个 http.ResponseWriter 和一个 *http.Request 作为参数。

  2. 函数引用:在 http.HandleFunc("/echo", echoHandler) 中,echoHandler 是一个函数引用,而不是一个函数调用。这意味着我们传递的是函数本身,而不是执行该函数的结果。

  3. 延迟执行:当服务器运行并接收到路径为 /echo 的 HTTP 请求时,net/http 包的内部机制会调用 echoHandler 函数,并且为它提供必要的 http.ResponseWriter*http.Request 参数。这是在请求发生时发生的,而不是在设置路由时。

  4. 回调机制:可以把 echoHandler 理解为一个回调函数。在编程中,回调函数是在特定事件或条件满足时由另一个函数调用的函数。在这种情况下,事件是对 /echo 路径的 HTTP 请求。

因此,当你在 http.HandleFunc 中看到 echoHandler 没有传递参数,这是因为你只是在注册一个当特定 HTTP 请求到来时应该被调用的函数,而实际的参数传递是在请求处理时由 net/http 包自动处理的。

        这里我们加入json解析的可能错误处理,处理来自windows客户端的json格式错误问题。然后让windows客户端来访问Linux上go写的服务。

Go Linux客户端

        需要配置好服务端设定的IP、端口、访问接口等。还有向URL发字符串时特殊字符例如空格、&、¥、%这些如何处理的问题。

package mainimport ("bytes""encoding/json""fmt""io/ioutil""net/http"
)type Message struct {Text string `json:"text"`
}func main() {getResponse, err := http.Get("http://localhost:8080/echo?message=Hello%2C%20Go%21")if err != nil {panic(err)}defer getResponse.Body.Close()body, err := ioutil.ReadAll(getResponse.Body)if err != nil {panic(err)}fmt.Println("GET Response:", string(body))message := Message{Text: "Hi from Client"}jsonRequest, err := json.Marshal(message)if err != nil {panic(err)}postResponse, err := http.Post("http://localhost:8080/post", "application/json", bytes.NewBuffer(jsonRequest))if err != nil {panic(err)}defer postResponse.Body.Close()body, err = ioutil.ReadAll(postResponse.Body)if err != nil {panic(err)}fmt.Println("POST Response:", string(body))
}

        这里打一个Get请求和一个Post请求,经测试,Linux本地没问题。

Windows C++ 客户端

        windows客户端开100线程取while true的访问,看看服务器能否顶住。

#include <windows.h>
#include <vector>
#include <winhttp.h>
#include <iostream>
#include <string>
#include <sstream>
#include <thread>#pragma comment(lib, "winhttp.lib")std::string SendRequest(const std::string& serverName, const std::string& apiPath, bool isPost, const std::string& postData = "") {std::stringstream responseStream;HINTERNET hSession = WinHttpOpen(L"A WinHTTP Example Program/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);if (!hSession) {responseStream << "WinHttpOpen failed with error: " << GetLastError();return responseStream.str();}std::wstring wServerName = std::wstring(serverName.begin(), serverName.end());std::wstring wApiPath = std::wstring(apiPath.begin(), apiPath.end());HINTERNET hConnect = WinHttpConnect(hSession, wServerName.c_str(), 8080, 0);if (!hConnect) {responseStream << "WinHttpConnect failed with error: " << GetLastError();WinHttpCloseHandle(hSession);return responseStream.str();}LPCWSTR method = isPost ? L"POST" : L"GET";HINTERNET hRequest = WinHttpOpenRequest(hConnect, method, wApiPath.c_str(), NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0);if (!hRequest) {responseStream << "WinHttpOpenRequest failed with error: " << GetLastError();WinHttpCloseHandle(hConnect);WinHttpCloseHandle(hSession);return responseStream.str();}BOOL bResults = FALSE;std::wstring wPostData = std::wstring(postData.begin(), postData.end());if (isPost) {bResults = WinHttpSendRequest(hRequest, L"Content-Type: application/json", -1, (LPVOID)wPostData.c_str(), wPostData.size() * sizeof(wchar_t), wPostData.size() * sizeof(wchar_t), 0);}else {bResults = WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0);}if (!bResults) {responseStream << "WinHttpSendRequest failed with error: " << GetLastError();}else {bResults = WinHttpReceiveResponse(hRequest, NULL);DWORD dwSize = 0;DWORD dwDownloaded = 0;LPSTR pszOutBuffer;if (bResults) {do {dwSize = 0;if (!WinHttpQueryDataAvailable(hRequest, &dwSize)) {responseStream << "WinHttpQueryDataAvailable failed with error: " << GetLastError();break;}pszOutBuffer = new char[dwSize + 1];if (!pszOutBuffer) {responseStream << "Out of memory";dwSize = 0;break;}else {ZeroMemory(pszOutBuffer, dwSize + 1);if (!WinHttpReadData(hRequest, (LPVOID)pszOutBuffer, dwSize, &dwDownloaded)) {responseStream << "WinHttpReadData failed with error: " << GetLastError();}else {responseStream.write(pszOutBuffer, dwDownloaded);}delete[] pszOutBuffer;}} while (dwSize > 0);}}if (hRequest) WinHttpCloseHandle(hRequest);if (hConnect) WinHttpCloseHandle(hConnect);if (hSession) WinHttpCloseHandle(hSession);return responseStream.str();
}std::string serverName = "192.168.125.104";
std::string getApiPath = "/echo?message=Hello%2C%20Go%21";
std::string postApiPath = "/post";
std::string postData = "{\"text\":\"HifromClient\"}";void myfunc_work() {while (1){std::string getResponse = SendRequest(serverName, getApiPath, false);std::string postResponse = SendRequest(serverName, postApiPath, true, postData);std::cout << "GET Response: " << getResponse << std::endl;std::cout << "POST Response: " << postResponse << std::endl;}
}int main() {std::vector<std::thread> thvec;for (int i = 0; i < 100; i++){thvec.push_back(std::thread(myfunc_work));}for (int i = 0; i < 10; i++){thvec[i].join();}return 0;
}

        实际上肯定不是100个线程同时访问,我的服务器时intel N100的低功耗芯片,Go语言编写的服务能顶住。没有错误出现,都返回成功了  32G的服务器服务内存占比40左右:

        多线程访问图:

        Linux服务器cpu占用:

 

 

总结 

        简单的服务端与客户端通信,检验了go语言高并发的恐怖。

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

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

相关文章

新产品推广选品牌外包广州迅腾文化传播多渠道传播能力

在当今激烈的市场竞争中&#xff0c;新产品推广已成为企业发展的关键。选择具备多渠道传播能力的品牌外包服务提供商&#xff0c;有助于快速提升品牌知名度和市场占有率。作为行业领先者&#xff0c;迅腾文化凭借卓越的多渠道传播能力&#xff0c;成为企业新产品推广的理想合作…

我的512天创作者纪念日总结:高效、高现

文章目录 512天创作者纪念日&#xff1a;2023年的12月31日CSDN的512天消息提醒第一篇文章&#xff0c;最后一篇文章总计847篇文章&#xff0c;每月发文分布512天&#xff0c;各专栏文章统计512天&#xff0c;互动总成绩 512天创作者纪念日&#xff1a;2023年的12月31日 2023年…

微服务(1)

目录 1.什么是微服务&#xff1f;谈谈你对微服务的理解&#xff1f; 2.什么是Spring Cloud&#xff1f; 3.Springcloud中的组件有哪些&#xff1f; 3.具体说说SpringCloud主要项目&#xff1f; 5.SpringCloud项目部署架构&#xff1f; 1.什么是微服务&#xff1f;谈谈你对微…

前端开发新趋势:Web3、区块链与虚拟现实

文章目录 Web3&#xff1a;下一代互联网区块链技术去中心化应用程序&#xff08;DApps&#xff09; 区块链&#xff1a;重塑数字世界数字钱包NFT&#xff08;非同质化代币&#xff09; 虚拟现实&#xff1a;沉浸式体验WebVR和WebXR三维图形 新挑战与机会性能与复杂性安全性创新…

【网络面试(2)】DNS原理-域名和IP地址的查询转换

从上一篇博客我们得知浏览器是如何生成了HTTP消息了&#xff0c;但是浏览器作为应用程序&#xff0c;是不具备向网络中发送请求的能力&#xff0c;而是需要委托给操作系统的内核协议栈来发送请求。在委托协议栈之前&#xff0c;浏览器还要做的一件事情就是将域名转换为IP地址。…

lambda表达式和包装器

正文开始前给大家推荐个网站&#xff0c;前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 我们在使用库里的排序算法时如果排序的是自定义类型或者库里默认的排序不能满足我们则需求&…

Apache DolphinScheduler 3.1.9 版本发布:提升系统的稳定性和性能

&#x1f680;我们很高兴宣布&#xff0c;Apache DolphinScheduler 的最新版本 3.1.9 已正式发布&#xff01;此版本在 3.1.8 的基础上进行了关键的 bug 修复和文档更新&#xff0c;共计修复了 14 个 bug 和改进了 3 个文档。 主要更新亮点 本次更新重点解决了以下几个关键问题…

磁盘阵列raid

一、服务器硬件 cpu 、 主板 、内存、硬盘、网卡、电源、raid卡、风扇、远程管理卡 二、硬盘尺寸 目前生产环境中主流的两种类型硬盘 3.5寸 和 2.5寸 硬盘 2.5寸硬盘可以通过使用硬盘托架后适用于3.5寸硬盘的服务器&#xff0c;但是3.5寸没法转换成2.5寸 三、服务器常见故…

[每周一更]-(第43期):Golang版本的升级历程

从1.13接触go语言开始更新我们公司内第一个Go项目&#xff0c;直至现在go版本已经发展到1.20&#xff08;20230428&#xff09;&#xff0c;我们从go发版开始认识go语言&#xff0c;有利于我们更深入 了解这门语言&#xff0c;洞悉一些深层方式&#xff0c;加深我们学习的动力&…

看懂基本的电路原理图(入门)

文章目录 前言一、二极管二、电容三、接地一般符号四、晶体振荡器五、各种符号的含义六、查看原理图的顺序总结 前言 电子入门&#xff0c;怎么看原理图&#xff0c;各个图标都代表什么含义&#xff0c;今天好好来汇总一下。 就比如这个电路原理图来说&#xff0c;各个符号都…

JDBC->SpringJDBC->Mybatis封装JDBC

一、JDBC介绍 Java数据库连接&#xff0c;&#xff08;Java Database Connectivity&#xff0c;简称JDBC&#xff09;是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口&#xff0c;提供了诸如查询和更新数据库中数据的方法。JDBC也是Sun Microsystems的商标。我们…

03 团队研究进一步详细介绍

一、印第安纳大学邢璐祎课题组 【团队网站】 https://www.xing-luyi.com/ 【团队介绍】 研究以形式化方法为特色&#xff0c;并保证系统中的安全性和隐私合规性&#xff0c;特别是物联网、云、移动和软件供应链。 【团队成果汇总】 物联网系统&#xff1a;[Oakland24][Se…

合伙企业法关于合伙企业的要求

合伙协议可以载明合伙企业的经营期限和合伙人争议的解决方式。 合伙协议经全体合伙人签名、盖章后生效。合伙人依照合伙协议享有权利&#xff0c;承担责任。 经全体合伙人协商一致&#xff0c;可以修改或者补充合伙协议。 申请合伙企业设立登记&#xff0c;应当向企业登记机关提…

TecoGAN视频超分辨率算法

1. 摘要 对抗训练在单图像超分辨率任务中非常成功&#xff0c;因为它可以获得逼真、高度细致的输出结果。因此&#xff0c;当前最优的视频超分辨率方法仍然支持较简单的范数&#xff08;如 L2&#xff09;作为对抗损失函数。直接向量范数作损失函数求平均的本质可以轻松带来时…

Linux自定义shell编写

Linux自定义shell编写 一.最终版本展示1.动图展示2.代码展示 二.具体步骤1.打印提示符2.解析命令行3.分析是否是内建命令1.shell对于内建名令的处理2.cd命令3.cd函数的实现4.echo命令的实现5.export命令的实现6.内建命令函数的实现 4.创建子进程通过程序替换执行命令5.循环往复…

微软开源,全平台通用:Shell 自动补全工具 | 开源日报 No.132

microsoft/inshellisense Stars: 7.6k License: MIT inshellisense 是一个为 Shell 提供 IDE 风格自动补全的工具。它是一个终端本地运行时自动完成&#xff0c;支持 600 多个命令行工具&#xff0c;并且可以在 Windows、Linux 和 macOS 上使用。主要功能包括安装后可通过运行…

30 UVM Adder Testbench Example

1 Adder Design 加法器设计在时钟的上升沿产生两个变量的加法。复位信号用于clear out信号。注&#xff1a;加法器可以很容易地用组合逻辑开发。引入时钟和重置&#xff0c;使其具有测试台代码中时钟和重置的样子/风格。 module adder(input clk, reset, input [7:0] in1, in…

【操作系统】处理机调度

文章目录 一. 实验目的二. 实验内容三. 实验步骤四. 实验结果五. 实验总结附&#xff1a;系列文章 一. 实验目的 &#xff08;1&#xff09;加深对进程概念的理解&#xff0c;明确进程和程序的区别 &#xff08;2&#xff09;深入理解系统如何组织进程 &#xff08;3&#xff…

Frappe Charts:数据可视化的强大工具

一、产品简介&#xff1a; 一个简单、零依赖、响应式的 开源SVG 图表库。这个图表库无论是数据更新还是屏幕大小变化&#xff0c;都能快速响应并更新图表。数据生成和悬停查看都有舒服的交互动效&#xff0c;体验感很好。不仅支持配置颜色&#xff0c;外观定制也很方便。还支持…

最新ChatGPT网站AI系统源码,附详细搭建教程/支持GPT4.0/AI绘画/GPT语言对话/DALL-E3文生图/自定义知识库

一、前言 SparkAi创作系统是基于ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI创作Ch…