Golang——TCP、UDP实现并发(服务端与客户端)

Server端常用函数、接口:

Listen函数:func Listen(network, address string) (Listener, error)network:选用的协议:TCP、UDP, 	如:“tcp”或 “udp”address:IP地址+端口号, 			如:“127.0.0.1:8000”或 “:8000”Listener 接口:
type Listener interface {Accept() (Conn, error)Close() errorAddr() Addr
}Conn 接口:
type Conn interface {Read(b []byte) (n int, err error)Write(b []byte) (n int, err error)Close() errorLocalAddr() AddrRemoteAddr() AddrSetDeadline(t time.Time) errorSetReadDeadline(t time.Time) errorSetWriteDeadline(t time.Time) error
}

TCP-服务端实现:

func main() {// 指定服务器的通讯协议、ip、端口,Listen本身不做监听,这一步是创建了一个用于监听的Socketlisten, err := net.Listen("tcp", "127.0.0.1:8000")if err != nil {fmt.Println("net.Listen出错:", err)return}defer listen.Close()fmt.Println("服务器启动完毕,等待客户端连接")// 阻塞监听客户端连接请求,成功建立连接后会返回用于通信的Socketaccept, err := listen.Accept()if err != nil {fmt.Println("listen.Accept出错:", err)return}defer accept.Close()fmt.Println("服务器与客户端连接成功")// 读取客户端发动的请求buf := make([]byte, 4096)read, err := accept.Read(buf)if err != nil {fmt.Println("accept.Read出错:", err)return}// 接收数据后处理数据fmt.Println("服务器获取到:", string(buf[:read]))
}

Mac可以通过netcat进行测试:

在这里插入图片描述

TCP-客户端实现:

func main() {// 指定用户端的通讯协议、ip、端口,Listen本身不做监听,这一步是创建了一个用于监听的Socketdial, err := net.Dial("tcp", "127.0.0.1:8000")if err != nil {fmt.Println("net.Dial出错:", err)return}defer dial.Close()// 发送数据dial.Write([]byte("我是客户端"))// 接收服务器返回的数据buf := make([]byte, 4096)read, err := dial.Read(buf)if err != nil {fmt.Println("accept.Read出错:", err)return}// 接收数据后处理数据fmt.Println("客户端获取到:", string(buf[:read]))
}

TCP实现并发-服务器:

上面都是单机版的客户端通信,如果想要实现并发,需要使用Goroutine+循环实现

  • 循环读取客户端发送的数据
  • 如果客户端强制关闭连接需要做处理
  • 客户端发送exit时

演示:

package mainimport ("fmt""net""strings"
)func main() {// 创建监听套接字listen, err := net.Listen("tcp", "127.0.0.1:8000")if err != nil {fmt.Println("net.Listen出错:", err)return}defer listen.Close()// 创建客户端连接请求fmt.Println("服务器启动成功,等待客户端连接!")for {accept, err := listen.Accept()if err != nil {fmt.Println("listen.Accept出错:", err)return}// 调用服务器和客户端通信的函数go HandlerConnect(accept)}
}func HandlerConnect(accept net.Conn) {defer accept.Close()// 获取客户端发送的数据// 获取连接客户端的网络地址addr := accept.RemoteAddr()fmt.Println(addr, "客户端连接成功!")buf := make([]byte, 4096)for {read, err := accept.Read(buf)if err != nil {fmt.Println("accept.Read出错:", err)return}fmt.Println("服务器读到数据:", string(buf[:read]))// 模拟服务器收到数据后,回发给客户端,小写转大写data := strings.ToUpper(string(buf[:read]))accept.Write([]byte(data))}
}

在这里插入图片描述

TCP实现并发-客户端:

演示:

package mainimport ("fmt""net""os"
)func main() {//主动发送连接请求dial, err := net.Dial("tcp", "127.0.0.1:8000")if err != nil {fmt.Println("et.Dial出错了", err)return}defer dial.Close()// os.Stdin():获取用户键盘录入,go func() {str := make([]byte, 4096)for {read, err := os.Stdin.Read(str)if err != nil {fmt.Println("os.Stdin.Read出错了", err)continue}// 读到的数据写给服务器,读多少写多少dial.Write(str[:read])}}()buf := make([]byte, 4096)// 回显服务器发送的数据,转成大写for {read, err := dial.Read(buf)// read=0的说明对端关闭连接,如果关闭连接这里就不需要往下读数据了if read == 0 {fmt.Println("检测到服务端端已经断开连接!")return}if err != nil {fmt.Println("回显服务器发送的数据dial.Read出错了", err)return}fmt.Println("客户端读到服务器的回显数据", string(buf[:read]))}
}

