GO网络编程(二):客户端与服务端通信【重要】
本节是新知识,偏应用,需要反复练习才能掌握。
目录
- 1.C/S通信示意图
- 2.服务端通信
- 3.客户端通信
- 4.通信测试
- 5.进阶练习:客户端之间通信
1.C/S通信示意图
客户端与服务端通信的模式也称作C/S模式,流程图如下
其中P是协程调度器。可以看到,客户端都是通过一个共同的端口和服务端通信的,且服务端开了两个协程并发处理两个客户端。注意,协程和P之间,协程和端口之间,端口和客户端之间的链接都是双向的。
2.服务端通信
服务端功能要求如下:
(1)编写一个服务器端程序,在8888端口监听
fmt.Println("服务器开始监听....")
//net.Listen("tcp", "0.0.0.0:8888")
//1. tcp 表示使用网络协议是tcp
//2. 0.0.0.0:8888 表示在本地监听 8888端口
listen, err := net.Listen("tcp", "0.0.0.0:8888")
(2)可以和多个客户端创建链接
//循环等待客户端来链接我
for {//等待客户端链接fmt.Println("等待客户端来链接....")conn, err := listen.Accept()//这里准备其一个协程,为客户端服务go process(conn)
}
(3)链接成功后,客户端可以发送数据,服务器端接受数据,并显示在终端上
完整代码如下:
package main
import ("fmt""net" //做网络socket开发时,net包含有我们需要所有的方法和函数_"io"
)func process(conn net.Conn) {//这里我们循环的接收客户端发送的数据defer conn.Close() //关闭connfor {//创建一个新的切片buf := make([]byte, 1024)//conn.Read(buf)//1. 等待客户端通过conn发送信息//2. 如果客户端没有wrtie[发送],那么协程就阻塞在这里//fmt.Printf("服务器在等待客户端%s 发送信息\n", conn.RemoteAddr().String())n , err := conn.Read(buf) //从conn读取if err != nil {fmt.Printf("客户端退出 err=%v", err)return //!!!}//3. 显示客户端发送的内容到服务器的终端fmt.Print(string(buf[:n])) }}func main() {fmt.Println("服务器开始监听....")//net.Listen("tcp", "0.0.0.0:8888")//1. tcp 表示使用网络协议是tcp//2. 0.0.0.0:8888 表示在本地监听 8888端口listen, err := net.Listen("tcp", "0.0.0.0:8888")if err != nil {fmt.Println("listen err=", err)return }defer listen.Close() //延时关闭listen//循环等待客户端来链接我for {//等待客户端链接fmt.Println("等待客户端来链接....")conn, err := listen.Accept()if err != nil {fmt.Println("Accept() err=", err)} else {fmt.Printf("Accept() suc con=%v 客户端ip=%v\n", conn, conn.RemoteAddr().String())}//这里准备其一个协程,为客户端服务go process(conn)}//fmt.Printf("listen suc=%v\n", listen)
}
流程总结:
1.使用net.Listen()
初始化监听端口,把初始信息存在变量listen中
2.循环使用listen.Accept()
,接收监听到的基本信息,存在变量conn中
3.每次循环用conn.Read(buf)
读取客户端发送的内容,将这些操作封装到一个协程中,并发执行
3.客户端通信
客户端功能:
(1)编写一个客户端程序,能链接到服务器端的8888端口
conn, err := net.Dial("tcp", "localhost:8888")
(2)客户端可以发送单行数据,然后就退出
reader := bufio.NewReader(os.Stdin) //os.Stdin 代表标准输入[终端]
(3)能通过终端输入数据(输入一行发送一行),并发送给服务器端
line, err := reader.ReadString('\n')
if err != nil {fmt.Println("readString err=", err)
}
其中ReadString(‘\n’)表示以换行符为截止符号,读取包括换行符在内的之前的字符,所以之后还需要去除line中的换行符。
(4)在终端输入exit,表示退出程序
line = strings.Trim(line, " \r\n")if line == "exit" {fmt.Println("客户端退出..")break}
完整代码如下:
package main
import ("fmt""net""bufio""os""strings"
)func main() {conn, err := net.Dial("tcp", "localhost:8888")if err != nil {fmt.Println("client dial err=", err)return }//功能一:客户端可以发送单行数据,然后就退出reader := bufio.NewReader(os.Stdin) //os.Stdin 代表标准输入[终端]for {//从终端读取一行用户输入,并准备发送给服务器line, err := reader.ReadString('\n')if err != nil {fmt.Println("readString err=", err)}//如果用户输入的是 exit就退出line = strings.Trim(line, " \r\n")if line == "exit" {fmt.Println("客户端退出..")break}//再将line 发送给 服务器_, err = conn.Write([]byte(line + "\n"))if err != nil {fmt.Println("conn.Write err=", err) }}
}
注:localhost是本地ip地址,默认ipv6。
流程总结:
1.使用net.Dial()
建立与服务端的链接,把初始信息存在变量conn中
2.循环使用bufio.NewReader(os.Stdin)
创建缓冲区,读入用户输入的信息并存在reader中
3.使用reader.ReadString('\n')
提取每行字符
4.判断输入是否为exit,若是则退出循环
4.若输入步是exit,则用conn.Write()
将每行内容发送到服务端
4.通信测试
先启动服务端,内容如下:
服务器开始监听....
等待客户端来链接....
注意,如果出现类似下图的提示框请点击允许!
再打开当前目录的命令行,启动客户端,命令行会显示如下语句
Accept() suc con=&{{0xc00008a508}} 客户端ip=[::1]:端口号
注意:这个端口号不是8888,而是客户端用来连接服务器时的本地端口。每个客户端在连接到服务器时,操作系统会为它分配一个临时的随机端口。
客户端输入几行语句
D:\code\golang\尚硅谷golang\代码\chapter18\tcpdemo\client>go run "client(1).go"
123
你好
服务端内容
服务器开始监听....
等待客户端来链接....
Accept() suc con=&{{0xc00008a508}} 客户端ip=[::1]:端口号
等待客户端来链接....
123
你好
客户端输入exit即可结束
客户端退出..D:\code\golang\尚硅谷golang\代码\chapter18\tcpdemo\client>
服务端内容
客户端退出 err=read tcp [::1]:8888->[::1]:端口号: wsarecv: An existing connection was forcibly closed by the remote host.
注意,此时服务端仍在循环监听,按ctrl+c即可结束程序。
5.进阶练习:客户端之间通信
为了使两个客户端能够相互通信,需要一个中介服务器(通常称为“中转服务器”),它负责转发每个客户端的消息给另一个客户端。服务器在两个客户端之间保持连接,接收其中一个客户端的消息,然后将其转发给另一个客户端。
实现步骤:
服务器端:
1.服务器负责接收客户端 A 和客户端 B 的连接。
2.服务器读取每个客户端发送的消息,然后将消息转发给另一个客户端。
package mainimport ("fmt""net""sync"
)var (clients = make(map[net.Conn]bool) // 存储连接的客户端mu sync.Mutex // 保护 clients 的并发访问
)func main() {listener, err := net.Listen("tcp", ":8888")if err != nil {fmt.Println("服务器启动错误:", err)return}defer listener.Close()fmt.Println("服务器正在监听8888端口...")for {conn, err := listener.Accept()if err != nil {fmt.Println("接受连接时发生错误:", err)continue}// 在服务端提示有新的客户端连接fmt.Printf("新的客户端已连接: %v\n", conn.RemoteAddr())// 将客户端添加到客户端列表mu.Lock()clients[conn] = truemu.Unlock()go handleConnection(conn) // 处理每个连接}
}func handleConnection(conn net.Conn) {defer func() {conn.Close()mu.Lock()delete(clients, conn) // 从列表中删除客户端mu.Unlock()}()buf := make([]byte, 1024)for {n, err := conn.Read(buf) // 读取客户端消息if err != nil {fmt.Printf("客户端 %v 断开连接: %v\n", conn.RemoteAddr(), err)return}message := string(buf[:n])// 判断是否是退出消息if message == "exit\n" || message == "exit" {fmt.Printf("客户端 %v 退出。\n", conn.RemoteAddr())return}// 转发消息给其他客户端mu.Lock()for client := range clients {if client != conn { // 不给发送者发送消息_, _ = client.Write(buf[:n]) // 将消息发送到其他客户端}}mu.Unlock()}
}
客户端:
1.客户端连接到服务器,通过服务器发送和接收消息。
2.客户端之间不会直接通信,而是通过服务器中转消息。
package mainimport ("bufio""fmt""net""os""strings"
)func main() {// 使用 localhost 连接到服务器conn, err := net.Dial("tcp", "localhost:8888")if err != nil {fmt.Println("连接服务器失败:", err)return}defer conn.Close() // 确保在退出时关闭连接// 启动一个 goroutine 负责接收服务器的消息go func() {buf := make([]byte, 1024)for {n, err := conn.Read(buf) // 从服务器读取数据if err != nil {fmt.Println("与服务器断开连接:", err)return}// 显示来自服务器的消息fmt.Print("收到消息: ", string(buf[:n]))}}()// 主协程负责发送用户输入的消息reader := bufio.NewReader(os.Stdin)fmt.Println("连接到服务器,输入消息发送,输入 'exit' 退出...")for {// 从终端读取一行用户输入line, err := reader.ReadString('\n')if err != nil {fmt.Println("读取输入时发生错误:", err)continue}// 去掉输入行的换行符和空格go ruline = strings.TrimSpace(line)// 如果用户输入的是 exit,退出程序if line == "exit" {fmt.Println("客户端退出...")break}// 将用户输入发送给服务器_, err = conn.Write([]byte(line + "\n"))if err != nil {fmt.Println("发送消息时发生错误:", err)}}
}
效果截图
相关文章:

GO网络编程(二):客户端与服务端通信【重要】
本节是新知识,偏应用,需要反复练习才能掌握。 目录 1.C/S通信示意图2.服务端通信3.客户端通信4.通信测试5.进阶练习:客户端之间通信 1.C/S通信示意图 客户端与服务端通信的模式也称作C/S模式,流程图如下 其中P是协程调度器。可…...

快速熟悉Nginx
一、Nginx是什么? Nginx是一款高性能、轻量级的Web服务器和反向代理服务器。 特点:Nginx采用事件驱动的异步非阻塞处理框架,内存占用少,并发能力强,资源消耗低。功能:Nginx主要用作静态文件服…...

VikParuchuri/marker 学习简单总结
核心代码 VikParuchuri/marker 的核心是使用https://github.com/VikParuchuri/surya的 pdf 模型,注意不仅仅是ocr,在marker的代码里面有标注ocr 是option的。强制OCR 要设置:OCR_ALL_PAGES=true核心代码就是convert.py def convert_single_pdf(fname: str,model_lst: List,…...

【AI知识点】词嵌入(Word Embedding)
词嵌入(Word Embedding)是自然语言处理(NLP)中的一种技术,用于将词语或短语映射为具有固定维度的实数向量。这些向量(嵌入向量)能够捕捉词语之间的语义相似性,即将语义相近的词映射到…...

Python从入门到高手5.1节-Python简单数据类型
目录 5.1.1 理解数据类型 5.1.2 Python中的数据类型 5.1.3 Python简单数据类型 5.1.4 特殊的空类型 5.1.5 Python变量的类型 5.1.6 广州又开始变热 5.1.1 理解数据类型 数据类型是根据数据本身的性质和特征来对数据进行分类,例如奇数与偶数就是一种数据类型。…...

Hbase要点简记
Hbase要点简记 Hbase1、底层架构2、表逻辑结构 Hbase HBase是一个分布式的、列式的、实时查询的、非关系型数据库,可以处理PB级别的数据,吞吐量可以到的百万查询/每秒。主要应用于接口等实时数据应用需求,针对具体需求,设计高效率…...

RabbitMQ的各类工作模式介绍
简单模式 P: ⽣产者, 也就是要发送消息的程序 C: 消费者,消息的接收者 Queue: 消息队列, 图中⻩⾊背景部分. 类似⼀个邮箱, 可以缓存消息; ⽣产者向其中投递消息, 消费者从其中取出消息.特点: ⼀个⽣产者P,⼀个消费者C, 消息只能被消费⼀次. 也称为点对点(Point-to-…...

李宏毅深度学习-图神经网络GNN
图卷积的开源代码网站DGL 好用的还是 GAT, GIN(指出最好的卷积 就是 hi 邻居特征(而且只能用 sum)) Introduction GNN 可以理解为是由 Graph(图) Nerual Networks 组合而成的,图结构应该都在数据结构与…...

Redis篇(缓存机制 - 分布式缓存)(持续更新迭代)
目录 一、单点 Redis 的问题 1. 数据丢失问题 2. 并发能力问题 3. 故障恢复问题 4. 存储能力问题 5. 四种问题的解决方案 二、Redis持久化(两种方案) 1. RDB持久化 1.1. 简介 1.2. 执行时机 save命令 bgsave命令 停机时 触发RDB条件 1.3. …...

python交互式命令时如何清除
在交互模式中使用Python,如果要清屏,可以import os,通过os.system()来调用系统命令clear或者cls来实现清屏。 [python] view plain copy print? >>> import os >>> os.system(clear) 但是此时shell中的状态是:…...

Token,Cookie,Session,JWT详解
这四个技术虽然在功能上有所不同,但在web应用中常常一起使用,已实现用户身份验证,授权和会话管理。 Token:指的是用于身份验证,授权成信息交换的令牌,可以有不同的实现方式,例如JWT。 Cookie&…...

opencv-rust 系列: 1, 安装及运行自带示例和测试程序
opencv-rust 系列: 1, 安装及运行自带示例和测试程序 运行环境: ubuntu ; rust 已安装; 对rust的掌握为三脚猫程度一. opencv-rust安装:二. 运行自带examples和tests 运行环境: ubuntu ; rust 已安装; 对rust的掌握为三脚猫程度 一. opencv-rust安装: 安装软件: sudo apt in…...

Linux系统编程(一):Linux平台上静态库和动态库的制作与使用
本篇文章我们通过 gcc 或g编译器手动制作Linux 平台上的静态库和动态库。由于涉及的内容较多,所以后面分多次来完成本篇文章。做任何事情都是一样的,我们不可能一次性把处在舒适区的事情做好。 本讲主要内容如下: 库的基本概念Linux 平台上…...

Nginx的基础讲解之重写conf文件
一、Nginx 1、什么是nginx? Nginx(engine x)是一个高性能的HTTP和反向代理web服务器,同时也提供了IMAP/POP3/SMTP服务。 2、用于什么场景 Nginx适用于各种规模的网站和应用程序,特别是需要高并发处理和负载均衡的场…...

RIFE: Real-Time Intermediate Flow Estimation for Video Frame Interpolation
Paper name RIFE: Real-Time Intermediate Flow Estimation for Video Frame Interpolation Paper Reading Note Paper URL: https://arxiv.org/pdf/2011.06294 Code URL: https://github.com/hzwer/ECCV2022-RIFE TL;DR 2022 年旷视出品的实时视频帧插值工作。提出 RIFE…...

rabbitMq-----broker服务器
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言管理的字段 前言 搭建一个网络服务器,在内部提供各个业务接口即可。 在业务处理函数中,每次请求过来找到对应的信道,通过信…...

MAC备忘录空白解决方案
打开icloud->备忘录 取消勾选同步此MAC后再次勾选,然后点击完成即可。...
cnn突破七(四层bpnet网络公式与卷积核bpnet公式相关)
我们要有一个概念,就是卷积核就是我们的w1,w12,w2 那么我们的5*5卷积核怎么表达,当他在14*14的图像中流动时,对应的像素也在变化 这个和我们的上面w1,w12,w2不同,因为这几个都是全…...

PHP中的PEAR是什么
PHP中的PEAR是PHP Extension and Application Repository的缩写,即PHP扩展与应用库。它是一个PHP扩展及应用的代码仓库,提供了许多常用的PHP库和工具,涵盖了页面呈现、数据库访问、文件操作、数据结构、缓存操作、网络协议、WebService等许多…...

(C语言贪吃蛇)4.贪吃蛇地图优化及算法说明
上节代码示例: #include <curses.h>void initNcurse() {initscr();keypad(stdscr,1); }void gamePic() {int hang;int lie;for(hang 0;hang < 20;hang ){if(hang 0){for(lie 0;lie < 20;lie ){printw("--");}printw("\n");for(…...

国外电商系统开发-运维系统拓扑布局
点击列表中设备字段,然后定位到【拓扑布局】中,可以看到拓扑发生了变化 再回头,您再次添加一个服务器到系统中,并且选择该服务器的连接节点为您刚才创建的“SDN路由器”,保存后,您可以看到这个服务器连接着…...

使用winsock和ip相关指令重置Window网络配置
netsh winsock reset 和 netsh int ip reset 是 Windows 中用于修复网络问题的命令。它们分别用于重置 Winsock 和 TCP/IP 网络配置,以解决可能由于配置错误或网络堆栈损坏而导致的网络连接问题。 1. netsh winsock reset 1.1 作用 重置 Winsock 目录。Winsock 是…...

用AI做电子萌宠,快速涨粉变现
今天给大家分享一个很热门的小副业—AI宠物视频 项目介绍 前一阵刷到一个萌宠账号,爆款率可真高,涨粉可真快呀。 28篇笔记涨粉3.2万,点赞更是达到了十几万。其中有6篇点赞上万。 至于究竟是不是AI其实没那么重要,重要的是&…...

如何在 Axios 中封装事件中心EventEmitter
在 Axios 中封装一个事件中心 EventEmitter 允许你在请求的不同阶段(如请求开始、请求成功、请求失败等)触发事件。这可以通过创建一个自定义的 Axios 实例,并结合 Node.js 的 events 模块来实现。以下是一个详细的步骤指南和示例代码&#x…...

计算机网络——ftp
在网络通信中,控制连接和数据连接是两种不同类型的连接,它们各自具有特定的功能和用途。 一、控制连接 定义与功能: 控制连接主要用于在通信双方之间传输控制信息,以建立、维护和终止数据连接。它负责协调和管理数据传输的过程&am…...

Redis:set类型
Redis:set类型 set命令SADDSMEMBERSSISMEMBERSCARDSPOPSRANDMEMBERSMOVESREM 集合间操作SINTERSINTERSTORESUNIONSUNIONSTORESDIFFSDIFFSTORE 内部编码intsethashtable 当把一些关联的数据放到一起,就构成一个集合。在Redis中,使用set类型维护…...

九大排序之插入排序
1.前言 插入排序是把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列 。实际中我们玩扑克牌时,就用了插入排序的思想。 本章重点:主要着重的介绍两种插入排序…...

DNABERT: 一个基于 Transformer 双向编码器表征的预训练 DNA 语言模型
本文结合 DNABERT 的原文,主要介绍了: Overview of DNABERT 开发 DNABERT 的背景 DNABERT 的 tokenization DNABERT 的模型架构 DNABERT 的预训练 基于微调 DNABERT 的应用 1. Overview of DNABERT 我们之前介绍了 BERT,它是一个基于 Transfo…...

基于Hive和Hadoop的电商消费分析系统
本项目是一个基于大数据技术的电商消费分析系统,旨在为用户提供全面的电商消费信息和深入的消费行为分析。系统采用 Hadoop 平台进行大规模数据存储和处理,利用 MapReduce 进行数据分析和处理,通过 Sqoop 实现数据的导入导出,以 S…...

记一次炉石传说记牌器 Crash 排查经历
大家好这里是 Geek技术前线。最近在打炉石过程中遇到了HSTracker记牌器的一个闪退问题,尝试性排查了下原因。这里简单记录一下 最近炉石国服回归;由于设备限制,我基本只会在 Mac 上打炉石。并且由于主要打竞技场,所以记牌器是必不…...