Go语言的TCP和HTTP网络服务基础

news2024/9/20 12:33:46

目录

【TCP Socket 编程模型】

Socket读操作

【HTTP网络服务】

HTTP客户端

HTTP服务端


TCP/IP 网络模型实现了两种传输层协议:TCP 和 UDP,其中TCP 是面向连接的流协议,为通信的两端提供稳定可靠的数据传输服务;UDP 提供了一种无需建立连接就可以发送数据包的方法。实现网络编程,不仅可以基于应用层协议的HTTP,也可以直接基于传输层暴露给用户的网络编程接口:Socket(套接字)。socket是一种 IPC 方法。

IPC 是 Inter-Process Communication 的缩写,可以被翻译为进程间通信,主要定义的是多个进程之间相互通信的方法。这些方法主要包括:系统信号(signal)、管道(pipe)、套接字 (socket)、文件锁(file lock)、消息队列(message queue)、信号量(semaphore)等。

关于TCP和UDP以及HTTP的相关术语可以参考:HTTP协议中的响应码和实体数据

【TCP Socket 编程模型】

网络 I/O 模型指的是 应用线程与操作系统内核之间的交互模式,常用的有 阻塞 I/O(Blocking)、非阻塞 I/O(Non-Blocking)、I/O 多路复用(I/O Multiplexing)。

  • 阻塞I/O模型:内核一直等到全部数据就绪才返回给发起系统调用的应用线程。一个线程仅能处理一个网络连接上的数据通信,即便连接上没有数据,线程也只能阻塞在对 Socket 的读操作上。虽然这个模型对应用整体来说是低效的,但却是最容易实现和使用的,所以各大平台在默认情况下都将 Socket 设置为阻塞的。
  • 非阻塞I/O模型:内核查看数据就绪状态后,即便没有就绪也立即返回错误给发起系统调用的应用线程。位于用户空间的 I/O 请求发起者会通过轮询的方式,去一次次发起 I/O请求,直到读到所需的数据为止。不过这样的轮询会浪费很多CPU计算资源,因此非阻塞 I/O 模型单独应用于实际生产的比例并不高。
  • I/O多路复用模型:一个应用线程可以同时处理多个 Socket,并且由内核实现可读/可写事件的通知,避免了非阻塞模型中轮询导致的CPU计算资源浪费的问题。

Go语言的socket服务端程序通常采用一个 Goroutine 处理一个连接,主要关键词是Listen和Accept,实现方式如下:

package main

import (
	"fmt"
	"net"
)

func handleConn(c net.Conn) {
	defer c.Close()
	for {
		//...
	}
}

func main() {
	//使用net包的Listen函数绑定服务器端口8888,并将它转换为监听状态
	l, err := net.Listen("tcp", ":8888")
	if err != nil {
		fmt.Println("listen error:", err)
		return
	}
	for {
		//Listen返回成功后,这个服务会进入一个循环,调用net.Listener 的 Accept 方法接收新客户端连接
		//在没有新连接的时候,这个服务会阻塞在 Accept 调用上,直到有客户端连接上来,Accept 方法将返回一个 net.Conn 实例,用于和新连上的客户端进行通信。
		c, err := l.Accept()
		if err != nil {
			fmt.Println("accept error:", err)
			break
		}

		//这个服务程序启动了一个新 Goroutine,并将 net.Conn 传给这个 Goroutine,这样这个Goroutine 就专门负责处理与这个客户端的通信了。
		go handleConn(c)
	}
}

运行上面的代码后,使用  netstat -an|grep 8888 可以查看端口的监听情况:

当服务端按照上面的Listen + Accept结构成功启动后,客户端就可以使用 net.Dial net.DialTimeout 向服务端发起建立连接的请求。net.Dial函数会接受两个参数,分别名为network和address,都是string类型的。

参数network常用的可选值一共有 9 个,分别代表了程序底层创建的 socket 实例可使用的不同通信协议:

  • "tcp":代表 TCP 协议,其基于的 IP 协议的版本根据参数address的值自适应。
  • "tcp4":代表基于 IP 协议第四版的 TCP 协议。
  • "tcp6":代表基于 IP 协议第六版的 TCP 协议。
  • "udp":代表 UDP 协议,其基于的 IP 协议的版本根据参数address的值自适应。
  • "udp4":代表基于 IP 协议第四版的 UDP 协议。
  • "udp6":代表基于 IP 协议第六版的 UDP 协议。
  • "unix":代表 Unix 通信域下的一种内部 socket 协议,以 SOCK_STREAM 为 socket 类型。
  • "unixgram":代表 Unix 通信域下的一种内部 socket 协议,以 SOCK_DGRAM 为 socket 类型。
  • "unixpacket":代表 Unix 通信域下的一种内部 socket 协议,以 SOCK_SEQPACKET 为 socket 类型。
