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

这篇具有很好参考价值的文章主要介绍了Go语言的TCP和HTTP网络服务基础。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

【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 tcp 服务,Golang,HTTP,tcp/ip,golang,网络,网络协议,http

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 可以查看端口的监听情况:

go tcp 服务,Golang,HTTP,tcp/ip,golang,网络,网络协议,http

当服务端按照上面的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可以向对方发送数据包,也可以接收来自对方的数据包,双方都会为已建立的连接分配一个发送缓冲区和一个接收缓冲区。

go tcp 服务,Golang,HTTP,tcp/ip,golang,网络,网络协议,http

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

go tcp 服务,Golang,HTTP,tcp/ip,golang,网络,网络协议,http

源代码:https://gitee.com/rxbook/go-demo-2023/tree/master/basic/go04/net文章来源地址https://www.toymoban.com/news/detail-659492.html

到了这里,关于Go语言的TCP和HTTP网络服务基础的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Go语言网络编程(socket编程)TCP粘包

    服务端代码如下: 客户端代码如下: 将上面的代码保存后,分别编译。先启动服务端再启动客户端,可以看到服务端输出结果如下: 收到client发来的数据: Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you? 收到client发来的数

    2024年02月09日
    浏览(58)
  • Go语言的网络编程与TCP_IP

    Go语言是一种现代的编程语言,由Google的Robert Griesemer、Rob Pike和Ken Thompson在2009年开发。Go语言的设计目标是简单、高效、可扩展和易于使用。它具有弱类型、垃圾回收、并发性和原生支持的网络编程。Go语言的网络编程是其强大功能之一,它使得开发者可以轻松地编写高性能的

    2024年02月22日
    浏览(69)
  • Go语言实战:网络编程与TCP_UDP

    Go语言是一种现代的编程语言,由Google的Robert Griesemer、Rob Pike和Ken Thompson于2009年开发。Go语言的设计目标是简单、高效、可扩展和易于使用。它具有垃圾回收、类型安全、并发性能等优点。Go语言的网络编程库 net 包提供了TCP/UDP的实现,使得开发者可以轻松地编写网络应用程序

    2024年02月21日
    浏览(59)
  • C++网络编程 TCP套接字基础知识,利用TCP套接字实现客户端-服务端通信

    流式套接字编程针对TCP协议通信,即是面向对象的通信,分为服务端和客户端两部分。 1)加载套接字库( 使用函数WSAStartup() ),创建套接字( 使用socket() ) 2)绑定套接字到一个IP地址和一个端口上( 使用函数bind() ) 3)将套接字设置为监听模式等待连接请求( 使用函数

    2024年02月03日
    浏览(55)
  • 【Unity】Socket网络通信(TCP) - 最基础的C#服务端通信流程

    我这里新建了一个C#控制台项目来写服务端代码。 下面是基于C# Socket的最基础服务端通信流程: 创建服务端Socket对象 绑定IP地址和端口 设置最大监听客户端数量 等待客户端连接 收发消息 释放连接 基于上面流程就能实现一个最简单并且能和客户端通信的服务器程序,每个步

    2023年04月16日
    浏览(60)
  • 多进程并发TCP服务器模型(含客户端)(网络编程 C语言实现)

    摘要 :大家都知道不同pc间的通信需要用到套接字sockte来实现,但是服务器一次只能收到一个客户端发来的消息,所以为了能让服务器可以接收多个客户端的连接与消息的传递,我们就引入了多进程并发这样一个概念。听名字就可以知道--需要用到进程,当然也有多线程并发

    2024年02月17日
    浏览(60)
  • UE5 独立程序的网络TCP/UDP服务器与客户端基础流程

    引擎源码版,复制EngineSourcePrograms路径下的BlankProgram空项目示例。 重命名BlankProgram,例如CustomTcpProgram,并修改项目名称。  修改.Build.cs内容 修改Target.cs内容     修改Private文件夹内.h.cpp文件名并修改.cpp内容 刷新引擎 在项目.Build.cs中加入模块  .cpp 客户端.cpp 未完待续。。

    2024年01月17日
    浏览(56)
  • 网络字节序——TCP接口及其实现简单TCP服务器

    简单TCP服务器的实现 TCP区别于UDP在于要设置套接字为监控状态,即TCP是面向链接,因此TCP套接字需要设置为监听状态 socket函数原型 domain 表示协议族,常用的有 AF_INET (IPv4)和 AF_INET6 (IPv6)。 type 表示Socket类型,常用的有 SOCK_STREAM (TCP)和 SOCK_DGRAM (UDP)。 protocol 通常可

    2024年02月10日
    浏览(43)
  • Go语言实现TCP通信

    TCP协议为 传输控制协议 ,TCP协议有以下几个特点: 1. TCP是面向连接的传输层协议; 2. 每条TCP连接只能有两个端点,每条TCP连接是点到点的通信; 3. TCP提供可靠的交付服务,保证传送的数据无差错,不丢失,不重要且有序; 4. TCP提供全双工通信,允许双方在任何时候都能发送

    2023年04月22日
    浏览(32)
  • Go语言TCP Socket编程

    Golang的主要 设计目标之一就是面向大规模后端服务程序,网络通信这块是服务端 程序必不可少也是至关重要的一部分。在日常应用中,我们也可以看到Go中的net以及其subdirectories下的包均是“高频+刚需”,而TCP socket则是网络编程的主流,即便您没有直接使用到net中有关TCP

    2024年02月15日
    浏览(32)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包