Go网络通信

Go中HTTP协议客户端实现

Go语言标准库内置了net/http包,涵盖了HTTP客户端和服务端具体的实现方式。内置的net/http包提供了最简洁的HTTP客户端实现方式,无须借助第三方网络通信库,就可以直接使用HTTP中用得最多的GET和POST方式请求数据。


func HttpClientA() {
	//创建一个客户端
	client := http.Client{}
	//创建一个请求
	request, err := http.NewRequest("GET", "https://cn.bing.com/", nil)
	checkErr(err) //检查err防止崩溃--匿名函数--闭包
	//cookie信息
	cookieInfo := &http.Cookie{
		Name:  "userName",
		Value: "Jobs",
	}
	//添加cookie
	request.AddCookie(cookieInfo)
	//发送请求--返回响应结果
	response, err := client.Do(request)
	checkErr(err)
	//设置请求头
	request.Header.Set("Accept-Language", "zh-cn")
	defer response.Body.Close()
	fmt.Println("header--", request.Header)
	fmt.Println("状态码--", response.StatusCode)
	if response.StatusCode == 200 {
		fmt.Println("请求成功")
		checkErr(err)
	} else {
		fmt.Println("请求失败", response.StatusCode)
	}
}

// 异常检查
func checkErr(err error) {
	defer func() {
		//程序可能会返回error
		if msg, ok := recover().(error); ok {
			fmt.Println("出现异常--", msg.Error())

		}
	}()
	if err != nil {
		panic(err)
	}
}

func main() {
	HttpClientA()
	httpB()
}

func httpB() {
	client := http.Client{}

	resp, err := client.Get("https://www.runoob.com/go/go-environment.html")
	checkErr(err)
	if resp.StatusCode == 200 {
		fmt.Println("请求成功--菜鸟")
		//
		defer resp.Body.Close()// Close ensures that the body has been fully read
	}

}

聚合代码Demo

func HttpClientA() {
	//创建一个客户端
	client := http.Client{}
	//创建一个请求
	request, err := http.NewRequest("GET", "https://cn.bing.com/", nil)
	checkErr(err) //检查err防止崩溃--匿名函数--闭包
	//cookie信息
	cookieInfo := &http.Cookie{
		Name:  "userName",
		Value: "Jobs",
	}
	//添加cookie
	request.AddCookie(cookieInfo)
	//发送请求--返回响应结果
	response, err := client.Do(request)
	checkErr(err)
	//设置请求头
	request.Header.Set("Accept-Language", "zh-cn")
	defer response.Body.Close()
	fmt.Println("header--", request.Header)
	fmt.Println("状态码--", response.StatusCode)
	if response.StatusCode == 200 {
		fmt.Println("请求成功")
		checkErr(err)
	} else {
		fmt.Println("请求失败", response.StatusCode)
	}
}

// 异常检查
func checkErr(err error) {
	defer func() {
		//程序可能会返回error
		if msg, ok := recover().(error); ok {
			fmt.Println("出现异常--", msg.Error())

		}
	}()
	if err != nil {
		panic(err)
	}
}

func main() {
	HttpClientA()
	httpB()
	httpP()
	httpC()
	httpPostM()
	PostM()
}

func httpB() {
	client := http.Client{}

	resp, err := client.Get("https://www.runoob.com/go/go-environment.html")
	checkErr(err)
	if resp.StatusCode == 200 {
		fmt.Println("请求成功--菜鸟")
		//
		defer resp.Body.Close() // Close ensures that the body has been fully read
	}

}

func httpP() {
	client := http.Client{}
	resp, err := client.Get("http://localhost:7000/showmsg")

	if err != nil {
		return
	}
	if resp.StatusCode == 200 {
		fmt.Println("请求成功")
		bytes := make([]byte, 1024)
		resp.Body.Read(bytes)
		fmt.Println("返回的信息--", string(bytes))
	}
	defer resp.Body.Close()
}

func httpC() {
	client := http.Client{}

	request, err := http.NewRequest("GET", "http://localhost:7000/showmsg", nil)
	checkError(err)
	cookie := &http.Cookie{
		Name:  "cookie",
		Value: "Steve Jobs",
	}
	request.AddCookie(cookie)
	request.Header.Set("key", "value")
	response, err := client.Do(request)
	checkError(err)
	if response.StatusCode == 200 {
		fmt.Println("请求成功--")

		fmt.Println("header--", response.Header)
	}
	defer response.Body.Close()
}
func checkError(err error) {
	if err != nil {
		panic(err) //如果请求失败,则抛异常
	}
	//处理异常
	defer func() {
		if msg, ok := recover().(error); ok {
			fmt.Println("请求出现错误--错误信息", msg.Error())
		}
	}()
}