//Dial 函数向服务端发起 TCP 连接,这个函数会一直阻塞,直到连接成功或失败后,才会返回。
conn, err := net.Dial("tcp", "localhost:8888")
//DialTimeout 带有超时机制,如果连接耗时大于超时时间,这个函数会返回超时错误。
conn, err := net.DialTimeout("tcp", "localhost:8888", 2 * time.Second)

客户端建立连接的几种特殊情况:

  • 如果传给Dial的服务端地址是网络不可达,或者服务地址中端口对应的服务并没有启动,端口未被监听(Listen),Dial会立即返回这样的错误:dial error: dial tcp :8888: getsockopt: connection refused
  • 当对方服务器很忙,瞬间有大量客户端尝试向服务端建立连接时,服务端可能会出现 listen backlog 队列满,接收连接(accept)不及时的情况,这就会导致客户端的Dial调用阻塞,直
    到服务端进行一次 accept,从 backlog 队列中腾出一个槽位,客户端的 Dial 才会返回成功。如果服务端一直不执行accept操作,那么客户端会阻塞大约1分钟左右才会返回超时错误:dial error: dial tcp :8888: getsockopt: operation timed out
  • 如果网络延迟较大,TCP的三次握手过程会经历各种丢包,时间消耗也会更长,这种情况下Dial函数会阻塞。如果经过长时间阻塞后依旧无法建立连接,那么Dial也会返回 getsockopt: operation timed out的错误。

当客户端调用 Dial 成功,就会在客户端与服务端之间建立起一条通信通道,双方通过各自获得的 Socket可以向对方发送数据包,也可以接收来自对方的数据包,双方都会为已建立的连接分配一个发送缓冲区和一个接收缓冲区。

Socket读操作

开发人员只需要采用 Goroutine + 阻塞 I/O 模型,就可以满足大部分场景需求。Dial 连接成功后,会返回一个 net.Conn 接口类型的变量值,这个接口变量的底层类型为一个 *TCPConn,TCPConn“继承”了conn类型的Read和Write方法,后续通过Dial函数返回值调用的Read和Write方法都是net.conn 的方法,它们分别代表了对 socket 的读和写。

//$GOROOT/src/net/tcpsock.go
type TCPConn struct {
    conn //这里的conn是一个非导出类型,封装了底层的 socket
}

 Go 从 socket 读取数据的几种场景:

  • Socket 中无数据:连接建立后,如果客户端未发送数据,服务端会阻塞在 Socket 的读操作上,执行这个操作的 Goroutine 也会被挂起。Go 运行时会监视这个 Socket,直到它有数据读事件才会重新调度这个 Socket 对应的 Goroutine 完成读操作。
  • Socket 中有部分数据:如果 Socket 中有部分数据就绪,且数据数量小于一次读操作期望读出的数据长度,那么读操作将会成功读出这部分数据并返回,而不是等待期望长度数据全部读取后再返回。
  • Socket 中有足够数据:如果连接上有数据,且数据长度大于等于一次Read操作期望读出的数据长度,那么Read将会成功读出这部分数据并返回。
  • 设置读操作超时:有些场合对 socket 的读操作的阻塞时间有严格限制的,但由于 Go 使用的是阻塞 I/O 模型,如果没有可读数据,Read 操作会一直阻塞在对 Socket 的读操作上。这时可以通过 net.Conn 提供的 SetReadDeadline 方法,设置读操作的超时时间,当超时后仍然没有数据可读的情况下,Read 操作会解除阻塞并返回超时错误,这就给 Read 方法的调用者提供了处理其他业务逻辑的机会。

 下面代码是结合 SetReadDeadline 设置的服务端一般处理逻辑:

func handleConn(c net.Conn) {
	defer c.Close()
	for {
		//从连接中读取数据
		var buf = make([]byte, 128)
		//SetReadDeadline 方法接收一个绝对时间作为超时的 deadline,一旦设置了,
		//那么无论后续的Read操作是否超时,后面与这个socket有关的所有读操作都会返回超时失败错误。
		//如果要取消超时设置,可以使用 SetReadDeadline(time.Time{})
		c.SetReadDeadline(time.Now().Add(time.Second))
		n, err := c.Read(buf)
		if err != nil {
			fmt.Printf("conn read %d bytes, error: %s", n, err)
			if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
				// 进行其他业务逻辑的处理
				continue
			}
			return
		}
		fmt.Printf("read %d bytes, content is %s\n", n, string(buf[:n]))
	}
}

【HTTP网络服务】

HTTP客户端

