高并发应用:TCP网络编程

这篇具有很好参考价值的文章主要介绍了高并发应用:TCP网络编程。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

高并发应用:TCP网络编程

Go网络编程

高并发采集tcp数据,网络,tcp/ip,服务器,golang

什么是非阻塞IO

高并发采集tcp数据,网络,tcp/ip,服务器,golang
高并发采集tcp数据,网络,tcp/ip,服务器,golang

Socket

  • 很多系统都提供Socket作为TCP网络连接的抽象
  • Linux-> internet domain socket -> SOCK_STREAM
  • Linux中Socket以“文件描述符”FD作为标识。
    高并发采集tcp数据,网络,tcp/ip,服务器,golang
    高并发采集tcp数据,网络,tcp/ip,服务器,golang

IO模型

  • IO模型指的是同时操作Socket的方案。
  • 阻塞
  • 非阻塞
  • 多路复用

阻塞IO

高并发采集tcp数据,网络,tcp/ip,服务器,golang

  • 同步读写Socket时,线程陷入内核态。
  • 当读写成功后,切换回用户态,继续执行。
  • 优点:开发难度小,代码简单。
  • 缺点:内核态切换开销大。

非阻塞IO

高并发采集tcp数据,网络,tcp/ip,服务器,golang

  • 如果暂时无法收发数据,会返回错误。
  • 应用会不断轮询,直到Socket可以读写。
  • 优点:不会陷入内核态,自由度高。
  • 缺点:需要自旋轮询。

多路复用-Linux epoll

高并发采集tcp数据,网络,tcp/ip,服务器,golang

  • 注册多个Socket事件
  • 调用epool,当有事件发生,返回
  • 优点:提供了事件列表,不需要查询各个Socket。
  • 缺点:开发难度大,逻辑复杂。
  • Linu:epoll; Mac:Kqueue; Windows:IOCP

高并发采集tcp数据,网络,tcp/ip,服务器,golang

Go是如何抽象Epoll的?

阻塞模型+多路复用

  • 在底层使用操作系统的多路复用IO
  • 在协程层次使用阻塞模型
  • 阻塞协程时,休眠协程。
    高并发采集tcp数据,网络,tcp/ip,服务器,golang

epoll抽象层

  • epoll抽象层是为了统一各个操作系统度多路复用器的实现。
    高并发采集tcp数据,网络,tcp/ip,服务器,golang
    高并发采集tcp数据,网络,tcp/ip,服务器,golang
netpollinit()新建多路复用器
  • 新建Epoll。
  • 新建一个pipe管道用于中断Epoll。
  • 将“管道有数据到达”事件注册在Epoll中。
netpollopen()插入事件
  • 传入一个Socket的FD,和pollDesc指针。
  • pollDesc指针是Socket相关详细信息。
  • pollDesc中记录了哪个协程休眠在等待此Socket。
  • 将Socket可读、可写、断开事件注册到Epoll中。
netpoll()查询发生了什么事件
  • 调用epoll_wait(),查询有哪些事件发生。
  • 根据Socket相关的pollDesc信息,返回哪些协程可以唤醒。
    高并发采集tcp数据,网络,tcp/ip,服务器,golang

Network Poller是如何工作的

NetWork Poller初始化

  • poll_runtime_pollServerInit()。
  • 使用原子操作保证只初始化一次。
  • 调用netpollinit()

pollcache与pollDesc

  • pollcache:一个带锁的链表头。
  • pollDesc:链表的成员。
  • pollDesc是runtime包对Socket的详细描述。
  • rg, wg:1,或2,或等待的协程G的地址。

NetWork Poller新增监听Socket

  • poll_runtime_pollOpen()
  • 在pollcache链表中分配一个pollDesc。
  • 初始化pollDesc(rg wg为0)
  • 调用netpollopen()

Network Poller收发数据

收发数据分为两个场景

  • 协程需要收发数据时,Socket已经可读写。
  • 协程需要收发数据时,Socket暂时无法读写。

场景1:Socket已经可读写

  • runtime会循环调用netpoll()方法(g0协程)
  • 发现Socket可读写时,给对应的rg或者wg置为pdReady(1)
  • 协程调用poll_runtime_pollWait()
  • 判断rg或者wg已经置为pdReady(1),返回0.