// post请求
func httpPostM() {

	client := http.Client{}
	//发送的数据
	m := make(url.Values)
	m["name"] = []string{"小明"}
	m["age"] = []string{"18"}
	resp, err := client.PostForm("http://localhost:7000/getStu", m)
	checkError(err)
	if resp.StatusCode == 200 {
		bytes := make([]byte, 1024)
		resp.Body.Read(bytes)
		fmt.Println(string(bytes))
	}
	defer resp.Body.Close()
}

func PostM() {
	values := url.Values{
		"name": {"小花", "小红"},
	}
	//将参数转化成body
	reader := strings.NewReader(values.Encode())
	resp, err := http.Post("http://localhost:7000/getStu", "application/x-www-form-urlencoded", reader)
	checkError(err)
	if resp.StatusCode == 200 {
		fmt.Println("请求成功啦")
		bytes := make([]byte, 64)
		resp.Body.Read(bytes)

		fmt.Println(string(bytes))
	}
	defer resp.Body.Close()

}

java项目结构–
在这里插入图片描述

Http协议服务端实现

实现服务端

实现HTTP服务端就是能够启动Web服务,相当于搭建起了一个Web服务器。这样客户端就可以通过网页请求来与服务器端进行交互。

使用http.FileServer()

这种方式只提供静态的文件访问;

使用ListenAndServe(addr string, handler Handler) 启动web服务,绑定监听的http端口,

第二个参数表示提供文件访问服务的HTTP处理器Handler

type Handler interface {
	ServeHTTP(ResponseWriter, *Request)
}

Handler是一个接口,接口中有ServeHTTP(ResponseWriter, *Request),只要实现了这个方法就实现了 Handler接口

FileServer 返回一个handler,
FileServer()的参数是FileSystem接口,可使用http.Dir()来指定服务端文件所在的路径。如果该路径中有index.html文件,则会优先显示html文件,否则会显示文件目录。
代码Demo

// FileServer returns a handler that serves HTTP requests
// with the contents of the file system rooted at root.
//
// As a special case, the returned file server redirects any request
// ending in "/index.html" to the same path, without the final
// "index.html".
//
// To use the operating system's file system implementation,
// use http.Dir:
//
//	http.Handle("/", http.FileServer(http.Dir("/tmp")))
//
// To use an fs.FS implementation, use http.FS to convert it:
//
//	http.Handle("/", http.FileServer(http.FS(fsys)))
func FileServer(root FileSystem) Handler {
	return &fileHandler{root}
}

开启一个简单的服务端

http.ListenAndServe


func ServerA() {
	//如果路径里有index.html,则会优先显示这个,否则会显示文件目录
	http.ListenAndServe(":7000", http.FileServer(http.Dir("./mkdir")))
}


func ServerB() {
	server := http.FileServer(http.Dir("./")) //Dir实现了FileSystem
	//addr如果为"" 则使用默认的  localhost 或127.0.0.1 不带端口号
	err := http.ListenAndServe("", server)
	//err := http.ListenAndServe("127.0.0.1:8000", server)
	if err != nil {
		fmt.Println("请求失败")
	}
}


ListenAndServe的第一个参数是监听的端口
第二个参数是Handler即处理请求的Handler

http.FileServer()返回的是一个Handler,当然我们也可以自己实现Handler;

请求:localhost:7000

在这里插入图片描述

ServeHTTP()方法有两个参数,其中第一个参数是ResponseWriter类型,包含了服务器端给客户端的响应数据。服务器端往ResponseWriter写入了什么内容,浏览器的网页源码就是什么内容。第二个参数是一个 *Request指针,包含了客户端发送给服务器端的请求信息(路径、浏览器类型等)。

路由功能的实现:

路由功能只需要重写http.HandleFunc,其核心的处理逻辑依旧是ServeHTTP()即实现Handler接口;

使用http.HandleFunc()方法
代码Demo