在这里插入图片描述

UDP实现并发-服务器:

由于UDP是“无连接”的,所以,服务器端不需要额外创建监听套接字,只需要指定好IP和port,然后监听该地址,等待客户端与之建立连接,即可通信。

创建监听地址:

	func ResolveUDPAddr(network, address string) (*UDPAddr, error) 

创建监听连接:

	func ListenUDP(network string, laddr *UDPAddr) (*UDPConn, error) 

接收udp数据:

	func (c *UDPConn) ReadFromUDP(b []byte) (int, *UDPAddr, error)

写出数据到udp:

	func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (int, error)

演示:

package mainimport ("fmt""net"
)func main() {// 指定服务器的ip和端口,和TCP协议不一样,需要先写好再传给ListenUDP使用ServerAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:8000")if err != nil {fmt.Println("net.ResolveUDPAddr err:", err)return}fmt.Println("服务器启动成功!")// 创建用户通信的SocketudpConnect, err := net.ListenUDP("udp", ServerAddr)if err != nil {fmt.Println("net.ListenUDP err:", err)return}defer udpConnect.Close()fmt.Println("服务器创建Socket成功!")// 读写客户端的数据buf := make([]byte, 4096)count := 0for {// 返回值:n int(读到的字节数), addr *UDPAddr(客户端的地址), err errorudpBytes, ConnectAddr, err := udpConnect.ReadFromUDP(buf)if err != nil {fmt.Println("udpConnect.ReadFromUDP err:", err)return}count++// 模拟处理数据fmt.Printf("服务器读到第%v条数据 %v :%s\n", count, ConnectAddr, string(buf[:udpBytes]))go func() {// 回写数据到客户端udpConnect.WriteToUDP([]byte("回写数据到客户端\n"), ConnectAddr)}()}
}

UDP实现并发-客户端:

