发文件与聊天室
文章发布较早,内容可能过时,阅读注意甄别。
# 1,收发文件
做一个文件上传接收的一个服务。
# 1,原理
# 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
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
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
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
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,运行验证
先运行接收方代码,然后在运行发送方进行发送,此处需要留意发送的路径,似乎不能有中文。
# 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
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
上次更新: 2024/11/19, 23:11:42