func main() {
	//绑定路径去触发方法
	//注册网络服务的路由
	//底层是:DefaultServeMux.HandleFunc(pattern, handler)
	http.HandleFunc("/index", func(writer http.ResponseWriter, request *http.Request) {
		if request.Method == "GET" {
			writer.WriteHeader(200)
			byt := []byte{'1', '2'}
			writer.Write(byt)
		}

	})
	//http.HandleFunc("/index",redir)
	err := http.ListenAndServe("localhost:7001", nil)
	checkError(err)
}

如果http.ListenAndServer()的第二个参数为nil,那么表示服务端采用默认的http.DefaultServeMux进行分发处理。
所以我们可以通过自定义ServeMux来实现自定义转发逻辑;

使用http.NewServeMux()方法

http. NewServeMux ()的作用是注册网络访问的多路路由。因为它采用的是自定义的多路由分发任务方式,所以称之为自定义多路由分发服务。

源码:

func NewServeMux() *ServeMux { return new(ServeMux) }

ServeMux是一个结构体

type ServeMux struct {
	mu    sync.RWMutex
	m     map[string]muxEntry
	es    []muxEntry // slice of entries sorted from longest to shortest.
	hosts bool       // whether any patterns contain hostnames
}

服务器端获取客户端请求数据

就是启动一个服务端,监听一个addr,重写Handler的ServeHTTP(ResponseWriter, *Request),这里loginActionHandler参数跟ServeHTTP()参数ResponseWriter, *Request一致

get请求参数


// 判断get请求参数
// ResponseWriter, *Request
func loginActionHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Println("路径", r.URL.Host)
	bytes := make([]byte, 100)
	r.Body.Read(bytes)
	fmt.Println("body--", string(bytes))
	r.Header.Set("header", "v")
	println(r.ProtoMajor)
	println(r.ProtoMinor)
	if r.Method == "GET" && r.ParseForm() == nil {
		username := r.FormValue("username")
		pwd := r.FormValue("pwd")
		fmt.Println(username)
		fmt.Println(pwd)
		if len(username) < 4 || len(username) > 10 {
			w.Write([]byte("用户名不合法"))

		}
		if len(pwd) < 6 || len(pwd) > 16 {
			w.Write([]byte("密码不符合规范"))

		}
		http.Redirect(w, r, "/list", http.StatusFound)
		return
	} else {
		w.Write([]byte("请求方式不对"))
		return
	}
	w.Write([]byte("登陆失败"))
}

func main() {

	http.HandleFunc("/login", loginActionHandler)
	http.ListenAndServe("localhost:7000", nil)

}


这样请求携带过来的信息 可以通过*Request 带过来—

header
在这里插入图片描述

post方式传递的参数

post传参分为以下几种情况:

post传表单信息—Content-Type=application/x-www-form-urlencoded。

传文件—Content-Type=multipart/form-data。

func posthand(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	//如果是post请求
	if r.Method == "POST" && r.ParseForm() == nil {
		username := r.PostFormValue("username")
		pwd := r.PostForm.Get("pwd")
		fmt.Println(username)
		fmt.Println(pwd)
		bytes := make([]byte, 8)
		rand.NewSource(time.Now().UnixMilli())
		for i := 0; i < len(bytes); i++ {
			bytes[i] = byte(rand.Intn(10))
		}
		encrypt, _ := Encrypt(pwd, bytes)

		//保存登录信息
		logininfo := username + ":" + encrypt
		fmt.Println(logininfo)
		cookie := &http.Cookie{
			Name:  "logininfo",
			Value: logininfo,
		}
		r.AddCookie(cookie)
		http.Redirect(w, r, "http://localhost:7001/userinfo?username="+username+"&pwd="+pwd, 302)
		return
	} else {
		w.Write([]byte("请求方式错误"))
		return
	}
	w.Write([]byte("注册失败"))
}

func ZeroPadding(ciphertext []byte, blockSize int) []byte {
	padding := blockSize - len(ciphertext)%blockSize
	padtext := bytes.Repeat([]byte{0}, padding)
	return append(ciphertext, padtext...)
}

func ZeroUnPadding(origData []byte) []byte {
	return bytes.TrimFunc(origData,
		func(r rune) bool {
			return r == rune(0)
		})
}

//加密

func Encrypt(text string, key []byte) (string, error) {
	src := []byte(text)
	block, err := des.NewCipher(key)
	if err != nil {
		return "", err
	}
	bs := block.BlockSize()
	src = ZeroPadding(src, bs)
	if len(src)%bs != 0 {
		return "", errors.New("Need a multiple of the blocksize")
	}
	out := make([]byte, len(src))
	dst := out
	for len(src) > 0 {
		block.Encrypt(dst, src[:bs])
		src = src[bs:]
		dst = dst[bs:]
	}
	return hex.EncodeToString(out), nil
}

