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、线程安全问题

相关推荐
Harold4 分钟前
【用户访问鉴权】Openresty实现方案
后端
用户9704438781167 分钟前
PHP 函数的参数顺序,它们是随机的吗?
后端·程序员·代码规范
3学习分享吧8 分钟前
C++从0实现百万并发Reactor服务器(完结)
后端
Korloa11 分钟前
表达式(CSP-J 2021-Expr)题目详解
c语言·开发语言·数据结构·c++·算法·蓝桥杯·个人开发
lssjzmn26 分钟前
java中,synchronized 关键字与 ReentrantLock 重入锁的区别以及应用场景,注意事项
java·后端
南雨北斗34 分钟前
词性
后端
南雨北斗36 分钟前
动词的类型
后端
小厂永远得不到的男人39 分钟前
ioc 原理篇
java·后端
yodala43 分钟前
C++中的内存管理(二)
开发语言·c++
屁股割了还要学1 小时前
【数据结构入门】排序算法:插入排序
c语言·开发语言·数据结构·算法·青少年编程·排序算法