第十一章 网络编程

在TCP/IP协议中,“IP地址+TCP或UDP端口号”唯一标识网络通讯中的一个进程。

因此可以用Socket来描述网络连接的一对一关系。

常用的Socket类型有两种:流式Socket(SOCK_STREAM)和数据报式Socket(SOCK_DGRAM)。流式是一种面向连接的Socket,针对于面向连接的TCP服务应用;数据报式Socket是一种无连接的Socket,对应于无连接的UDP服务应用。

1 TCP的C/S架构

1.1 单服务版本

服务端代码:

package main  import (  "fmt"  "net")  func main() {  // 创建监控  listener, err := net.Listen("tcp", "localhost:8000")  if err != nil {  fmt.Println("listen err:", err)  return  }  defer listener.Close()  // 主协程结束时,关闭listener  fmt.Println("服务器等待客户端建立连接...")  // 等待客户端连接请求  conn, err := listener.Accept()  if err != nil {  fmt.Println("accpet err:", err)  return  }  defer conn.Close()  // 使用结束,断开与客户端链接  fmt.Println("客户端与服务器连接建立成功...")  // 接收客户端数据  buf := make([]byte, 1024)  // 创建1024大小的缓冲区,用于read  n, err := conn.Read(buf)  // 读取到n个大小的数据  if err != nil {  fmt.Println("read err:", err)  return  }  fmt.Println("服务器读到:", string(buf[:n])) // 读多少,打印多少  
}

如图,在整个通信过程中,服务器端有两个socket参与进来,但用于通信的只有 conn 这个socket。它是由 listener创建的。隶属于服务器端。

客户端代码:

package main  import (  "fmt"  "net")  func main() {  // 主动发送连接请求  conn, err := net.Dial("tcp", "localhost:8000")  if err != nil {  fmt.Println("Dial err", err)  }  defer conn.Close()  // 结束时,关闭连接  // 发送数据  _, err = conn.Write([]byte("Are u ready?"))  if err != nil {  fmt.Println("Write err:", err)  return  }  
}

1.2 并发服务

并发服务端:

Accept()函数的作用是等待客户端的链接,如果客户端没有链接,该方法会阻塞。如果有客户端链接,那么该方法返回一个Socket负责与客户端进行通信。所以,每来一个客户端,该方法就应该返回一个Socket与其通信,因此,可以使用一个死循环,将Accept()调用过程包裹起来。

需要注意的是,实现并发处理多个客户端数据的服务器,就需要针对每一个客户端连接,单独产生一个Socket,并创建一个单独的goroutine与之完成通信。

在判断客户端数据是否为“exit”字符串时,要注意,客户端会自动的多发送2个字符:KaTeX parse error: Undefined control sequence: \n at position 4: “\r\̲n̲”(这在windows系统下代表回车、换行)

服务端代码:

package main  import (  "fmt"  "net"   "strings")  func main() {  // 创建监控  listener, err := net.Listen("tcp", "localhost:8000")  if err != nil {  fmt.Println("listen err:", err)  return  }  defer listener.Close()  // 主协程结束时,关闭listener  for {  // 等待客户端连接请求  conn, err := listener.Accept()  if err != nil {  fmt.Println("accpet err:", err)  return  }  // 处理用户请求,新建一个协程  go HandleConn(conn)  }  
}  // 处理用户请求  
func HandleConn(conn net.Conn) {  // 函数调用完毕,自动关闭conn  defer conn.Close()  // 获取客户端发过来的网址信息  addr := conn.RemoteAddr().String()  fmt.Println(addr, "connect successful")  buf := make([]byte, 2048)  for {  // 读取用户数据  n, err := conn.Read(buf)  if err != nil {  fmt.Println("err=", err)  return  }  fmt.Printf("[%s]: %s\n",  addr,  string(buf[:n]))  fmt.Println("len = ", len(string(buf[:n])))  //if string(buf[:n-1]) == "exit" // nc测试,发送时,只有/n  if string(buf[:n-2]) == "exit" {  fmt.Println(addr, "exit")  return  }  // 将数据转化为大写,再给用户发送  conn.Write([]byte(strings.ToUpper(string(buf[:n]))))  }  
}

并发客户端:

客户端不仅需要持续的向服务端发送数据,同时也要接收从服务端返回的数据。因此可将发送和接收放到不同的协程中。

主协程循环接收服务器回发的数据(该数据应已转换为大写),并打印至屏幕;子协程循环从键盘读取用户输入数据,写给服务器。读取键盘输入可使用 os.Stdin.Read(str)。定义切片str,将读到的数据保存至str中。

这样,客户端也实现了多任务。