//解密

func Decrypt(decrypted string, key []byte) (string, error) {
	src, err := hex.DecodeString(decrypted)
	if err != nil {
		return "", err
	}
	block, err := des.NewCipher(key)
	if err != nil {
		return "", err
	}
	out := make([]byte, len(src))
	dst := out
	bs := block.BlockSize()
	if len(src)%bs != 0 {
		return "", errors.New("crypto/cipher: input not full blocks")
	}
	for len(src) > 0 {
		block.Decrypt(dst, src[:bs])
		src = src[bs:]
		dst = dst[bs:]
	}
	out = ZeroUnPadding(out)
	return string(out), nil
}
func main() {
	http.HandleFunc("/login", posthand)
	http.ListenAndServe("localhost:7000", nil)

}

未完待续

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Python】内置函数

文章目录 反射相关【4】基础数据类型相关【38】和数字相关&#xff08;14&#xff09;数据类型 <4>bool([x])int((x, base10)float([x])complex([real[, imag]]) 进制转换 <3>bin(x)oct(x)hex(x) 数学运算&#xff08;7&#xff09;abs(x)divmod(a, b)round(x [, n…

vue 阻止事件冒泡常用的方法

在 Vue 中&#xff0c;阻止事件冒泡有两种常用方法&#xff1a; 1. 使用 event.stopPropagation() 方法&#xff1a; 在事件处理函数中&#xff0c;可以通过调用事件对象的 stopPropagation() 方法来阻止事件冒泡。例如&#xff1a; html <template> <div click"…

00后太卷了,搞的我们这些老油条太难受了......

前几天我们公司一下子也来了几个新人&#xff0c;这些年前人是真能熬啊&#xff0c;本来我们几个老油子都是每天稍微加会班就打算走了&#xff0c;这几个新人一直不走&#xff0c;搞得我们也不好走。 2023年春招结束了&#xff0c;最近内卷严重&#xff0c;各种跳槽裁员&#x…

报表控件FastReport使用指南——使用NuGet包创建PDF文档

FastReport 是功能齐全的报表控件&#xff0c;可以帮助开发者可以快速并高效地为.NET&#xff0c;VCL&#xff0c;COM&#xff0c;ActiveX应用程序添加报表支持&#xff0c;由于其独特的编程原则&#xff0c;现在已经成为了Delphi平台最优秀的报表控件&#xff0c;支持将编程开…

多层网关已成过去,网关多合一成潮流,网关改造正当时丨Higress 正式发布 1.0 版本

作者&#xff1a;Higress 团队 01 前言 K8s 通过 Ingress / Gateway API 将网关标准化&#xff0c;逐步将安全网关、流量网关、微服务网关内聚&#xff0c;解决从单体到微服务到云原生多层网关的复杂度&#xff0c;合久必分&#xff0c;分久必合&#xff0c;多层网关已成过去…

(3)NUC980 kenerl编译

解压 用到的配置文件位置&#xff1a; /NUC980-linux-4.4.y-master/arch/arm/configs/nuc980_defconfig 执行&#xff1a; 编译linux内核源码。了解其 配置文件在 arch/arm/configs/nuc980_defconfig (1) make nuc980_defconfig 载入配置文件 (2) make menuconfig --->Devi…

SAP MM 根据采购订单反查采购申请

如何通过采购订单号查询到其前端的采购申请号。 首先从采购申请的相关报表着手&#xff0c;比如ME5A, 发现它是可以满足需求的。 例如&#xff1a;如下的采购订单&#xff0c; 该订单是由采购申请10003364转过来的。 如果想通过这个采购订单找到对应的采购申请&#xff0c;在…

Python3中goto的用法

Python3代码指定跳转可以使用goto这个库&#xff1a; 安装&#xff1a; pip install goto-statement 一般安装的版本是1.2 需要做以下修改才能正常使用&#xff1a; python 使用goto&#xff0c;遇到的问题解决_奶嘴偷走初吻的博客-CSDN博客python goto 出现报错:Attribut…

情绪管理ABC法

情绪管理ABC法 是由著名心理学家艾利斯&#xff08;Albert Ellis&#xff09;提出的一种情绪管理方法。 模型介绍 情绪&#xff0c;不取决于发生的事实&#xff0c;取决于我们如何看待这件事ABC理论认为&#xff0c;我们的情绪©&#xff0c;其实与发生的事件(A)无关&…

