二丫讲梵 二丫讲梵
首页
  • 最佳实践
  • 迎刃而解
  • Nginx
  • Php
  • Zabbix
  • AWS
  • Prometheus
  • Grafana
  • CentOS
  • Systemd
  • Docker
  • Rancher
  • Ansible
  • Ldap
  • Gitlab
  • GitHub
  • Etcd
  • Consul
  • RabbitMQ
  • Kafka
  • MySql
  • MongoDB
  • OpenVPN
  • KVM
  • VMware
  • Other
  • ELK
  • K8S
  • LLM
  • Nexus
  • Jenkins
  • 随写编年
  • 家人物语
  • 追忆青春
  • 父亲的朋友圈
  • 电影音乐
  • 效率工具
  • 博客相关
  • Shell
  • 前端实践
  • Vue学习笔记
  • Golang学习笔记
  • Golang编程技巧
  • 学习周刊
  • Obsidian插件周刊
关于
友链
  • 本站索引

    • 分类
    • 标签
    • 归档
  • 本站页面

    • 导航
    • 打赏
  • 我的工具

    • 备忘录清单 (opens new window)
    • json2go (opens new window)
    • gopher (opens new window)
    • 微信MD编辑 (opens new window)
    • 国内镜像 (opens new window)
    • 出口IP查询 (opens new window)
    • 代码高亮工具 (opens new window)
  • 外站页面

    • 开往 (opens new window)
    • ldapdoc (opens new window)
    • HowToStartOpenSource (opens new window)
    • vdoing-template (opens new window)
GitHub (opens new window)

二丫讲梵

行者常至,为者常成
首页
  • 最佳实践
  • 迎刃而解
  • Nginx
  • Php
  • Zabbix
  • AWS
  • Prometheus
  • Grafana
  • CentOS
  • Systemd
  • Docker
  • Rancher
  • Ansible
  • Ldap
  • Gitlab
  • GitHub
  • Etcd
  • Consul
  • RabbitMQ
  • Kafka
  • MySql
  • MongoDB
  • OpenVPN
  • KVM
  • VMware
  • Other
  • ELK
  • K8S
  • LLM
  • Nexus
  • Jenkins
  • 随写编年
  • 家人物语
  • 追忆青春
  • 父亲的朋友圈
  • 电影音乐
  • 效率工具
  • 博客相关
  • Shell
  • 前端实践
  • Vue学习笔记
  • Golang学习笔记
  • Golang编程技巧
  • 学习周刊
  • Obsidian插件周刊
关于
友链
  • 本站索引

    • 分类
    • 标签
    • 归档
  • 本站页面

    • 导航
    • 打赏
  • 我的工具

    • 备忘录清单 (opens new window)
    • json2go (opens new window)
    • gopher (opens new window)
    • 微信MD编辑 (opens new window)
    • 国内镜像 (opens new window)
    • 出口IP查询 (opens new window)
    • 代码高亮工具 (opens new window)
  • 外站页面

    • 开往 (opens new window)
    • ldapdoc (opens new window)
    • HowToStartOpenSource (opens new window)
    • vdoing-template (opens new window)
GitHub (opens new window)
  • Shell编程

  • Go编程笔记

  • 前端编程笔记

  • Go学习笔记

    • 基础部分

      • 基础知识
      • 数据类型
      • fmt的格式化输入输出
      • 运算符
      • 流程控制
      • 认识函数
      • 包的操作
      • 项目工程-在同级目录
      • 指针(pointer)
      • 数组(array)
      • 切片(slice)
      • 字典(map)
      • 结构体(struct)
      • 匿名组合
      • 方法
      • 接口
      • error接口
      • panic
      • recover
      • 字符串操作
      • 字符串转换
      • 正则表达式
      • JSON处理
      • 文件的操作
      • goroutine
      • channel
      • select
      • sync-lock锁
      • 网络概述
      • Socket编程
      • 发文件与聊天室
        • 1,收发文件
          • 1,原理
          • 2,os.stat 的使用。
          • 3,发送方。
          • 4,接收方。
          • 5,运行验证
        • 2,并发聊天室
      • http编程
      • 爬虫实战
      • 单元测试了解
    • web框架

    • orm框架

  • Vue-21年学习笔记

  • Vue-22年重学笔记

  • 编程世界
  • Go学习笔记
  • 基础部分
二丫讲梵
2021-07-10
目录

发文件与聊天室

文章发布较早,内容可能过时,阅读注意甄别。