package main  import (  "fmt"  "net"   "os")  func main() {  // 主动发送连接请求  conn, err := net.Dial("tcp", "localhost:8000")  if err != nil {  fmt.Println("Dial err", err)  }  defer conn.Close()  // 客户端终止时,关闭于服务器通讯的socket  // 启动子协程: 接受用户键盘输入发送给服务端  go func() {  // 创建用于存储用户键盘输入数据的切片缓冲区str := make([]byte, 1024)    for {  // 反复读取  n, err := os.Stdin.Read(str)  // 获取用户键盘输入(阻塞)  if err != nil {  fmt.Println("os.Stdin.Read err:", err)  return  }  // 从键盘读到的数据,发送给服务端  _, err = conn.Write(str[:n])  if err != nil {  fmt.Println("conn.Write err:", err)  return  }  }  }()  // 主协程: 接受服务端数据,进行打印输出  buf := make([]byte, 1024)  // 定义用于存储服务器回发数据的切片缓冲区  for {  n, err := conn.Read(buf)  // 从通信socket中读数据,存入切片缓冲区(阻塞)  if err != nil {  fmt.Println("conn.Read err:", err)  return  }  fmt.Printf("服务器回发: %s\n", string(buf[:n]))  }  }

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

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

相关文章

ffmpeg实现视频流抽帧

ffmpeg 实现视频流抽帧 抽取实时视频帧 如果你的实时视频是通过 RTSP、UDP 或其他协议获取的,可以直接调用 FFmpeg 命令来抽取帧。 ffmpeg 命令 示例 1 ffmpeg -i rtsp://your_rtsp_stream_url -vf fps1 -update 1 output.jpg说明: -i rtsp://your…

【GIT】放弃”本地更改,恢复到远程仓库的状态git fetch origin git reset --hard origin/分支名

如果你想完全放弃本地更改,恢复到远程仓库的状态,可以按照以下步骤操作: 获取远程最新版本 首先执行: git fetch origin这条命令会把远程仓库的最新提交拉取到你的本地,但不会自动合并到你的当前分支。 硬重置你的当前…

flutter doctor 信号号超时

报错如下: :\Users\Administrator>flutter doctor Doctor summary (to see all details, run flutter doctor -v): [√] Flutter (Channel stable, 3.27.4, on Microsoft Windows [版本 10.0.22631.5189], locale zh-CN) [√] Windows Version (Installed versi…

【Linux】系统入门

【Linux】系统初识 起源开源 闭源版本内核内核编号 Linux的安装双系统(不推荐)WindowsLinuxvmware虚拟机vitualbox操作系统的镜像centos 7/ubuntu云服务器租用 Linux的操作lsmkdir 文件名pwdadduser userdel -rrm文件名cat /proc/cpuinfolinux支持编程vim code.c./a.out 运行程…

mybatis-plus整合springboot与使用方式

注解 TableField(exist false)&#xff1a;表示该属性不为数据库表字段&#xff0c;但又是必须使用的。 整合springboot pom <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xs…

[第十六届蓝桥杯 JavaB 组] 真题 + 经验分享

A&#xff1a;逃离高塔(AC) 这题就是简单的签到题&#xff0c;按照题意枚举即可。需要注意的是不要忘记用long&#xff0c;用int的话会爆。 &#x1f4d6; 代码示例&#xff1a; import java.io.*; import java.util.*; public class Main {public static PrintWriter pr ne…

GPU服务器声音很响可以怎么处理

当GPU服务器运行时噪音过大&#xff0c;通常是由于高负载下散热风扇高速运转所致。以下是分步骤的解决方案&#xff0c;帮助您有效降低噪音并保持设备稳定运行&#xff1a; 一、排查噪音来源 定位声源 • 使用 声级计 或手机分贝检测APP&#xff0c;确定最大噪音位置&#xff0…

STM32平衡车开发实战教程:从零基础到项目精通

STM32平衡车开发实战教程&#xff1a;从零基础到项目精通 一、项目概述与基本原理 1.1 平衡车工作原理 平衡车是一种基于倒立摆原理的两轮自平衡小车&#xff0c;其核心控制原理类似于人类保持平衡的过程。当人站立不稳时&#xff0c;会通过腿部肌肉的快速调整来维持平衡。平…

C#设计模式-状态模式

状态模式案例解析&#xff1a;三态循环灯的实现 案例概述 本案例使用 状态模式&#xff08;State Pattern&#xff09; 实现了一个 三态循环灯 的功能。每点击一次按钮&#xff0c;灯的状态会按顺序切换&#xff08;状态1 → 状态2 → 状态3 → 状态1...&#xff09;&#xff…

Mac系统升级node.js版本和npm版本并安装pnpm

1.升级node.js版本 第一步&#xff1a;查询当前node.js版本 node -v第二步&#xff1a;清除node.js的缓存 sudo npm cache clean -f第三步&#xff1a;验证缓存是否清空 npm cache verify第四步&#xff1a;安装n工具&#xff0c;n工具是专门用于管理node.js版本的工具 su…

[net 5] udp_dict_server 基于udp的简单字典翻译(服务器与业务相分离)

目录 1. 功能了解 1.1. 啥是 dic_server? 1.2. dic_server 的小目标 2. 基本框架 2.1. 基本文件框架 2.2. 业务与服务器解耦 -> 回调函数 3. 字典 3.1. 字典配置文件 3.2. 构建字典类 3.2.1. 字典类的基本成员 3.2.2. 字典类构造 3.2.2.1. 构造 3.2.2.2. 信息加…

七种驱动器综合对比——《器件手册--驱动器》

九、驱动器 名称 功能与作用 工作原理 优势 应用 隔离式栅极驱动器 隔离式栅极驱动器用于控制功率晶体管&#xff08;如MOSFET、IGBT、SiC或GaN等&#xff09;的开关&#xff0c;其核心功能是将控制信号从低压侧传输到高压侧的功率器件栅极&#xff0c;同时在输入和输出之…

EM储能网关ZWS智慧储能云应用(8) — 电站差异化支持

面对不同项目、种类繁多的储能产品&#xff0c;如何在储能云平台上进行电站差异化支持尤为关键&#xff0c;ZWS智慧储能云从多方面支持储能电站差异化。 简介 随着行业发展&#xff0c;市场“内卷”之下&#xff0c;各大储能企业推陈出新的速度加快。面对不同项目、种类繁多…

图像预处理-色彩空间补充,灰度化与二值化

一.图像色彩空间转换 1.1 HSV颜色空间 HSV颜色空间使用色调&#xff08;Hue&#xff09;、饱和度&#xff08;Saturation&#xff09;和亮度&#xff08;Value&#xff09;三个参数来表示颜色 一般对颜色空间的图像进行有效处理都是在HSV空间进行的&#xff0c;然后对于基本…

Midnight Flag CTF 2025

周末还是三个比赛&#xff0c;可惜不好弄。不是远端连不上就是远端打不开。再有就是太难了。 Crypto ABC 这个题还是不算难的。给了两个30位数的平方和&#xff0c;并且pu1*baser0,qu2*baser1其中r 都很小&#xff0c;可以copper。 只是sage里的two_squres不管用&#xff0…

深度学习--激活函数

激活函数通过计算加权和并加上偏置来确定神经元是否应该倍激活&#xff0c;它们将输入信号转换为输出的可微运算。大多数激活函数都是非线性的&#xff0c;由于激活函数是深度学习的基础&#xff0c;下面简要介绍一些常见的激活函数。 1 RelU函数 最受欢迎的激活函数是修正线性…

深入解析 OrdinalEncoder 与 OneHotEncoder:核心区别与实战应用

标题&#xff1a;深入解析 OrdinalEncoder 与 OneHotEncoder&#xff1a;核心区别与实战应用 摘要&#xff1a; 本文详细探讨了机器学习中类别特征编码的两种核心方法——OrdinalEncoder 和 OneHotEncoder。通过对比两者的功能、特点、适用场景及代码实现&#xff0c;帮助读者…

CTF web入门之命令执行 完整版

web29 文件名过滤 由于flag被过滤,需要进行文件名绕过,有以下几种方法: 1.通配符绕过 fla?.* 2.反斜杠绕过 fl\ag.php 3.双引号绕过 fl’‘ag’.php 还有特殊变量$1、内联执行等 此外 读取文件利用cat函数,输出利用system、passthru 、echo echo `nl flag.php`; ec…

【Linux实践系列】:用c/c++制作一个简易的进程池

&#x1f525; 本文专栏&#xff1a;Linux Linux实践项目 &#x1f338;作者主页&#xff1a;努力努力再努力wz &#x1f4aa; 今日博客励志语录&#xff1a; 人生没有标准答案&#xff0c;你的错题本也能写成传奇。 ★★★ 本文前置知识&#xff1a; 匿名管道 1.前置知识回顾…

2.2 函数返回值

1.回顾def def sum(x,y): return xy res sum(10,20) #调用函数 print(res) 2.函数的三个重要属性 -函数的类型&#xff1a;function -函数的ID&#xff1a;16进制的整数数值 -函数的值&#xff1a;封装在函数中的数据和代码 # - 函数是一块内存空间&#xff0c;通过…