package mainimport ("fmt""net""time"
)func main() {dial, err := net.Dial("udp", "127.0.0.1:8000")if err != nil {fmt.Println("net.Dial出错:", err)return}defer dial.Close()for {// 发送数据dial.Write([]byte("我是客户端"))// 接收服务器返回的数据buf := make([]byte, 4096)read, err := dial.Read(buf)if err != nil {fmt.Println("accept.Read出错:", err)return}// 接收数据后处理数据fmt.Println("客户端获取到:", string(buf[:read]))time.Sleep(time.Second)}
}

在这里插入图片描述

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

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

相关文章

java中 将字符串时间 '2015-9-8 17:05:06' 转化为格式 '2015-09-08 17:05:06'

/** * 将字符串时间2015-9-8 17:05:06转化为格式2015-09-08 17:05:06 */import java.text.SimpleDateFormat; public class TestDate{ public static void main(String[] args) throws Exception{ String time "2015-9-8 17:05:06";//注意:时分秒必须都…

详解TCP协议三次握手四次挥手

三次握手: 三次握手表示建立通信阶段,在TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠,由于这种面向连接的特性, TCP协议可以保证传输数据的安全,…

Visual Paradigm中文乱码

http://blog.csdn.net/microrain/article/details/1201661?_t_t_t0.8160515017611372 转载于:https://www.cnblogs.com/mkxzy/p/7420463.html

Golang——实现文件传输

借助TCP完成文件的传输,基本思路如下: 发送方(客户端)向服务端发送文件名,服务端保存该文件名。接收方(服务端)向客户端返回一个消息ok,确认文件名保存成功。发送方(客户…

Golang——HTTP编程请求和响应实现

请求: HTTP 请求报文由请求行、请求头部、空行、请求包体4个部分组成,如下图所示: 请求行: 请求行由方法字段、URL 字段 和HTTP 协议版本字段 3个部分组成,他们之间使用空格隔开。常用的 HTTP 请求方法有 GET、POST。…

zabbix v3.0安装部署【转】

关于zabbix及相关服务软件版本: Linux:oracle linux 6.5 nginx:1.9.15 MySQL:5.5.49 PHP:5.5.35 一、安装nginx: 安装依赖包: yum -y install gcc gcc-c autoconf automake zlib zlib-devel ope…

WeChatTweak-微信小助手安装教程

github下载:https://github.com/Sunnyyoung/WeChatTweak-macOS CSDN下载:https://download.csdn.net/download/weixin_45477086/83895866 双击解压下载的WeChatTweak-macOS-***.zip在终端输入cd ,并敲一个空格,然后把解压的文件夹拖到终端 …

nodejs开发工具

我选择的是Hbuilder作为node项目的开发工具。先在Hbuilder 里面安装nodeEclipse插件,然后重启工具。点击添加项目,选择其他选项,出现下图选项,然后选择圈住的选项点击下一步:3. 如果不使用缺省位置,那么你的路径一定要…

Go_包、工程管理

包: 包其实就是文件夹,go的源文件就是文件,把所有的文件分类放到不同的包利于管理。 作用: 如果把所有的代码都放在一个文件中,后续的可维护性、阅读性都比较差。所以可以使用包的来区分不同的模块/功能分别放在不同…

排序之外部排序

有时,待排序的文件很大,计算机内存不能容纳整个文件,这时候对文件就不能使用内部排序了(这里做一下说明,其实所有的排序都是在内存中做的,这里说的内部排序是指待排序的内容在内存中就可以完成,…

Go_时间日期函数

时间: func main() {// 获取当前时间now : time.Now()fmt.Println("当前时间:", now)// 获取年月日时分秒fmt.Println("年:", now.Year())fmt.Println("月:", int(now.Month())) // 不转int是英文…

Golang——Json的序列化和反序列化

JSON: JSON(JavaScript Object Notation):是一种轻量级的数据交换格式。 它是基于 ECMAScript 规范的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。易于人阅读和编写…

安装配置 flannel - 每天5分钟玩转 Docker 容器技术(59)

上一节我们部署了 etcd,本节安装和配置 flannel。 build flannel flannel 没有现成的执行文件可用,必须自己 build,最可靠的方法是在 Docker 容器中 build。不过用于做 build 的 docker 镜像托管在 gcr.io,国内可能无法直接访问&a…

Golang——单元测试testing

Go语言中带有一个轻量级的测试框架testing和go test命令来实现单元测试及性能测试。单元测试可以解决 确保每个函数都是可运行且结果正确确保代码性能完好单元测试可以及时发现程序设计或实现的逻辑错误,使问题及早暴露,便于问题的定位解决,而…

排序算法之希尔排序

基本思想 希尔排序(Shell Sort)是插入排序的一种。也称缩小增量排序,是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时&#x…

Rust——Macos安装使用

进入官网会自动检测当前是什么操作系统,我的是Mac,所以使用官网给的命令安装就可以了 终端输入: curl --proto https --tlsv1.2 -sSf https://sh.rustup.rs | sh # 安装过程中,最后一步需要输入选择,输入1是继续安装…

android踩坑日记1

Android四大组件-活动、服务、广播、碎片情况一应用场景:定时从服务器获取数据,然后活动或者碎片中根据最新获得的数据,更新UI。思考: 首先定时,想到定时器,推荐使用系统自带的AlertManager,而它…

Go_常量、iota(枚举)的使用

常量 常量是在程序运行过程中,其值不可以发生改变的数据,常量无法被获取地址 常量中的数据类型能是布尔型、数字型(整型、浮点型和复数型)、字符串 常量的定义是通过const关键字完成的,并且常量名称大写为公开&#…

pthon_函数式编程与面向对象编程的比较

函数式编程作为结构化编程的一种,正在受到越来越多的重视。工程中不在只是面向对象编程,更多的人尝试着开始使用函数式编程来解决软件工程中遇到的问题。 什么是函数式编程?在维基百科中给出了详细的定义,函数式编程(英…

Go_方法、方法重载

方法: 方法是绑定在自定义类型上的,常用在结构体上 方法只能通过绑定的自定义类型的变量来调用,方法不能直接调用,因为方法是和类型作关联的 方法是值拷贝的传递方式,如果希望改变结构体变量的值,需要通过…