类的成员之:构造器(构造方法)

1.构造器的特征&#xff1a; 它具有与类相同的名称它不声明返回值类型。&#xff08;与声明为void不同&#xff09;不能被static、final、synchronized、abstract、native修饰&#xff0c;不能有return语句返回值 2.构造器的作用&#xff1a; 1.创建对象2.初始化对象的…

PID算法在流量控制中的应用

目录 增量式或位置式 目录 增量式或位置式 PID控制周期 T1 时间 T2 约4ms PID C代码 最近有小伙伴向我提问关于PID的问题&#xff1a;通过比例阀控制水流速度&#xff08;流量&#xff09;&#xff0c; 使用增量式还是位置式 PID&#xff1f;他的比例法驱动频率是500Hz…

linux环境搭建

&#x1f388;个人主页:&#x1f388; :✨✨✨初阶牛✨✨✨ &#x1f43b;推荐专栏: &#x1f354;&#x1f35f;&#x1f32f;C语言进阶 &#x1f511;个人信条: &#x1f335;知行合一 &#x1f349;本篇简介:>:介绍学习如何使用云服务器搭建Linux的环境. 前言 linux介绍…

【计算机网络中ip概念总结】【平时我们说的ip 到底是什么】【计算机网络中 ip地址是什么】

专注 效率 记忆 预习 笔记 复习 做题 欢迎观看我的博客&#xff0c;如有问题交流&#xff0c;欢迎评论区留言&#xff0c;一定尽快回复&#xff01;&#xff08;大家可以去看我的专栏&#xff0c;是所有文章的目录&#xff09;   文章字体风格&#xff1a; 红色文字表示&#…

Linux系统之编译安装python3

Linux系统之编译安装python3 一、python3介绍1. python3简介2. python3特点 二、检查本地环境1. 检查本地操作系统版本2. 检查内核版本3. 检查当前python版本 三、安装前准备工作四、下载python最新版本源码包1. 访问python官网2. 创建下载目录3. 下载python源码包4. 解压pytho…

Redis 常见面试题

1. 认识Redis Redis是一个开源的内存数据结构存储&#xff0c;Redis是一个基于内存的数据库&#xff0c;对数据的读写都在内存中完成&#xff0c;因此数据读写速度非常快&#xff0c;常用于缓存&#xff0c;分布式锁等&#xff0c;MySQL的表数据都存储在 t_order.ibd&#xff…

国内可以免费使用的GPT

一、wetab新标签页 教程&#xff1a;https://diwlwltzssn.feishu.cn/docx/MnHhdvxATomBnMxfas2cm8wWnVd 装GPT界面&#xff1a;https://microsoftedge.microsoft.com/addons/detail/wetab%E5%85%8D%E8%B4%B9chatgpt%E6%96%B0%E6%A0%87%E7%AD%BE%E9%A1%B5/bpelnogcookhocnaokfp…

华为OD机试真题(Java),跳跃游戏 II(100%通过+复盘思路)

一、题目描述 给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。 每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i + j] 处: 0 <= j <= nums[i]0i + j <返回到达 nums[n - 1] 的最小跳跃…

C++学习笔记(四): 类、头文件、对象

一个类定义了一个类型&#xff0c;以及与其关联的一组操作。所谓类&#xff0c;是用户自定义的数据类型。 类机制是C最重要的特性之一。实际上&#xff0c;C最初的一个设计焦点就是能定义使用上像内置类型一样自然的类类型&#xff08;class type&#xff09;。 类的定义一般分…

Java之旅(三)

Java 输出&#xff1a;println()、print() 使用 println() 方法在 Java 中输出值或打印文本。 System.out.println("Hello World!"); println() 可以根据需要添加任意数量的方法。请注意&#xff0c;它将为每个方法添加一个新行&#xff1a; System.out.println(&…

Unity2D骨骼动画制作之单张图片编辑

1、打开骨骼制作面板 在Sprite Editor左侧选项&#xff0c;选择Skinning Editor 2、 &#xff08;1&#xff09;骨骼制作 Preview Pose 预览模式&#xff0c;可以预览动作并不会真正的改变设置 Reset Pose 将角色骨骼和关节恢复到原始位置 Edit Bone 编辑骨骼&#xff0c;…