HTTP 协议是基于 TCP/IP 协议的,如果只是访问基于 HTTP 协议的网络服务,那么使用net/http代码包中的程序实体会更加便捷。比如使用 http.Get函数获取一个HTTP请求的返回信息,在调用它的时候只需要传给它一个 URL 就可以了:

func testGet() {
	url1 := "https://www.baidu.com"
	//http.Get函数会返回两个结果值:第一个结果值的类型是*http.Response,表示网络服务传回来的响应内容的结构化表示;
	//第二个结果值是error类型的,表示在创建和发送HTTP请求以及接收和解析HTTP响应的过程中可能发生的错误。
	resp1, err := http.Get(url1)
	if err != nil {
		fmt.Printf("请求发送失败: %v\n", err)
	}
	defer resp1.Body.Close()
	line1 := resp1.Proto + " " + resp1.Status
	fmt.Printf("返回内容:%s\n", line1) //HTTP/1.1 200 OK
}
func main() {
	testGet()
}

HTTP服务端

http.Server类型是基于 HTTP 协议的服务端,其中ListenAndServe方法的功能是:监听一个基于 TCP 协议的网络地址,并对接收到的 HTTP 请求进行处理。该方法会一直执行,直到有严重的错误发生或者被外界关掉。

func httpServer1() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "用户名: %s", r.URL.Path[1:])
	})
	http.ListenAndServe(":8080", nil)
}
func main() {
	httpServer1()
}

启动后,浏览器输入 http://localhost:8080/zhangsan

源代码:https://gitee.com/rxbook/go-demo-2023/tree/master/basic/go04/net

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

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

相关文章

NodeJSMongodbMVC管理开发⑨

文章目录 ✨文章有误请指正,如果觉得对你有用,请点三连一波,蟹蟹支持😘前言MVC思想开发 服务器代码演示 M层 Services 或 Model V层 Views C层 Controllers总结 ✨文章有误请指正,如果觉得对你有用&#xff0c…

C语言笔记之结构体总结

C语言笔记之结构体总结 code review! 文章目录 C语言笔记之结构体总结一.介绍二.3种结构体类型变量说明1. 先定义结构,再定义结构变量2. 定义结构体类型的同时说明变量3. 直接说明结构变量(匿名结构体) 四.结构体成员表示方法五.结构体指针做参数六.结构体初始化1…

阵列信号处理笔记(2):均匀线阵、均匀加权线阵、波束方向图

阵列信号处理笔记(2) 文章目录 阵列信号处理笔记(2)均匀线阵(Uniform Linear Array)均匀加权线阵波束方向图的关键参数附polardb.m用来计算HPBW的Mathematica代码,以及用于拟合的数据拟合的MATL…

二、DSMP/OLS等夜间灯光数据贫困地区识别——MPI和灯光指数计算

一、前言 其实在计算MPI和灯光指数之前,最重要是DMSP/OLS等夜间灯光指数的校正还有就是MPI计算,那么校正分为DMSP/OLS和NPP/VIIRS夜间灯光数据,DMSP/OLS夜间灯光数据校正主要采取不变目标区域法原理进行校正,当前对其有很多优化后的做法,但是万变不离其宗,核心思想还是没…

LeetCode257. 二叉树的所有路径

写在前面: 题目链接:LeetCode257. 二叉树的所有路径 题目难度:简单 编程语言:C 一、题目描述 给你一个二叉树的根节点 root ,按 任意顺序 ,返回所有从根节点到叶子节点的路径。 叶子节点 是指没有子节点的…

阿里P8架构师手码的Java工程师面试小抄在Github火了,完整版限时开源

网上的 JAVA 面试文档更是层出不穷。但是单单刷 JAVA 面试题就足够了吗? 答案显然是不够的!那么为什么呢? 因为现在的程序员就业环境早就和两年前不可同日而语了。 如果你在两年前面试: 就拿 JVM 来说,刷面试题可能…

Springboot+vue.js+协同过滤推荐+余弦相似度算法实现新闻推荐系统

Springbootvue.js协同过滤推荐余弦相似度算法实现新闻推荐系统 - 简书 针对海量的新闻资讯数据,如何快速的根据用户的检索需要,完成符合用户阅读需求的新闻资讯推荐?本篇文章主要采用余弦相似度及基于用户协同过滤算法实现新闻推荐&#xff0…

SpringMvc详解

SpringMvc用来代替展示层Servlet,均属于Web层开发技术 Servlet是如何工作的 1、导入Servlet依赖坐标 2、创建一个Servlet接口实现类,重写其中的所有方法 3、在Servlet实现类上加上WebServlet注解,用来配置Servlet访问路径 4、启动Tomca…

Misc(6)