# 1,收发文件

做一个文件上传接收的一个服务。

# 1,原理

HTB1lQAydG5s3KVjSZFN763D3FXaC

# 2,os.stat 的使用。

通过这个函数可以获取到文件对应的一些状态信息,如下举例:

package main

import (
	"fmt"
	"os"
)

func main() {
	list := os.Args
	if len(list) != 2 {
		fmt.Println("usage: xxx file")
		return
	}
	filename := list[1]
	info, err := os.Stat(filename)
	if err != nil {
		fmt.Println("err = ", err)
		return
	}
	fmt.Println("file name is ", info.Name())
	fmt.Println("file size is ", info.Size())

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

运行结果如下:

$ go run 04_os.stat的使用.go '/d/all-in/39,go语言教程/letsgo/1801-Go语言视频零 基础入门到精通项目实战web编程Golang 2018年新教程/第01套-Go语言快速入门(2018年2 月更新精品,推荐观看)/第7天视频/01_昨日回顾.mp4'
file name is  01_昨日回顾.mp4
file size is  67010611
1
2
3

# 3,发送方。

先来写一个发送端代码:

package main

import (
	"fmt"
	"io"
	"net"
	"os"
)

//发送文件内容
func SendFile(path string, conn net.Conn) {
	//以只读方式打开文件
	f, err := os.Open(path)
	if err != nil {
		fmt.Println("os.Open err = ", err)
		return
	}
	defer f.Close()

	buf := make([]byte, 1024*4)
	//读多少内容,发送多少
	for {
		n, err := f.Read(buf) //从文件读取内容
		if err != nil {
			if err == io.EOF {
				fmt.Println("文件发送完毕")
			} else {
				fmt.Println("f.Read err = ", err)
			}
			return
		}
		//发送内容
		conn.Write(buf[:n]) //给服务器发送内容
	}

}
func main() {
	//提示输入文件
	fmt.Println("请输入需要传输的文件:")
	var path string
	fmt.Scan(&path)

	//获取文件名 info.name()
	info, err := os.Stat(path)
	if err != nil {
		fmt.Println("os.Stat err = ", err)
		return
	}

	//主动连接服务器
	conn, err := net.Dial("tcp", "127.0.0.1:8000")
	if err != nil {
		fmt.Println("net.Dial err = ", err)
		return
	}
	defer conn.Close()

	//先给接收方发送文件名
	_, err = conn.Write([]byte(info.Name()))
	if err != nil {
		fmt.Println("conn.Write err = ", err)
		return
	}

	//判断接收方的回复,如果回复"ok",说明对方准备好,可以发送文件
	var n int
	buf := make([]byte, 1024)
	n, err = conn.Read(buf)
	if err != nil {
		fmt.Println("conn.Read err = ", err)
		return
	}
	if "ok" == string(buf[:n]) {
		//发送文件内容
		SendFile(path, conn)
	}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78

# 4,接收方。

然后是接收方:

package main

import (
	"fmt"
	"io"
	"net"
	"os"
)

//接受文件内容
func RecvFile(FileName string, conn net.Conn) {
	//新建文件
	f, err := os.Create(FileName)
	if err != nil {
		fmt.Println("os.Create err = ", err)
		return
	}
	buf := make([]byte, 1024*4)
	//接受多少,写入多少,一点不差
	for {
		n, err := conn.Read(buf)
		if err != nil {
			if err == io.EOF {
				fmt.Println("文件接收完毕")
			} else {
				fmt.Println("conn.Read err = ", err)

			}
			return
		}
		f.Write(buf[:n])
	}
}
func main() {
	//监听
	listenner, err := net.Listen("tcp", "127.0.0.1:8000")
	if err != nil {
		fmt.Println("net.Listen err = ", err)
		return
	}
	defer listenner.Close()

	//阻塞等待用户连接
	conn, err := listenner.Accept()
	if err != nil {
		fmt.Println("listenner.Accept err = ", err)
		return
	}
	defer conn.Close()
	buf := make([]byte, 1024)
	var n int
	n, err = conn.Read(buf)
	if err != nil {
		fmt.Println("conn.Read err = ", err)
		return
	}
	FileName := string(buf[:n])

	//回复"ok"
	conn.Write([]byte("ok"))
	//接受文件内容
	RecvFile(FileName, conn)

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64

# 5,运行验证

先运行接收方代码,然后在运行发送方进行发送,此处需要留意发送的路径,似乎不能有中文。

HTB1TFovdRGw3KVjSZFw762Q2FXag

# 2,并发聊天室

代码内容:

package main

import (
	"fmt"
	"net"
	"strings"
	"time"
)

type Client struct {
	C    chan string //用户发送数据的管道
	Name string      //用户名
	Addr string      //网络地址
}

//保存在线用户,使用map,cliAddr ===> Client
var onlineMap map[string]Client

var message = make(chan string)

//新开一个协程,转发消息,只要有消息来了,就遍历map,给map中每个成员都发送此消息
func Manager() {
	//给map分配空间
	onlineMap = make(map[string]Client)
	for {
		msg := <-message //没有消息前,阻塞等待
		//遍历map,给map中每个成员都发送此消息
		for _, cli := range onlineMap {
			cli.C <- msg
		}
	}
}

//给用户发信息
func WriteMsgToClient(cli Client, conn net.Conn) {
	for msg := range cli.C {
		conn.Write([]byte(msg + "\n"))
	}
}

func MakeMsg(cli Client, msg string) (buf string) {
	buf = "[" + cli.Addr + "]" + cli.Name + ": " + msg
	return
}

func HandleConn(conn net.Conn) {
	//获取客户端的网络地址
	cliAddr := conn.RemoteAddr().String()

	//创建一个结构体,默认情况下,用户名和网络地址一样
	cli := Client{make(chan string), cliAddr, cliAddr}
	//把结构体添加到map
	onlineMap[cliAddr] = cli
	//新开一个协程,专门给客户端发送信息
	go WriteMsgToClient(cli, conn)

	//广播用户在线
	//message<-"[" + cli.Addr + "]" + cli.Name + ": login"
	message <- MakeMsg(cli, "login")
	//提示我是谁
	cli.C <- MakeMsg(cli, "I m here")

	isQuit := make(chan bool)//对方是否主动退出
	hasData := make(chan bool)//对方是否有数据
	//新建一个协程,接收用户发送过来的数据
	go func() {
		buf := make([]byte, 2048)
		for {
			n, err := conn.Read(buf)
			if n == 0 { //对方断开,或者出问题
				isQuit <- true
				fmt.Println("conn.Read err = ", err)
				return
			}
			msg := string(buf[:n-1])
			if len(msg) == 3 && msg == "who" {
				//遍历map,给当前用户发送所有在线用户
				conn.Write([]byte("user list:\n"))
				for _, tmp := range onlineMap {
					msg = tmp.Addr + ":" + tmp.Name + "\n"
					conn.Write([]byte(msg))
				}
			} else if len(msg) >= 8 && msg[:6] == "rename" {
				//rename|mike
				name := strings.Split(msg, "|")[1]
				cli.Name = name
				onlineMap[cliAddr] = cli
				conn.Write([]byte("rename success\n"))
			} else { //转发此内容
				message <- MakeMsg(cli, msg)
			}
			hasData <- true //代表有数据
		}
	}()
	for {
		//通过select检测channel的流动
		select {
		case <-isQuit:
			delete(onlineMap, cliAddr)//当前用户从map中删除
			message <- MakeMsg(cli, "login out")//广播谁下线了
			return

		case <-hasData:

		case <-time.After(10 * time.Second): //60s后退出
			delete(onlineMap, cliAddr)
			message <- MakeMsg(cli, "time out leave out")
			return
		}
	}

}
func main() {
	//监听
	listener, err := net.Listen("tcp", ":8000")
	if err != nil {
		fmt.Println("net.Listen err = ", err)
		return
	}
	defer listener.Close()

	//新开一个协程,转发消息,只要有消息来了,就遍历map,给map中每个成员都发送此消息
	go Manager()

	//主协程,阻塞循环等待用户连接
	for {
		conn, err := listener.Accept()
		if err != nil {
			fmt.Println("listener.Accept err = ", err)
			continue
		}
		go HandleConn(conn) //处理用户连接
	}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
微信 支付宝
#go
上次更新: 2024/07/04, 22:40:37
Socket编程
http编程

← Socket编程 http编程→

最近更新
01
学习周刊-总第213期-2025年第22周
05-29
02
学习周刊-总第212期-2025年第21周
05-22
03
从赵心童世锦赛夺冠聊聊我的斯诺克情缘
05-16
更多文章>
Theme by Vdoing | Copyright © 2017-2025 | 点击查看十年之约 | 浙ICP备18057030号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式