初学GO不到两周,本着熟悉语言的目标写了这个小程序,漏洞很多,实现上写的也有些渣渣,欢迎大家阅读指点。
下载地址:https://github.com/yinxin630/gochat
简单思路描述:
0、服务端监听客户端请求,完成会话转发的任务
1、服务端采用心跳包维护用户在线状态
2、客户端通知服务端自己的监听地址,创建服务端-客户端信息通道
服务端:
复制代码
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194package main import ( "fmt" "net" "os" "strconv" "time" ) //用户信息 type User struct { userName string userAddr *net.UDPAddr userListenConn *net.UDPConn chatToConn *net.UDPConn } //服务器监听端口 const LISTENPORT = 1616 //缓冲区 const BUFFSIZE = 1024 var buff = make([]byte, BUFFSIZE) //在线用户 var onlineUser = make([]User, 0) //在线状态判断缓冲区 var onlineCheckAddr = make([]*net.UDPAddr, 0) //错误处理 func HandleError(err error) { if err != nil { fmt.Println(err.Error()) os.Exit(2) } } //消息处理 func HandleMessage(udpListener *net.UDPConn) { n, addr, err := udpListener.ReadFromUDP(buff) HandleError(err) if n > 0 { msg := AnalyzeMessage(buff, n) switch msg[0] { //连接信息 case "connect ": //获取昵称+端口 userName := msg[1] userListenPort := msg[2] //获取用户ip ip := AnalyzeMessage([]byte(addr.String()), len(addr.String())) //显示登录信息 fmt.Println(" 昵称:", userName, " 地址:", ip[0], " 用户监听端口:", userListenPort, " 登录成功!") //创建对用户的连接,用于消息转发 userAddr, err := net.ResolveUDPAddr("udp4", ip[0] + ":" + userListenPort) HandleError(err) userConn, err := net.DialUDP("udp4", nil, userAddr) HandleError(err) //因为连接要持续使用,不能在这里关闭连接 //defer userConn.Close() //添加到在线用户 onlineUser = append(onlineUser, User{userName, addr, userConn, nil}) case "online ": //收到心跳包 onlineCheckAddr = append(onlineCheckAddr, addr) case "outline ": //退出消息,未实现 case "chat ": //会话请求 //寻找请求对象 index := -1 for i := 0; i < len(onlineUser); i++ { if onlineUser[i].userName == msg[1] { index = i } } //将所请求对象的连接添加到请求者中 if index != -1 { nowUser, _ := FindUser(addr) onlineUser[nowUser].chatToConn = onlineUser[index].userListenConn } case "get ": //向请求者返回在线用户信息 index, _ := FindUser(addr) onlineUser[index].userListenConn.Write([]byte("当前共有" + strconv.Itoa(len(onlineUser)) + "位用户在线")) for i, v := range onlineUser { onlineUser[index].userListenConn.Write([]byte("" + strconv.Itoa(i + 1) + ":" + v.userName)) } default: //消息转发 //获取当前用户 index, _ := FindUser(addr) //获取时间 nowTime := time.Now() nowHour := strconv.Itoa(nowTime.Hour()) nowMinute := strconv.Itoa(nowTime.Minute()) nowSecond := strconv.Itoa(nowTime.Second()) //请求会话对象是否存在 if onlineUser[index].chatToConn == nil { onlineUser[index].userListenConn.Write([]byte("对方不在线")) } else { onlineUser[index].chatToConn.Write([]byte(onlineUser[index].userName + " " + nowHour + ":" + nowMinute + ":" + nowSecond + "n" + msg[0])) } } } } //消息解析,[]byte -> []string func AnalyzeMessage(buff []byte, len int) ([]string) { analMsg := make([]string, 0) strNow := "" for i := 0; i < len; i++ { if string(buff[i:i + 1]) == ":" { analMsg = append(analMsg, strNow) strNow = "" } else { strNow += string(buff[i:i + 1]) } } analMsg = append(analMsg, strNow) return analMsg } //寻找用户,返回(位置,是否存在) func FindUser(addr *net.UDPAddr) (int, bool) { alreadyhave := false index := -1 for i := 0; i < len(onlineUser); i++ { if onlineUser[i].userAddr.String() == addr.String() { alreadyhave = true index = i break } } return index, alreadyhave } //处理用户在线信息(暂时仅作删除用户使用) func HandleOnlineMessage(addr *net.UDPAddr, state bool) { index, alreadyhave := FindUser(addr) if state == false { if alreadyhave { onlineUser = append(onlineUser[:index], onlineUser[index + 1:len(onlineUser)]...) } } } //在线判断,心跳包处理,每5s查看一次所有已在线用户状态 func OnlineCheck() { for { onlineCheckAddr = make([]*net.UDPAddr, 0) sleepTimer := time.NewTimer(time.Second * 5) <- sleepTimer.C for i := 0; i < len(onlineUser); i++ { haved := false FORIN:for j := 0; j < len(onlineCheckAddr); j++ { if onlineUser[i].userAddr.String() == onlineCheckAddr[j].String() { haved = true break FORIN } } if !haved { fmt.Println(onlineUser[i].userAddr.String() + "退出!") HandleOnlineMessage(onlineUser[i].userAddr, false) i-- } } } } func main() { //监听地址 udpAddr, err := net.ResolveUDPAddr("udp4", "127.0.0.1:" + strconv.Itoa(LISTENPORT)) HandleError(err) //监听连接 udpListener, err := net.ListenUDP("udp4", udpAddr) HandleError(err) defer udpListener.Close() fmt.Println("开始监听:") //在线状态判断 go OnlineCheck() for { //消息处理 HandleMessage(udpListener) } }
客户端:
复制代码
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152package main import ( "fmt" "os" "strconv" "net" "bufio" "math/rand" "time" ) //数据包头,标识数据内容 var reflectString = map[string]string { "连接": "connect :", "在线": "online :", "聊天": "chat :", "在线用户": "get :", } //服务器端口 const CLIENTPORT = 1616 //数据缓冲区 const BUFFSIZE = 1024 var buff = make([]byte, BUFFSIZE) //错误消息处理 func HandleError(err error) { if err != nil { fmt.Println(err.Error()) os.Exit(2) } } //发送消息 func SendMessage(udpConn *net.UDPConn) { scaner := bufio.NewScanner(os.Stdin) for scaner.Scan() { if scaner.Text() == "exit" { return } udpConn.Write([]byte(scaner.Text())) } } //接收消息 func HandleMessage(udpListener *net.UDPConn) { for { n, _, err := udpListener.ReadFromUDP(buff) HandleError(err) if n > 0 { fmt.Println(string(buff[:n])) } } } /* func AnalyzeMessage(buff []byte, len int) ([]string) { analMsg := make([]string, 0) strNow := "" for i := 0; i < len; i++ { if string(buff[i:i + 1]) == ":" { analMsg = append(analMsg, strNow) strNow = "" } else { strNow += string(buff[i:i + 1]) } } analMsg = append(analMsg, strNow) return analMsg }*/ //发送心跳包 func SendOnlineMessage(udpConn *net.UDPConn) { for { //每间隔1s向服务器发送一次在线信息 udpConn.Write([]byte(reflectString["在线"])) sleepTimer := time.NewTimer(time.Second) <- sleepTimer.C } } func main() { //判断命令行参数,参数应该为服务器ip if len(os.Args) != 2 { fmt.Println("程序命令行参数错误!") os.Exit(2) } //获取ip host := os.Args[1] //udp地址 udpAddr, err := net.ResolveUDPAddr("udp4", host + ":" + strconv.Itoa(CLIENTPORT)) HandleError(err) //udp连接 udpConn, err := net.DialUDP("udp4", nil, udpAddr) HandleError(err) //本地监听端口 newSeed := rand.NewSource(int64(time.Now().Second())) newRand := rand.New(newSeed) randPort := newRand.Intn(30000) + 10000 //本地监听udp地址 udpLocalAddr, err := net.ResolveUDPAddr("udp4", "127.0.0.1:" + strconv.Itoa(randPort)) HandleError(err) //本地监听udp连接 udpListener, err := net.ListenUDP("udp4", udpLocalAddr) HandleError(err) //fmt.Println("监听", randPort, "端口") //用户昵称 userName := "" fmt.Printf("请输入昵称:") fmt.Scanf("%s", &userName) //向服务器发送连接信息(昵称+本地监听端口) udpConn.Write([]byte(reflectString["连接"] + userName + ":" + strconv.Itoa(randPort))) //关闭端口 defer udpConn.Close() defer udpListener.Close() //发送心跳包 go SendOnlineMessage(udpConn) //接收消息 go HandleMessage(udpListener) command := "" for { //获取命令 fmt.Scanf("%s", &command) switch command { case "chat" : people := "" //fmt.Printf("请输入对方昵称:") fmt.Scanf("%s", &people) //向服务器发送聊天对象信息 udpConn.Write([]byte(reflectString["聊天"] + people)) //进入会话 SendMessage(udpConn) //退出会话 fmt.Println("退出与" + people + "的会话") case "get" : //请求在线情况信息 udpConn.Write([]byte(reflectString["在线用户"])) } } }
运行图:
服务端:
客户端:
用户一:
用户二:
用户三:
最后
以上就是高挑冰棍最近收集整理的关于[golang]GO语言编写的基于UDP协议的简易聊天软件的全部内容,更多相关[golang]GO语言编写内容请搜索靠谱客的其他文章。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复