隐藏的钥匙 还是一样挨个试,详细信息里没有发现flag信息,就用010打开试试 文件头正常,暂时没有发现隐藏文件 喝,多搜搜还是有收获的,一开始以为是有隐藏文件就利用ctrlf在010中进行搜索 分别搜索了txt和rar文件都没有反…

VLAN基础知识2_VLAN二层通信原理

目录 1.VLAN二层通信简介 2.同一交换机VLAN二层通信 3.跨交换机VLAN二层通信 4.VLAN二层通信实验 4.1 VLAN基本配置命令 4.2 VLAN配置步骤 4.3 VLAN二层通信实验效果 1.VLAN二层通信简介 VLAN是一个逻辑上的分组,可以将一个物理网络端口分割成多个虚拟网络&…

C++初始化列表详解

写在前面: 初始化列表是一种用于初始化成员变量的语法结构,它可以在类的构造函数中使用,用于初始化类的成员变量。 而 列表初始化指的是 是一种初始化变量的简洁方式,可以用花括号{}来表示。列表初始化可以用于各种类型的变量&…

某农业大学c/c++第五次实验(类和对象)

1.Time时间类 【问题描述】 设计一个Time类,并设计多个重载的构造函数,可以设置时间、进行时间的加减运算、按12小时格式和24小时格式输出时间。 例如: 其中构造函数Time::Time(int s)根据总秒数计算hour、minute、second并构造对象&#x…

C语言,BMP格式解析,生成不同位深的图片。

0.前言 目录 0.前言 1.BMP格式详解 1.1图片的构成 1.2BMP格式 1.2.1文件头 1.2.2位图信息头 1.2.3调色板 1.2.4位图数据 2.生成 3.总结 最近工作任务繁重且对我来说小有难度,但是没有困难的事情做起来怎么有收获呢? 今天推荐一本书《寻找光…

Windows10环境下载安装Oracle19c教程

Windows10环境下载安装Oracle19c教程 前言1. 官网下载2. 运行setup.exe2.1 解压缩2.2 报错解决 3.正式安装3.1 配置选项3.2 系统类3.3 oracle主目录用户3.4 安装位置3.5 先决条件检查3.6 安装产品3.7 安装完成3.8 完全卸载oracle(非必备) 4. 安装验证4.1 CMD验证4.2 SQL Plus4.…

本周大新闻|Quest游戏订阅服务曝光;平价版苹果XR头显25年推出

本周XR大新闻,AR方面,彭博苹果预计25年推出更亲民头显;Vision Pro外屏含微透镜;WWDC23苹果高管脱口秀爆料更多细节;外媒爆料称Vision Pro预期销量改为15万台;VitreaLab展示RGB激光背光技术。还是提前说一下…

leetcode刷题之字符串相关问题

344.反转字符串 方法一:找中间结点,头尾翻转 var reverseString function(s) {let len s.lengthlet mid Math.floor((s.length - 1) / 2) //向下取整 如果长度是奇数,那么mid是最中间的结点 如果长度是偶数,那么mid是中间两个…

【Java技术专题】「攻破技术盲区」带你攻破你很可能存在的Java技术盲点之动态性技术原理指南(反射技术专题)

带你攻破你很可能存在的Java技术盲点之动态性技术原理指南 带你攻破你很可能存在的Java技术盲点之动态性技术原理指南编程语言的类型静态类型语言动态类型语言 技术核心方向反射API反射案例介绍反射功能操作获取构造器长度可变的参数 - 构造方法使用反射 API 获取参数长度可变的…

【软件测试】

系列文章目录 文章目录 系列文章目录前言第四章 单元测试4.1 软件测试过程概述4.2 什么是单元测试4.2.1 单元测试的定义4.2.2 单元测试的重要性4.2.3 单元测试原则 4.3 单元测试的目标和任务4.3.1 单元测试的目标:单元模块被正确编码4.3.2 单元测试的主要任务 4.4 单…

FreeRTOS:事件标志组

目录 一、事件标志组简介1.1事件位(事件标志)1.2事件组1.3事件标志组和事件位的数据类型 二、创建事件标志组2.1函数 xEventGroupCreate()2.2函数xEventGroupCreateStatic() 三、设置事件位3.1函数 xEventGroupClearBits()3.2函数xEventGroupClearBitsFromISR()3.3函数 xEventG…

Python模块MarkupPy 自定义html报告

简介 MarkupPy是Python模块用于生成HTML和XML格式的字符串。它的主要作用是提供了一种比原生HTML/XML更加易读和易写的编写方式,通过Python代码来生成HTML或XML代码。 使用MarkupPy,可以在Python中使用不同的对象类型和方法,来动态地生成HTML…