场景2:Socket暂时无法读写

  • runtime循环调用netpoll()方法(g0协程)
  • 协程调用poll_runtime_pollWait()
  • 发现对应的rg或者wg为0
  • 给对应的rg或者wg置为协程地址
  • 休眠等待
  • runtime循环调用netpoll()方法(g0协程)
  • 发现Socket可读写时,给对应的查看对应的rg或者wg
  • 若为协程地址,返回协程地址。
  • 调度器开始调度对应协程

总结

  • Network Poller是runtime的强大工具
  • 抽象了多路复用器的操作
  • Network Poller可以自动监测多个Socket状态
  • 在Socket状态可用时,快速返回成功。
  • 在Socket状态不可用时,休眠等待。

Go是如何抽象Socket的?

net包

  • net包是Go原生的网络包
  • net包实现了TCP、UDP、HTTP等网络操作。

net.Listen()

  • 新建Socket,并执行bind操作
  • 新建一个FD(net包对Socket的详情描述)
  • 返回一个TCPListener对象
  • 将TCPListener的FD信息加入监听。
  • TCPListener对象本质上是一个LISTEN状态的Socket。

TCPListener.Accept()

  • 直接调用Socket的accept()
  • 如果失败,休眠等待新的连接。
  • 将新的Socket包装为TCPConn变量返回
  • 将TCPConn的FD信息加入监听
  • TCPConn本质上是一个ESTABLISHED状态的Socket。

TCPConn.Read() / Write()

  • 直接调用Socket原生读写方法。
  • 如果失败,休眠等待可读/可写。
  • 被唤醒后调用系统Socket。

总结

  • net包抽象了TCP网络操作。
  • 使用net.Listen()得到TCPListener(LISTEN状态的Socket)
  • 使用TCPListener.Accept()得到TCPConn(ESTABLISHED)
  • TCPConn.Read() / Writer()进行读写Socket的操作。
  • Network Poller作为上述功能的底层支撑。

实战:怎样结合阻塞模型和多路复用?

思路

  • 用主协程监听Listener
  • 每个Conn使用一个新协程处理

goroutine-per-connection编程风格

/*
实现一个简单的TCP Server
*/

func ListenAndServe(address string) {
	// 绑定监听地址
	listen, err := net.Listen("tcp", address)
	if err != nil {
		log.Fatal(fmt.Sprintf("listen err: %v", err))
	}
	defer listen.Close()
	log.Println(fmt.Sprintf("bind: %s, start listening...", address))

	for true {
		// Accept会一直阻塞直到有新的连接建立或者Listen中断才返回
		conn, err := listen.Accept()
		if err != nil {
			// 通常是由于listen被关闭导致无法继续监听导致的错误
			log.Fatal(fmt.Sprintf("accept err: %v", err))
		}
		// 开启新的Goroutine处理该连接
		go Handle(conn)
	}
}

func Handle(conn net.Conn) {
	// 使用bufio标准库提供的缓冲区功能
	reader := bufio.NewReader(conn)
	for {
		// ReadString 会一直阻塞直到遇到分隔符'\n'
		// 遇到分隔符会返回分隔符或连接建立后收到的所有数据,包括分隔符本身
		// 若在遇到分隔符之前遇到异常,ReadString会返回已收到的数据和错误信息
		msg, err := reader.ReadString('\n')
		if err != nil {
			// 通常遇到的错误是连接中断或被关闭,用io.EOF表示
			if err == io.EOF {
				log.Println("connection close")
			} else {
				log.Println(err)
			}
			return
		}
		b := []byte(msg)
		// 将收到的信息发送给客户端
		conn.Write(b)
	}
}

func main() {
	ListenAndServe(":8080")
}
运行结果

高并发采集tcp数据,网络,tcp/ip,服务器,golang文章来源地址https://www.toymoban.com/news/detail-839897.html

2024/02/13 00:21:08 bind: :8080, start listening...
2024/02/13 00:23:49 connection close
2024/02/13 00:23:55 connection close

