go并发编程以及socket通信的理解

go并发编程以及socket通信的理解

文章目录

一、管道的简单使用

" golang不是通过共享内存来通信,而是通过通信来共享内存 "

1、go简单初始化

go 复制代码
// golang不是通过共享内存来通信,而是通过通信来共享内存
func a1() {
	// 声明初始化 channel
	var ch chan string = make(chan string) // deadlock 会造成死锁,因为我们的管道是没有缓冲的
	// 内置的make函数有什么作用?
	// make 初始化内存,并且返回引用类型本身
	// new 只是将内存清零,返回的是指向类型的指针
	ch <- "hello" //阻塞写
	str := <-ch   //阻塞读
	fmt.Println(str)
	// 单向channel
	var ch1 chan<- string // 只能写
	var ch2 <-chan string // 只能读
	// 关闭channel
	close(ch1)
	x, ok := <-ch2
	if ok {
		fmt.Println(x)
	} else {
		fmt.Println("channel is closed")
	}
}

2、用 select 做一个简单的超时管理

go 复制代码
	// Go语言直接引入select关键字,用于处理异步问题
	var ch1, ch2 chan string
	select {
	case x := <-ch1: // 如果从ch1读取数据,那么执行此语句
		fmt.Println(x)
	case y := <-ch2: // 如果从ch2读取数据,那么执行此语句
		fmt.Println(y)
	default:
		fmt.Println("default")
	}

超时管理:

go 复制代码
func download(ch chan string) {
	for i := 1; i < 10; i++ {
		fmt.Println(i)
		time.Sleep(time.Second * 1)
	}
	ch <- "ok"
}
func a2() {
	// 超时处理
	timeout := make(chan int, 1)
	go func() {
		time.Sleep(time.Second * 3)
		timeout <- 1 // 用来标记超时,可以是任何非0值
	}()
	ch := make(chan string, 6) // 用于从download中接受数据
	go download(ch)
	select {
	case <-ch:
		fmt.Println("从ch中读取数据,执行正常业务处理") // 如果从ch中读取到数据那么正常处理业务
	case <-timeout:
		fmt.Println("3秒内没有从ch中读取数据,执行超时处理")// 如果从timeout中读取到数据,那么download执行超时
	}
}

3、编程体:通过go协程输出100个以内的任意两个数之和,减少等待。

go 复制代码
func add(i, x, y int) {
	fmt.Printf("%d + %d = %d\n", x, y, x+y)
}
func a3() {
	for i := 1; i <= 100; i++ {
		x := rand.Intn(100)
		time.Sleep(time.Millisecond)
		y := rand.Intn(100)
		go add(i, x, y)
	}
}

二、go中的socket实现通信

知识速记

go 复制代码
// 共享数据机制-sync
func s1() {
	// sync.Mutex
	//mutex := sync.Mutex{}

}

// 上下文机制 - Context

// socket 原理
/*
	互联网TCP/IP四层模型
	四层:数据层(帧)、网络层(IP)、传输层(TCP/UDP)、应用层(HTTP)
	通信:封包和解包
	抽象:应用程序到应用程序、进程到进程、主机到主机、设备到设备

*/

1、代码示例clinet.go和server.go

clinet.go

go 复制代码
package main
import (
	"bufio"
	"fmt"
	"net"
	"time"
)
func main() {
	// 与服务端建立连接
	conn, err := net.DialTimeout("tcp", "127.0.0.1:8899", time.Second)
	if err != nil {
		fmt.Printf("dial failed, err:%v\n", err)
		return
	}
	defer conn.Close()
	// 要发送的数据
	msg := []string{"hello world!", "golang", "c++", "python"}
	//  通过bufio方式发送
	writer := bufio.NewWriter(conn)
	for i, v := range msg {
		n, err := writer.Write([]byte(v))
		writer.Flush()
		if err != nil {
			fmt.Printf("write failed, err:%v\n", err)
			return
		}
		fmt.Printf("%d: write %d,bytes, data:%s\n", i, n, v)
		time.Sleep(time.Second)
	}
}

server.go

go 复制代码
// go 基于 socket 的 tcp 编程
func HandleConn(conn net.Conn) {
	fmt.Println("accepted a new connection!")
	defer conn.Close()
	for {
		// 负责缓存接受的数据
		buf := make([]byte, 32)
		n, err := conn.Read(buf) //表面上是 阻塞读
		if err != nil {
			fmt.Println("read err:", err)
			break
		}
		if n == 0 {
			continue
		}
		// 打印客户端发送的数据
		fmt.Printf("recv client data: %s\n", string(buf[:n]))
		// 发送数据给客户端
		conn.Write([]byte("hello client"))
	}
}
func main() {
	// 开始监听 8899 端口
	listen, err := net.Listen("tcp", ":8899")
	if err != nil {
		fmt.Println("listen err:", err)
		return
	}
	// 循环接受连接
	for {
		conn, err := listen.Accept()
		if err != nil {
			fmt.Println("accept err:", err)
			break
		}
		// 处理连接
		go HandleConn(conn)
	}
}

2、go基于 socket 的 tcp 编程

1、连接建立问题

连接拒绝:网络ping不通、ip或port指定错误、server未启动

listen backlog:增大server端listen backlog队列

网络延迟较大

2、读数据问题

无数据可读:goroutine阻塞即可

数据不足

超时

3、写数据问题

写阻塞

写入部分数据

4、线程安全问题

相关推荐
追逐时光者5 分钟前
Everything替代工具,一款基于 .NET 开源免费、高效且用户友好文件搜索工具!
后端·.net
爱学习的小鱼gogo10 分钟前
python 矩阵中寻找就接近的目标值 (矩阵-中等)含源码(八)
开发语言·经验分享·python·算法·职场和发展·矩阵
可爱的秋秋啊17 分钟前
简单网站编写
开发语言·前端
冬夜戏雪19 分钟前
[学习日记][springboot 1-7][leetcode 6道]
java·开发语言·学习
QX_hao20 分钟前
【Go】--数据类型
开发语言·后端·golang
h79971023 分钟前
redis lua脚本(go)调用教程以及debug调试
redis·golang·lua
趣味编程11127 分钟前
go的学习2---》并发编程
学习·golang·perl
桦说编程30 分钟前
线程池拒绝策略避坑:谨慎使用抛弃策略,可能导致系统卡死
java·后端
星秀日37 分钟前
框架--MyBatis
java·开发语言·mybatis
沐知全栈开发1 小时前
Ruby 条件判断
开发语言