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(…...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...
ubuntu搭建nfs服务centos挂载访问
在Ubuntu上设置NFS服务器 在Ubuntu上,你可以使用apt包管理器来安装NFS服务器。打开终端并运行: sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享,例如/shared: sudo mkdir /shared sud…...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...
IGP(Interior Gateway Protocol,内部网关协议)
IGP(Interior Gateway Protocol,内部网关协议) 是一种用于在一个自治系统(AS)内部传递路由信息的路由协议,主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...
linux 错误码总结
1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...
排序算法总结(C++)
目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指:同样大小的样本 **(同样大小的数据)**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...
并发编程 - go版
1.并发编程基础概念 进程和线程 A. 进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中…...
【Linux】Linux安装并配置RabbitMQ
目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的,需要先安…...
leetcode73-矩阵置零
leetcode 73 思路 记录 0 元素的位置:遍历整个矩阵,找出所有值为 0 的元素,并将它们的坐标记录在数组zeroPosition中置零操作:遍历记录的所有 0 元素位置,将每个位置对应的行和列的所有元素置为 0 具体步骤 初始化…...
