Go Socket
Golang之Socket
go创建socket很简单

package main import ( "fmt" "net" ) func main() { //服务器监听地址 fmt.Println("start server...") listen, err := net.Listen("tcp", "127.0.0.1:50000") if err != nil { fmt.Println("listen failed,err:", err) return } //接受客户端信息 for { conn, err := listen.Accept() if err != nil { fmt.Println("accept failed,err:", err) continue } //用协程建立连接 go process(conn) } } //读取数据 func process(conn net.Conn) { defer conn.Close() for { buf := make([]byte, 512) n, err := conn.Read(buf) if err != nil { fmt.Println("read err:", err) return } fmt.Printf(string(buf[0:n])) } }

package main import ( "bufio" "fmt" "net" "os" "strings" ) func main() { conn, err := net.Dial("tcp", "localhost:50000") if err != nil { fmt.Println("Error dialing", err.Error()) return } defer conn.Close() inputReader := bufio.NewReader(os.Stdin) for { input, _ := inputReader.ReadString('\n') trimmedInput := strings.Trim(input, "\r\n") if trimmedInput == "Q" { return } _, err = conn.Write([]byte(trimmedInput)) if err != nil { return } } }
发送HTTP

package main import ( "fmt" "io" "net" ) func main() { conn, err := net.Dial("tcp", "www.baidu.com:80") if err != nil { fmt.Println("Error dialing", err.Error()) return } defer conn.Close() msg := "GET / HTTP/1.1\r\n" msg += "Host:www.baidu.com\r\n" msg += "Connection:keep-alive\r\n" //msg += "User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36\r\n" msg += "\r\n\r\n" //io.WriteString(os.Stdout, msg) n, err := io.WriteString(conn, msg) if err != nil { fmt.Println("write string failed, ", err) return } fmt.Println("send to baidu.com bytes:", n) buf := make([]byte, 4096) for { count, err := conn.Read(buf) fmt.Println("count:", count, "err:", err) if err != nil { break } fmt.Println(string(buf[0:count])) } }
Golang之并发篇
进程和线程
A。进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。
B。线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。
C。一个进程可以创建和撤销多个线程;同一进程中的多个线程之间可以并发执行。
并发和并行
并发:多线程程序在一个核的cpu上运行
并行:多线程程序在多个核的cpu上运行
举例。。一个妈给一个碗给多个小孩喂饭,,是并发
一个妈给每个小孩一人一个碗,就是并行
并发 并行
协程和线程
协程:独立的栈空间,共享堆空间,调度由用户自己控制,本质上有点类似于用户级协程,这些用户级线程的调度也是自己实现的。
线程:一个线程上可以跑多个协程,协程是轻量级的线程。
例子
package main import ( "fmt" "time" ) func test() { var i int for { fmt.Println(i) time.Sleep(time.Second) i++ } } func main() { go test() //起一个协程执行test() for { fmt.Println("i : runnging in main") time.Sleep(time.Second ) } }
-
--
设置Golang运行的cpu核数。
1.8版本以上,默认跑多个核
package main import ( "fmt" "runtime" ) func main() { num := runtime.NumCPU() runtime.GOMAXPROCS(num) fmt.Println(num) }
并发计算

package main import ( "fmt" "sync" "time" ) var ( m = make(map[int]uint64) lock sync.Mutex ) type task struct { n int } func calc(t *task) { var sum uint64 sum = 1 for i := 1; i < t.n; i++ { sum *= uint64(i) } fmt.Println(t.n, sum) lock.Lock() m[t.n] = sum lock.Unlock() } func main() { for i := 0; i < 16; i++ { t := &task{n: i} go calc(t)//并发执行,谁快谁先出 } time.Sleep(10 * time.Second) //lock.Lock() //for k, v := range m { // fmt.Printf("%d!=%v\n", k, v) //} //lock.Unlock() }
Channel
channel概念
a。类似unix中的管道(pipe)
b.先进先出
c。线程安全,多个goroutine同时访问,不需要枷锁
d。channel是有类型的,一个整数的channel只能存整数
channel声明
var name chan string var age chan int var mapchan chan map[string]string var test chan student var test1 chan *student
channel初始化
使用make进行初始化 var test chan int test = make(chan int,10) var test chan string test=make(chan string,10)
channel基本操作
从channel读取数据
var testChan chan int testChan = make(chan int, 10) var a int a = <-testChan
-从channel写入数据
var testChan chan int testChan = make(chan int, 10) var a int = 10 testChan <- a
第一个例子
package main import "fmt" type student struct { name string } func main() { var stuChan chan *student stuChan = make(chan *student, 10) stu := student{name: "stu001"} stuChan <- &stu var stu01 interface{} stu01 = <-stuChan var stu02 *student stu02, ok := stu01.(*student) if !ok { fmt.Println("can not convert") return } fmt.Println(stu02) }
goroutine与channel
package main import ( "fmt" "time" ) //起一个读的协程 func write(ch chan int) { for i := 0; i < 1000; i++ { ch <- i } } func read(ch chan int) { for { var b int b = <-ch fmt.Println(b) } } func main() { intChan := make(chan int, 10) go write(intChan) go read(intChan) time.Sleep(10 * time.Second) }
读,取,字符串

package main import ( "fmt" "time" ) func sendData(ch chan string) { ch <- "Washington" ch <- "Tripoli" ch <- "London" ch <- "Beijing" ch <- "Tokio" } func getData(ch chan string) { var input string for { input = <-ch fmt.Println(input) } } func main() { ch := make(chan string) go sendData(ch) go getData(ch) time.Sleep(3 * time.Second) }
Channel缓冲区
只能放一个元素的testChan var testChan chan int testChan = make(chan int) var a int a = <-testChan
-
testChan是带缓冲区的chan,一次可以放10个元素
var testChan chan int testChan = make(chan int, 10) var a int = 10 testChan <- a
-

package main import ( "fmt" "sync" "time" ) var ( m = make(map[int]uint64) lock sync.Mutex ) type task struct { n int } func calc(t *task) { var sum uint64 sum = 1 for i := 1; i < t.n; i++ { sum *= uint64(i) } fmt.Println(t.n, sum) lock.Lock() m[t.n] = sum lock.Unlock() } func main() { for i := 0; i < 16; i++ { t := &task{n: i} go calc(t) } time.Sleep(10 * time.Second) lock.Lock() for k, v := range m { fmt.Printf("%d!=%v\n", k, v) } lock.Unlock() }

package main import ( "fmt" ) func calc(taskChan chan int, resChan chan int, exitChan chan bool) { for v := range taskChan { flag := true for i := 2; i < v; i++ { if v%i == 0 { flag = false break } } if flag { resChan <- v } } fmt.Println("exit") exitChan <- true } func main() { intChan := make(chan int, 1000) resultChan := make(chan int, 1000) exitChan := make(chan bool, 8) go func() { for i := 0; i < 10000; i++ { intChan <- i } close(intChan) }() for i := 0; i < 8; i++ { go calc(intChan, resultChan, exitChan) } //等待所有计算的goroutine全部退出 go func() { for i := 0; i < 8; i++ { <-exitChan fmt.Println("wait goroute", i, "exited") } close(resultChan) }() for v := range resultChan { fmt.Println(v) } }

package main import "fmt" func send(ch chan int, exitChan chan struct{}) { for i := 0; i < 10; i++ { ch <- i } close(ch) var a struct{} exitChan <- a } func recv(ch chan int, exitChan chan struct{}) { for { v, ok := <-ch if !ok { break } fmt.Println(v) } var a struct{} exitChan <- a } func main() { var ch chan int ch = make(chan int, 10) exitChan := make(chan struct{}, 2) go send(ch, exitChan) go recv(ch, exitChan) var total = 0 for _ = range exitChan { total++ if total == 2 { break } } }
检测管道是否关闭

package main import "fmt" func main() { var ch chan int ch = make(chan int, 10) for i := 0; i < 10; i++ { ch <- i } close(ch) for { var b int b, ok := <-ch //检测管道是否关闭 if ok == false { fmt.Println("chan is close") break } fmt.Println(b) } }
如何关闭chan
1. 使用内置函数close进行关闭,chan关闭之后,for range遍历chan中 已经存在的元素后结束 2. 使用内置函数close进行关闭,chan关闭之后,没有使用for range的写法 需要使用,v, ok := <- ch进行判断chan是否关闭
chan的只读和只写
只读的声明 Var 变量的名字 <-chan int Var readChan <- chan int 只写的声明 Var 变量的名字 chan<- int Var writeChan chan<- int

package main import "fmt" //只写chan func send(ch chan<- int, exitChan chan struct{}) { for i := 0; i < 10; i++ { ch <- i } close(ch) var a struct{} exitChan <- a } //只读chan func recv(ch <-chan int, exitChan chan struct{}) { for { v, ok := <-ch if !ok { break } fmt.Println(v) } var a struct{} exitChan <- a } func main() { var ch chan int ch = make(chan int, 10) //初始化chan exitChan := make(chan struct{}, 2) go send(ch, exitChan) go recv(ch, exitChan) var total = 0 for _ = range exitChan { total++ if total == 2 { break } } }
不阻塞

package main import "fmt" import "time" func main() { var ch chan int ch = make(chan int, 10) ch2 := make(chan int, 10) go func() { var i int for { ch <- i time.Sleep(time.Second) ch2 <- i * i time.Sleep(time.Second) i++ } }() for { select { case v := <-ch: fmt.Println(v) case v := <-ch2: fmt.Println(v) case <-time.After(time.Second): fmt.Println("get data timeout") time.Sleep(time.Second) } } }
Golang之定时器,recover
滴答滴答……定时器的使用
package main import ( "fmt" "time" ) //定时器的使用 func main() { t := time.NewTicker(time.Second) //t.C是个channel,背后是个goroutine for v := range t.C { fmt.Println("hello,", v) } }
一次定时器
package main import ( "fmt" "time" ) //定时器的使用 func main() { select { //5秒之后触发 case <-time.After(5*time.Second): fmt.Println("after") } }
超时控制
package main import ( "fmt" "time" ) func queryDb(ch chan int) { time.Sleep(time.Second) ch <- 100 } func main() { ch := make(chan int) go queryDb(ch) t := time.NewTicker(time.Second) select { case v := <-ch: fmt.Println("result", v) case <-t.C: fmt.Println("timeout") } }
goroutine中使用recover
应用场景,如果某个goroutine panic了,而且这个goroutine里面没有捕获(recover),
那么整个进程就会挂掉。所以好的习惯是每当go产生一个goroutine,就需要写下recover
捕获goroutine的错误
package main import ( "fmt" "runtime" "time" ) func test() { defer func() { if err := recover(); err != nil { fmt.Println("panic:", err) } }() var m map[string]int m["stu"] = 100 } func calc() { for { fmt.Println("i'm calc") time.Sleep(time.Second) } } func main() { num := runtime.NumCPU() runtime.GOMAXPROCS(num - 1) go test() for i := 0; i < 2; i++ { go calc() } time.Sleep(time.Second * 10000) }
Golang之单元测试
文件名必须以_test.go结尾
使用go test 执行单元测试
例

package main func add(a, b int) int { return a + b } func sub(a, b int) int { return a - b }

package main import ( "testing" ) func TestAdd(t *testing.T) { r := add(2, 4) if r != 6 { t.Fatalf("add(2,4) error,expect:%d,actual:%d", 6, r) } t.Logf("test add succ") } func TestSub(t *testing.T) { r := sub(2, 4) if r != -2 { t.Fatalf("sub(2,4) error,expect:%d,actual:%d", 6, r) } t.Logf("test sub succ") }

package main
运行:
E:\project\src\go_dev\day8\test>go test -v === RUN TestAdd --- PASS: TestAdd (0.00s) calc_test.go:13: test add succ === RUN TestSub --- PASS: TestSub (0.00s) calc_test.go:20: test sub succ PASS ok go_dev/day8/test 0.142s
Golang之定义错误(errors)
基本示例:
package main //定义错误 //error 也是个接口 import ( "errors" "fmt" ) var errNotFound error = errors.New("Not found error") func main() { fmt.Printf("error:%v", errNotFound) }
错误处理:

package main import ( "fmt" "os" "time" ) type PathError struct { path string op string createTime string message string } func (p *PathError) Error() string { return fmt.Sprintf("path=%s op=%s createTime=%s message=%s", p.path, p.op, p.createTime, p.message) } func Open(filename string) error { file, err := os.Open(filename) if err != nil { return &PathError{ path: filename, op: "read", message: err.Error(), createTime: fmt.Sprintf("%v", time.Now()), } } defer file.Close() return nil } func main() { err := Open("D:/project/src/go_dev/day7/example10/123.txt") switch v := err.(type) { case *PathError: fmt.Println("get path error,", v) default: } }
练习
package main import "fmt" func badCall() { panic("bad end") } func test() { defer func() { if e := recover(); e != nil { fmt.Printf("Panicking%s\r\n", e) } }() badCall() fmt.Printf("After bad call\r\n") } func main() { fmt.Printf("Calling test\r\n") test() }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构