到了这里,关于高并发应用:TCP网络编程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 多进程并发TCP服务器模型(含客户端)(网络编程 C语言实现)

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

    2024年02月17日
    浏览(60)
  • 网络编程套接字应用分享【Linux &C/C++ 】【UDP应用 | TCP应用 | TCP&线程池小项目】

    目录 前提知识 1. 理解源ip,目的ip和Macip 2. 端口号 3. 初识TCP,UDP协议 4. 网络字节序 5. socket 编程 sockaddr类型  一,基于udp协议编程  1. socket——创建套接字 2. bind——将套接字强绑定  3. recvfrom——接受数据 4. sendto——发出信息  遇到的问题 (1. 云服务器中以及无法分配I

    2024年04月08日
    浏览(86)
  • 初学记录【linux应用】 TCP/UDP 网络编程 C语言

    以下内容分别为TCP 与 UDP编程,内容有相似或者重合部分,可根据流程 相互对照学习,都已经附上源码 。 **1.** socket 创建 tcp套接字 (监听的套接字) 2、IPv4套接字地址结构 #include netinet/in.h struct in_addr: 如果使用 Internet 所以 sin_family 一般为 AF_INET。 ⚫ sin_addr 设置为 INADDR_AN

    2024年02月03日
    浏览(62)
  • 计算机网络套接字编程实验-TCP多进程并发服务器程序与单进程客户端程序(简单回声)

    1.实验系列 ·Linux NAP-Linux网络应用编程系列 2.实验目的 ·理解多进程(Multiprocess)相关基本概念,理解父子进程之间的关系与差异,熟练掌握基于fork()的多进程编程模式; ·理解僵尸进程产生原理,能基于|sigaction()或signal(),使用waitpid()规避僵尸进程产生; ·

    2024年02月12日
    浏览(45)
  • 网络编程:TCP粘包问题——各层粘包/拆包、Nagle 算法、Go实现长度字段协议解决TCP粘包、使用TCP的应用层协议设计

    1.1 TCP介绍 如上图,TCP具有面向连接、可靠、基于字节流三大特点。 字节流可以理解为一个双向的通道里流淌的数据,这个数据其实就是我们常说的二进制数据,简单来说就是一大堆 01 串。纯裸TCP收发的这些 01 串之间是没有任何边界的,你根本不知道到哪个地方才算一条完

    2024年02月04日
    浏览(39)
  • 网络编程1—— IP地址 + 端口号 +TCP/IP协议 + 协议分层的封装与应用

    本人是一个刚刚上路的IT新兵,菜鸟!分享一点自己的见解,如果有错误的地方欢迎各位大佬莅临指导,如果这篇文章可以帮助到你,劳请大家点赞转发支持一下! 从本篇文章开始就要分享网络编程的内容了,越发的感受到了编程的魅力,鸡汤来喽!! 1️⃣单机阶段:计算机跟计算

    2024年02月12日
    浏览(40)
  • ESP32网络编程-TCP客户端数据传输

    本文将详细介绍在Arduino开发环境中,实现一个ESP32 TCP客户端,从而达到与TCP服务器数据交换的目标。 Internet 协议(IP)是 Internet 的地址系统,具有将数据包从源设备传递到目标设备的核心功能。IP 是建立网络连接的主要方式,奠定了 Internet 的基础。IP 不负责数据包排序或错

    2024年02月03日
    浏览(43)
  • TCP IP网络编程(三) 地址族与数据序列

    网络地址 IP地址分为两类: IPv4 4字节地址族 IPv6 16字节地址族 IPv4和IPv6的差别主要是表示在IP地址所用的字节数,目前通用的地址族为IPv4,而IPv6是为了应对IP地址耗尽的问题而提出的标准,目前主要还是使用IPv4 IPv4标准的4字节IP地址分为网络地址和主机地址,且分为A、B、C、

    2024年02月10日
    浏览(82)
  • 基于数据库及TCP网络编程实现的电子词典

    目录 一、前言 二、项目介绍 三、功能实现 3.1. 用户注册 3.1.1 功能演示 3.1.2 功能函数实现 3.2. 用户登录 3.2.1 功能演示 3.2.2 功能函数实现 3.3. 查询单词 3.3.1 功能演示 3.3.2 功能函数实现 3.4. 历史查询 3.4.1 功能演示 3.4.2 功能函数实现 四、代码实现 4.1 服务器程序 server.c 4.2 客户

    2023年04月09日
    浏览(82)
  • TCP/IP网络编程(二) 套接字协议及其数据传输特性

    关于协议 如果相隔比较远的两人进行通话,必须先决定通话方式,如果一方选择电话,另一方也必须选择电话,否则接受不到消息。 总之,协议就是为了完成数据交换而定好的约定。 创建套接字 协议族 通过socket函数的第一个参数传递套接字中使用的协议分类信息,此协议

    2024年02月10日
    浏览(59)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包