├── udpHole.png
├── client
├── client
└── client.go
├── server
├── server
└── server.go
├── .idea
├── encodings.xml
├── misc.xml
├── modules.xml
├── udpnat.iml
└── workspace.xml
├── common
└── common.go
└── README.md
/udpHole.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderInk/udpnat/HEAD/udpHole.png
--------------------------------------------------------------------------------
/client/client:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderInk/udpnat/HEAD/client/client
--------------------------------------------------------------------------------
/server/server:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderInk/udpnat/HEAD/server/server
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/udpnat.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/common/common.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | import "time"
4 |
5 | //Client给服务器发送消息
6 | const (
7 | LOGIN byte = iota //Client登录时消息头
8 | LOGOUT
9 | P2PTRANS //Client向服务器发送的协助打洞头
10 | GETALLUSER
11 | GETUSER
12 | )
13 |
14 | //Server默认监听端口和ip
15 | const SERVER_PORT = 8848
16 | const SERVER_IP = "0.0.0.0"
17 |
18 | const CLIENT_PORT = 8900
19 | const CLIENT_IP = "0.0.0.0"
20 |
21 |
22 | //发送消息长度
23 | const BUFFSIZE = 4096
24 |
25 | //Client登录时向服务器发送的消息
26 | type StLoginMessage struct {
27 | UserName string
28 | }
29 |
30 | //Client注销时发送的消息
31 | type StLogoutMessage struct {
32 | UserName string
33 | }
34 |
35 | //客户节点信息
36 | type StUserListNode struct {
37 | UserInfo StLoginMessage
38 | Ip string
39 | Port int
40 | }
41 |
42 | //Server向Client发送的消息
43 | type StServerToClient struct {
44 | User StUserListNode
45 | }
46 |
47 | //Client打洞握手时的消息
48 | type Handshack struct {
49 | From string
50 | To string
51 | Message string
52 | }
53 |
54 | /*********************************************
55 | 下面的协议用于客户端之间的通信
56 | ********************************************/
57 | const (
58 | P2PMESSAGE byte = iota
59 | P2PTRANSMSG
60 | USERCOUNT
61 |
62 | P2PTRANSACK
63 |
64 | //服务器回客户端消息
65 | USERINFO
66 | S2PTRANSMSG
67 | ERRORMSG
68 | )
69 |
70 | const P2PSHAKETIMEOUT = time.Second * 2
71 |
72 |
73 | //客户端之间的发送消息格式
74 | type StP2PMessage struct {
75 | IMessageType int
76 | Ip string
77 | Port int
78 | }
79 |
80 | type STATE int
81 |
82 |
83 | //节点之间是否已经p2p直连
84 | const P2P_MESSAGR_TRANS = 1
85 | const P2P_MESSAGT_CONNECT = 2
86 |
87 | var HANDSHACK_MESSAGE string = "Handshake"
88 |
89 |
90 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # **udpnat 用UDP实现NAT穿透,p2p聊天系统**
4 |
5 | 用UDP实现的NAT穿透,一个简单的p2p聊天系统, 理论上来说, 只要不是Port Restricted Cone NAT与Symmetric NAT , Symmetric NAT与Symmetric NAT. 这两种类型组合之间打洞,都可以使用.这两种类型参考其他方案.
6 | (默认只有一个网卡,一个ip的情况)
7 |
8 | ## 原理
9 | ### NAT分类
10 | #### **Full Cone NAT**:
11 | 内网主机建立一个UDP socket(LocalIP:LocalPort) 第一次使用这个socket给外部主机发送数据时NAT会给其分配一个公网(PublicIP,PublicPort),以后用这个socket向外面**任何主机**发送数据都将使用这对(PublicIP,PublicPort)。此外**任何外部主机**只要知道这个(PublicIP,PublicPort)就可以发送数据给(PublicIP,PublicPort),内网的主机就能收到这个数据包
12 |
13 | #### **Restricted Cone NAT**:
14 | 内网主机建立一个UDP socket(LocalIP,LocalPort) 第一次使用这个socket给外部主机发送数据时NAT会给其分配一个公网(PublicIP,PublicPort),以后用这个socket向外面**任何主机**发送数据都将使用这对(PublicIP,PublicPort)。此外,如果任何外部主机想要发送数据给这个内网主机,只要知道这个(PublicIP,PublicPort)并且内网主机之前用这个**socket曾向这个外部主机IP发送过数据**。只要满足这两个条件,这个外部主机就可以用自己的(**IP,任何端口**)发送数据给(PublicIP,PublicPort),内网的主机就能收到这个数据包
15 |
16 | #### **Port Restricted Cone NAT**:
17 |
18 | 内网主机建立一个UDP socket(LocalIP,LocalPort) 第一次使用这个socket给外部主机发送数据时NAT会给其分配一个公网(PublicIP,PublicPort),以后用这个socket向外面**任何主机**发送数据都将使用这对(PublicIP,PublicPort)。此外,如果任何外部主机想要发送数据给这个内网主机,只要知道这个(PublicIP,PublicPort)并且内网主机之前用这个**socket曾向这个外部主机(IP,Port)发送过数据**。只要满足这两个条件,这个外部主机就可以用自己的(**IP,Port**)发送数据给(PublicIP,PublicPort),内网的主机就能收到这个数据包
19 |
20 | #### **Symmetric NAT**:
21 | 内网主机建立一个UDP socket(LocalIP,LocalPort),当用这个socket第一次发数据给外部主机1时,NAT为其映射一个(PublicIP-1,Port-1),以后内网主机发送给外部主机1的所有数据都是用这个(PublicIP-1,Port-1),如果内网主机同时用这个socket给外部主机2发送数据,第一次发送时,NAT会为其分配一个(PublicIP-2,Port-2), 以后内网主机发送给外部主机2的所有数据都是用这个(PublicIP-2,Port-2).如果NAT有多于一个公网IP,则PublicIP-1和PublicIP-2可能不同,如果NAT只有一个公网IP,则Port-1和Port-2肯定不同,也就是说一定不能是PublicIP-1等于 PublicIP-2且Port-1等于Port-2。此外,如果任何外部主机想要发送数据给这个内网主机,那么它首先应该收到内网主机发给他的数据,然后才能往回发送,否则即使他知道内网主机的一个(PublicIP,Port)也不能发送数据给内网主机,这种NAT实现UDP-P2P通信比较困难,可以通过先给对端发送消息,再给发服务器发送消息,猜测端口。
22 |
23 |
24 | ### 思路
25 |
26 | 既然已经知道了各种NAT类型的特点了, 也就可以知道,那三个cone类型的NAT, 同一个socket向外部的任何主机通信, NAT都会为它映射同一个端口,在外部主机看来, 就好像有固定的IP和端口一样.
27 |
28 | 即然对外面所有主机来说, 它的IP和端口一样, 那么我们做NAT穿透是不是就差最后一步了, 如何知道对方的IP和端口.
29 |
30 | 答案就是辅助服务器. 搭建一个服务器, 它有固定的外网IP和端口. 可以让所有的客户端都能连接它. 这样, 这个服务器就能知道所有连入它的客户端的外网IP和端口号了.
31 |
32 | 到这里思路就清楚了:
33 |
34 | 所有客户端都去连接辅助服务器,服务器就知道了所有客户端的外网ip和端口, 客户端再向服务器请求要穿透的目标客户端, 服务器就可以返回其目标的外网IP和端口, 同时通知目标客户端要被P2P连接并发送要连接它的另一个客户端的IP和端口. 这时双方都知道对方IP和端口,P2P就能顺利进行了.
35 |
36 |
37 | , 需要唯一标识符表明需要哪个客户端的IP和端口. 这里就可以有很多设计方案了, 比如事先两个客户端之间就协定好了这个标识符, 比如一个友好的唯一用户名.
38 |
39 |
40 | ### 代码实现流程
41 |
42 | 
43 |
44 | 1. 加入p2p系统的节点向服务器发送注册消息,服务器记录加入节点的用户名与经过NAT设备后的ip与port.
45 | 2. Client A向用户名B发送消息,则先找到用户名B的经过NAT后的ip和port,判断与对端是否已经打洞.
46 | 3. 如果没打洞则发送握手消息给Client B,如果在一个局域网则B收到握手消息后回复ACK;如果不在同一个局域网则等待ACK超时,发送握手消息给服务器,服务器转发消息给Client B.
47 | 4. Client B收到握手消息后回复ACK给Client A,由于CLIent A先给Client B发送握手消息,则Client A的NAT设备记录了Client B的信息,当Client使用相同的端口回复消息时,能透过NAT A到达A.
48 | 5. 由于Client B给Client A发送了消息,则Client B的NAT设备也记录下了Client A的经过NAT A后的ip和port,则Client A也可以给Client B发送消息.
49 |
50 |
51 | ### 聊天系统使用
52 | **服务端**
53 |
54 | ./server --ip serverIp --port serverPort
55 |
56 | //ip后带服务器ip,port后带服务器监听端口,默认ip为0.0.0.0,端口为8848
57 |
58 | **客户端**
59 |
60 | ./client --sip serverIp --sport serverPort --cip clientIp --cport clientPort
61 |
62 | //sip后带服务器ip地址,sport后带服务器监听端口,cip为本客户端ip地址,--cport本客户端监听端口,默认服务器ip:port为(0.0.0.0:8848),默认客户端ip:port(0.0.0.0:8900)
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/server/server.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "MyCode/udpnat/common"
5 | "bufio"
6 | "encoding/json"
7 | "fmt"
8 | "github.com/urfave/cli"
9 | "net"
10 | "os"
11 | "strconv"
12 | )
13 |
14 | var (
15 | app = cli.NewApp()
16 |
17 | ipFlag = cli.StringFlag{
18 | Name: "ip",
19 | Value: common.SERVER_IP,
20 | Usage: "Server Listen ip",
21 | }
22 |
23 | portFlag = cli.IntFlag{
24 | Name: "port",
25 | Value: common.SERVER_PORT,
26 | Usage: "listening port",
27 | }
28 |
29 | )
30 |
31 | //存放登录用户信息
32 | var userList map[common.StUserListNode]struct{}
33 |
34 |
35 | type peer struct {
36 | conn *net.UDPConn
37 | addr *net.UDPAddr
38 | writeAddr *net.UDPAddr
39 | ReadMsgCh chan stReadMsg
40 | WriteMsgCh chan *sendMsg
41 | }
42 |
43 | type sendMsg struct {
44 | contentMsg []byte
45 | reply chan error
46 | }
47 |
48 | type stReadMsg struct {
49 | content []byte
50 | length int
51 | addr string
52 | }
53 |
54 | func init() {
55 | app.Name = "updNatServer"
56 | app.Usage = "upd NAT Server"
57 | app.Flags = []cli.Flag{
58 | portFlag,
59 | ipFlag,
60 | }
61 |
62 | userList = make(map[common.StUserListNode]struct{})
63 |
64 | app.Action = updServer
65 | }
66 |
67 | func main(){
68 | fmt.Println("udp Server begin to Work...")
69 | app.Run(os.Args)
70 | console()
71 | fmt.Println("main end...")
72 | }
73 |
74 | func updServer(ctx *cli.Context) error{
75 | go listenToConnect(ctx)
76 |
77 | return nil
78 | }
79 |
80 | func console() {
81 | input := bufio.NewScanner(os.Stdin)
82 | for input.Scan() {
83 | command := input.Text()
84 | fmt.Println("command:",command)
85 | }
86 | }
87 |
88 | func listenToConnect(ctx *cli.Context) {
89 | address := ctx.GlobalString(ipFlag.Name) + ":" + strconv.Itoa(ctx.GlobalInt(portFlag.Name))
90 | addr, err := net.ResolveUDPAddr("udp",address)
91 | if err != nil {
92 | fmt.Println("listenToConnect "," ResolveUDPAddr fail,err:",err)
93 | return
94 | }
95 |
96 | conn, err := net.ListenUDP("udp",addr)
97 | if err != nil {
98 | fmt.Println("listenToConnect "," ListenUDP fail,err:",err)
99 | return
100 | }
101 | fmt.Println("listen address: ",address)
102 |
103 | go readMsg(conn)
104 | }
105 |
106 |
107 | func newPeer(conn *net.UDPConn)*peer{
108 | return &peer{
109 | conn:conn,
110 | ReadMsgCh:make(chan stReadMsg),
111 | WriteMsgCh:make(chan *sendMsg),
112 | }
113 | }
114 |
115 | func readMsg(conn *net.UDPConn){
116 | defer conn.Close()
117 | for{
118 | buff := make([]byte,common.BUFFSIZE)
119 |
120 | nRead, addr, err := conn.ReadFromUDP(buff)
121 | if err != nil {
122 | fmt.Println("readMsg ","ReadFromUDP fail,err:",err)
123 | return
124 | }
125 |
126 | s := newPeer(conn)
127 |
128 | msg := stReadMsg{
129 | content:make([]byte,0,len(buff)),
130 | length:nRead,
131 | addr:addr.String(),
132 | }
133 | msg.content = buff
134 | s.addr = addr
135 | s.writeAddr = addr
136 |
137 | go func(msg stReadMsg){
138 | s.ReadMsgCh <- msg
139 | }(msg)
140 |
141 | go s.loop()
142 | }
143 | }
144 |
145 | func (s *peer)writeMsg(msg *sendMsg){
146 | msgBuff := msg.contentMsg
147 | mReply := msg.reply
148 |
149 | _, err := s.conn.WriteToUDP(msgBuff,s.writeAddr)
150 | if err != nil {
151 | fmt.Println("write message fail,err:",err)
152 | deleteUserFromAddr(s.writeAddr.String())
153 | return
154 | }
155 |
156 | mReply <- err
157 | }
158 |
159 | func (s *peer)loop(){
160 | for{
161 | select {
162 | case rMsg := <- s.ReadMsgCh:
163 | msg := rMsg.content
164 | addr := rMsg.addr
165 | msgContent := msg[1:rMsg.length]
166 |
167 | switch msg[0] {
168 | case common.LOGIN:
169 | go s.handleLogin(msgContent,addr)
170 | case common.LOGOUT:
171 | go s.handleLogout(msgContent,addr)
172 | case common.P2PTRANS:
173 | go s.handleTrans(msgContent,addr)
174 | case common.GETALLUSER:
175 | fmt.Println("loop ","msgContent:",msgContent)
176 | case common.GETUSER:
177 | go s.handleGetUset(msgContent,addr)
178 | }
179 | case msg := <- s.WriteMsgCh:
180 | go s.writeMsg(msg)
181 |
182 | }
183 | }
184 | }
185 |
186 | func (s *peer)handleLogin(msgContent []byte,addr string){
187 | userNode := getLogUserInfo(msgContent,addr)
188 | if userNode.UserInfo.UserName == "" {
189 | err := "getLogUserInfo fail,please check the input Userinfo"
190 | doErr(err,s.WriteMsgCh)
191 | return
192 | }
193 |
194 | fmt.Println("UserName: ",userNode.UserInfo.UserName,"(",userNode.Ip,":",userNode.Port,")"," LogIn")
195 |
196 | found := false
197 | for node, _ := range userList {
198 | if node.UserInfo == userNode.UserInfo {
199 | found = true
200 | break
201 | }
202 | }
203 |
204 | if !found {
205 | userList[userNode] = struct{}{}
206 | }else{
207 | errMsg := "this userName have used"
208 |
209 | doErr(errMsg,s.WriteMsgCh)
210 | return
211 | }
212 |
213 | s.sendUserInfo()
214 |
215 | return
216 | }
217 |
218 | func (s *peer)handleGetUset(content []byte,addr string){
219 | if !findUserFromAddr(addr) {
220 | err := "This user does not have permissions,please login"
221 |
222 | doErr(err,s.WriteMsgCh)
223 | return
224 | }
225 | s.sendUserList()
226 | }
227 |
228 | func (s *peer)handleLogout(content []byte,addr string){
229 | var contentMsg common.StLogoutMessage
230 | err := json.Unmarshal(content,&contentMsg)
231 | if err != nil {
232 | fmt.Println("Unmarshal fail,err: ",err)
233 | return
234 | }
235 |
236 | fmt.Println("handleLogout ","user ",contentMsg.UserName,"address ",addr,"want to logout p2p network")
237 |
238 | for v, _ := range userList {
239 | if v.UserInfo.UserName == contentMsg.UserName {
240 | delete(userList, v)
241 | break
242 | }
243 | }
244 |
245 | s.sendUserInfo()
246 |
247 | }
248 |
249 | func (s *peer)handleTrans(content []byte,addr string){
250 | var msg common.Handshack
251 | err := json.Unmarshal(content,&msg)
252 | if err != nil {
253 | fmt.Println("handleTrans Unmarshal,err: ",err)
254 | return
255 | }
256 |
257 | s.writeAddr, err = net.ResolveUDPAddr("udp",msg.To)
258 | if err != nil {
259 | fmt.Println("handleTrans ResolveUDPAddr fail,err: ",err)
260 | return
261 | }
262 |
263 | writeMsg := make([]byte,0,common.BUFFSIZE)
264 | writeMsg = append(writeMsg,common.S2PTRANSMSG)
265 |
266 | reply := make(chan error)
267 |
268 | writeMsg = append(writeMsg,content...)
269 |
270 | sMsg := &sendMsg{writeMsg,reply}
271 |
272 | go func(msg *sendMsg){
273 | s.WriteMsgCh <- msg
274 | }(sMsg)
275 |
276 | select {
277 | case err := <- reply:
278 | if err != nil {
279 | fmt.Println("write msg fail,err:",err)
280 | return
281 | }
282 | }
283 | }
284 |
285 | func (s *peer)sendUserList() {
286 | for msg, _ := range userList {
287 | uMsgInfo := make([]byte,0,common.BUFFSIZE)
288 | uMsgInfo = append(uMsgInfo,common.USERINFO)
289 |
290 | userMsgInfo, err := json.Marshal(msg)
291 | if err != nil {
292 | fmt.Println("sendUserList Marshal fail,err:",err)
293 | return
294 | }
295 |
296 | uMsgInfo = append(uMsgInfo,userMsgInfo...)
297 |
298 | uReply := make(chan error)
299 | uMsg := &sendMsg{uMsgInfo,uReply}
300 |
301 | go func(msg *sendMsg){
302 | s.WriteMsgCh <- msg
303 | }(uMsg)
304 |
305 | select {
306 | case err := <- uReply:
307 | if err != nil {
308 | fmt.Println("write msg fail,err:",err)
309 | return
310 | }
311 | }
312 | }
313 | }
314 |
315 | func getLogUserInfo(msg []byte,addr string)common.StUserListNode{
316 | userNode := common.StUserListNode{}
317 | var content common.StLoginMessage
318 | err := json.Unmarshal(msg,&content)
319 | if err != nil {
320 | fmt.Println("getLogUserInfo fail,err: ",err)
321 | return userNode
322 | }
323 |
324 | userNode.UserInfo.UserName = content.UserName
325 |
326 | ip, port, err := net.SplitHostPort(addr)
327 | if err != nil {
328 | fmt.Println("getLogUserInfo "," SplitHostPort fail,err: ",err)
329 | return common.StUserListNode{}
330 | }
331 | userNode.Ip = ip
332 | userNode.Port,_ = strconv.Atoi(port)
333 |
334 | return userNode
335 | }
336 |
337 | func (s *peer)sendUserInfo() {
338 |
339 | userLen := len(userList)
340 |
341 | for k,_ := range userList {
342 | replyCh := make(chan error)
343 |
344 | sizeMsg := make([]byte,0,common.BUFFSIZE)
345 | sizeMsg = append(sizeMsg,common.USERCOUNT)
346 |
347 | userMsg, err := json.Marshal(userLen)
348 | if err != nil {
349 | fmt.Println("handleLogin Marshal fail,err:",err)
350 | doErr(err.Error(),s.WriteMsgCh)
351 | return
352 | }
353 |
354 | s.writeAddr,err = net.ResolveUDPAddr("udp",k.Ip + ":" + strconv.Itoa(k.Port))
355 | if err != nil {
356 | fmt.Println("handleLogin ResolveUDPAddr fail,err: ",err)
357 | doErr(err.Error(),s.WriteMsgCh)
358 | return
359 | }
360 | sizeMsg = append(sizeMsg,userMsg...)
361 | sMsg := &sendMsg{sizeMsg,replyCh}
362 |
363 | go func(sMsg *sendMsg){
364 | s.WriteMsgCh <- sMsg
365 | }(sMsg)
366 |
367 | select {
368 | case err := <- replyCh:
369 | if err != nil {
370 | fmt.Println("write msg fail,err:",err)
371 | doErr(err.Error(),s.WriteMsgCh)
372 | }
373 | }
374 | }
375 |
376 | for k,_ := range userList {
377 | s.writeAddr,_ = net.ResolveUDPAddr("udp",k.Ip + ":" + strconv.Itoa(k.Port))
378 |
379 | s.sendUserList()
380 | }
381 |
382 |
383 | }
384 |
385 | func doErr(errMsg string,WriteMsgCh chan *sendMsg) {
386 | fmt.Println(errMsg)
387 |
388 | eMsg := make([]byte,0,common.BUFFSIZE)
389 |
390 | eMsg = append(eMsg,common.ERRORMSG)
391 |
392 | tMsg, err := json.Marshal(errMsg)
393 | if err != nil {
394 | fmt.Println("doErr Marshal fail,err: ",err)
395 | return
396 | }
397 | eMsg = append(eMsg,tMsg...)
398 |
399 | reply := make(chan error)
400 | sMsg := &sendMsg{eMsg,reply}
401 |
402 | go func(){
403 | WriteMsgCh <- sMsg
404 | }()
405 |
406 | select {
407 | case err := <- reply:
408 | if err != nil {
409 | fmt.Println("write msg fail,err:",err)
410 | return
411 | }
412 | }
413 | }
414 |
415 | func deleteUserFromAddr(addr string) {
416 | for k,_ := range userList {
417 | userAddr := k.Ip + ":" + strconv.Itoa(k.Port)
418 | if userAddr == addr {
419 | delete(userList,k)
420 | return
421 | }
422 | }
423 |
424 | return
425 | }
426 |
427 | func findUserFromAddr(addr string)bool {
428 | for k,_ := range userList {
429 | userAddr := k.Ip + ":" + strconv.Itoa(k.Port)
430 | if userAddr == addr {
431 | return true
432 | }
433 | }
434 |
435 | return false
436 | }
437 |
--------------------------------------------------------------------------------
/.idea/workspace.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
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 | ResolveUDPAddr
71 | stUserInfo
72 | USERINFO
73 | stP2pTransMsg
74 | from the ACK,can't find the sned SYN messag
75 | hash
76 | Split
77 | "||"
78 | handleTrans
79 | P2PTRANSMSG
80 | ||
81 | "||
82 | P2PTRANS
83 | stP2pMsgACK
84 | P2PMESSAGEACK
85 | P2PMESSAGE
86 | serverAddress
87 | getUser
88 | read
89 | Server
90 | delete
91 | listen A
92 | p2p M
93 | sendMs
94 | from
95 | do
96 | This user does not have permissions,please login
97 | os.Ex
98 | send
99 | nWite
100 |
101 |
102 |
103 |
104 |
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 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
--------------------------------------------------------------------------------
/client/client.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "MyCode/udpnat/common"
5 | "bufio"
6 | "encoding/json"
7 | "errors"
8 | "fmt"
9 | "github.com/urfave/cli"
10 | "net"
11 | "os"
12 | "os/signal"
13 | "strconv"
14 | "strings"
15 | "syscall"
16 | "time"
17 | )
18 |
19 | var (
20 | app = cli.NewApp()
21 |
22 | clientPortFlag = cli.IntFlag{
23 | Name:"cport",
24 | Value:common.CLIENT_PORT,
25 | Usage:"Client port",
26 | }
27 |
28 | clientIpFlag = cli.StringFlag{
29 | Name: "cip",
30 | Value: common.CLIENT_IP,
31 | Usage: "Client Listen ip",
32 | }
33 |
34 | serverPortFlag = cli.IntFlag{
35 | Name:"sport",
36 | Value:common.SERVER_PORT,
37 | Usage:"Server Port",
38 | }
39 |
40 | serverIpFlag = cli.StringFlag{
41 | Name: "sip",
42 | Value: common.SERVER_IP,
43 | Usage: "Server Listen ip",
44 | }
45 | )
46 |
47 | var userList map[common.StUserListNode]struct{}
48 |
49 | var self common.StUserListNode
50 |
51 | type peer struct{
52 | conn *net.UDPConn
53 | serverAddr string
54 |
55 | writeMsg chan stWriteMsg
56 |
57 | gotreply map[common.Handshack]reply
58 |
59 | state map[connectPeer]common.STATE
60 |
61 | }
62 |
63 | type connectPeer struct {
64 | serverAddr string
65 | localAddr string
66 | peerAddr string
67 | }
68 |
69 | type stWriteMsg struct {
70 | msgContent []byte
71 | remoteAddr string
72 | }
73 |
74 | type reply struct{
75 | matched chan <- bool
76 | }
77 |
78 | type (
79 | stP2pMsg struct {
80 | }
81 |
82 | stUserCount struct {
83 | }
84 |
85 | stUserInfo struct {
86 | }
87 |
88 | stS2pTransMsg struct {
89 | }
90 |
91 | stP2PTransAck struct {
92 | }
93 |
94 | stErrorMsg struct {
95 |
96 | }
97 | )
98 |
99 | type stPackage interface {
100 | handle(p *peer,msg []byte,from *net.UDPAddr) error
101 | }
102 |
103 |
104 | func init(){
105 | app.Name = "udpNatClient"
106 | app.Flags = []cli.Flag{
107 | clientPortFlag,
108 | clientIpFlag,
109 | serverPortFlag,
110 | serverIpFlag,
111 | }
112 |
113 | app.Action = udpClient
114 | }
115 |
116 |
117 | func main(){
118 | app.Run(os.Args)
119 |
120 | fmt.Println("main end...")
121 | }
122 |
123 | func udpClient(ctx *cli.Context) error{
124 | var name string
125 |
126 | fmt.Printf("Please input your user name:>")
127 | scanner := bufio.NewScanner(os.Stdin)
128 |
129 | if scanner.Scan(){
130 | name = scanner.Text()
131 | }
132 |
133 | var logMsg common.StLoginMessage
134 | logMsg.UserName = name
135 |
136 | self.UserInfo.UserName = name
137 |
138 | serverAddress := ctx.GlobalString(serverIpFlag.Name) + ":" + strconv.Itoa(ctx.GlobalInt(serverPortFlag.Name))
139 | listenAddress := ctx.GlobalString(clientIpFlag.Name) + ":" + strconv.Itoa(ctx.GlobalInt(clientPortFlag.Name))
140 |
141 | listenAddr, err := net.ResolveUDPAddr("udp",listenAddress)
142 | if err != nil {
143 | fmt.Println("ResolveUDPAddr fail,err:",err)
144 | return err
145 | }
146 |
147 | serverAddr, err := net.ResolveUDPAddr("udp",serverAddress)
148 | if err != nil {
149 | fmt.Println("ResolveUDPAddr fail,err:",err)
150 | return err
151 | }
152 |
153 | conn, err := net.ListenUDP("udp",listenAddr)
154 | if err != nil {
155 | fmt.Println("ListenUDP fail,err: ",err)
156 | return err
157 | }
158 |
159 | doWriteLoginMsg(logMsg,conn,serverAddr)
160 |
161 | if err := doListenUDP(conn,serverAddress); err != nil {
162 | fmt.Println("doListenUDP fail, err: ",err)
163 | return err
164 | }
165 |
166 | return nil
167 | }
168 |
169 | func doWriteLoginMsg(msg common.StLoginMessage,conn *net.UDPConn,serverAddr *net.UDPAddr) {
170 | msgBuff := make([]byte,0,common.BUFFSIZE)
171 | msgBuff = append(msgBuff,common.LOGIN)
172 | buff, err := json.Marshal(msg)
173 | if err != nil {
174 | fmt.Println("doWriteLoginMsg "," Marshal fail,err:",err)
175 | return
176 | }
177 | msgBuff = append(msgBuff,buff...)
178 |
179 | _ , err = conn.WriteToUDP(msgBuff,serverAddr)
180 | if err != nil {
181 | fmt.Println("doWriteLoginMsg "," write message fail,err:",err)
182 | return
183 | }
184 | }
185 |
186 | func doListenUDP(conn *net.UDPConn,serverAddr string) error{
187 | p := newPeer(conn,serverAddr)
188 |
189 | go p.readLoop()
190 |
191 | go p.writeLoop()
192 |
193 | go p.stop()
194 |
195 |
196 | menu()
197 |
198 | p.console()
199 |
200 |
201 | return nil
202 | }
203 |
204 | func newPeer(conn *net.UDPConn,serverAddr string) *peer{
205 | peer := &peer{
206 | conn: conn,
207 | writeMsg:make(chan stWriteMsg),
208 | gotreply:make(map[common.Handshack]reply),
209 | serverAddr:serverAddr,
210 | state:make(map[connectPeer]common.STATE),
211 | }
212 |
213 |
214 | return peer
215 | }
216 |
217 | func (p *peer) readLoop() {
218 | defer p.conn.Close()
219 |
220 | for{
221 | buf := make([]byte,common.BUFFSIZE)
222 | nRead, from, err := p.conn.ReadFromUDP(buf)
223 | if err != nil {
224 | fmt.Println("ReadFromUDP fail,err: ",err)
225 | return
226 | }
227 | p.handlePacket(from,buf[:nRead])
228 | }
229 | }
230 |
231 | func (p *peer) writeLoop() {
232 | for {
233 | select {
234 | case msg := <- p.writeMsg:
235 | err := sendMsg(p.conn,msg.msgContent,msg.remoteAddr)
236 | if err != nil {
237 | fmt.Println("sendMsg fail,err: ",err)
238 | return
239 | }
240 | }
241 | }
242 | }
243 |
244 | func (p *peer) handlePacket(from *net.UDPAddr,bufs []byte) error {
245 | mPackage,msg := decodePacket(bufs)
246 | if mPackage == nil {
247 | fmt.Println("unknow package type")
248 | return errors.New("unknow package type")
249 | }
250 |
251 | err := mPackage.handle(p,msg,from)
252 | if err != nil {
253 |
254 | }
255 |
256 |
257 | return nil
258 | }
259 |
260 | func decodePacket(bufs []byte)(stPackage,[]byte) {
261 | var req stPackage
262 | switch pType := bufs[0];pType {
263 | case common.P2PMESSAGE:
264 | req = new(stP2pMsg)
265 | case common.P2PTRANSACK:
266 | req = new(stP2PTransAck)
267 | case common.USERCOUNT:
268 | req = new(stUserCount)
269 | case common.USERINFO:
270 | req = new(stUserInfo)
271 | case common.P2PTRANSMSG: //接收到客户端的握手消息的处理机制与接收到服务器转发的握手消息机制相同
272 | req = new(stS2pTransMsg)
273 | case common.S2PTRANSMSG:
274 | req = new(stS2pTransMsg)
275 | case common.ERRORMSG:
276 | req = new(stErrorMsg)
277 | default:
278 | return nil,bufs[1:]
279 | }
280 |
281 | return req,bufs[1:]
282 | }
283 |
284 | func (p *peer)console() {
285 | input := bufio.NewScanner(os.Stdin)
286 | fmt.Printf(">")
287 | for input.Scan() {
288 | command := input.Text()
289 | p.parseCommand(command)
290 | fmt.Printf(">")
291 | }
292 | }
293 |
294 | func (p *peer)stop(){
295 | sigc := make(chan os.Signal,1)
296 | signal.Notify(sigc,syscall.SIGINT,syscall.SIGTERM)
297 | defer signal.Stop(sigc)
298 |
299 | <- sigc
300 |
301 | fmt.Println("Got interrupt, shutting down...")
302 |
303 | p.exit()
304 | }
305 |
306 | func menu(){
307 | fmt.Println("====================================================================")
308 | fmt.Println("* You can input you command: *")
309 | fmt.Println("* Command Type:\"login\",\"send\",\"logout\",\"getuser\",\"exit\" *")
310 | fmt.Println("* logout UserName(your address should right) *")
311 | fmt.Println("* login UserName(your User name not used) *")
312 | fmt.Println("* Example : send Username Message *")
313 | fmt.Println("* logout UserName(your address should right) *")
314 | fmt.Println("* getuser *")
315 | fmt.Println("====================================================================")
316 | }
317 |
318 | func (p *peer)parseCommand(cmd string){
319 | cmds := strings.Split(cmd," ")
320 |
321 | switch cmds[0] {
322 | case "send":
323 | if len(cmds) != 3 {
324 | fmt.Println("argv err")
325 | fmt.Println("send UserName Message")
326 | return
327 | }
328 | go p.sendHandle(cmds[1],cmds[2])
329 | case "logout":
330 | if len(cmds) != 2{
331 | fmt.Println("argv err")
332 | fmt.Println("logout userName")
333 | return
334 | }
335 |
336 | go p.logout(cmds[1])
337 | case "login":
338 | if len(cmds) != 2{
339 | fmt.Println("argv err")
340 | fmt.Println("logout userName")
341 | return
342 | }
343 | go p.login(cmds[1])
344 | case "getuser":
345 | go p.getUser()
346 | case "exit":
347 | go p.exit()
348 | default:
349 | fmt.Println("You entered the wrong command,Please check!!!")
350 | }
351 | }
352 |
353 | func (p *peer)sendHandle(userName string,message string){
354 | //通过用户名取出ip,端口
355 | isFind, userIp, userPort := findUser(userName)
356 |
357 | if !isFind {
358 | fmt.Println("do not find this user,username:",userName)
359 | return
360 | }
361 |
362 | isSelf, selfIp, selfPort := findSelf(self.UserInfo.UserName)
363 |
364 | if !isSelf {
365 | fmt.Println("Server have error message")
366 | return
367 | }
368 |
369 | localAddr := selfIp + ":" + strconv.Itoa(selfPort)
370 |
371 | msg := make([]byte,0,common.BUFFSIZE)
372 |
373 | msgContent, err := json.Marshal(message)
374 | if err != nil {
375 | fmt.Println("sendHandle ","Marshal fail,err: ",err)
376 | return
377 | }
378 |
379 | peerAddress := userIp + ":" + strconv.Itoa(userPort)
380 |
381 | connPeer := connectPeer{
382 | serverAddr:p.serverAddr,
383 | localAddr:localAddr,
384 | peerAddr:peerAddress,
385 | }
386 |
387 | var typeFlag bool
388 |
389 | //判断此链接是否打洞过
390 | if v,ok := p.state[connPeer];!ok {
391 | p.state[connPeer] = common.P2P_MESSAGR_TRANS
392 |
393 | typeFlag = true
394 |
395 | }else {
396 | if v == common.P2P_MESSAGR_TRANS {
397 | typeFlag = true
398 | }else{
399 | typeFlag = false
400 | }
401 | }
402 |
403 | //此链接没有打洞过则发消息对对端,NAT设备会记录发出的链接请求,同时发送消息给服务器,服务器转发消息给对端,对端回应ACK消息给本机,
404 | //由于NAT设备记录了本机给对端发送的链接请求,则对端回应过来的消息NAT设备会当成发出消息的回应,会到达本机,而本机发送给对端的消息会被NAT设备丢弃.
405 |
406 | if typeFlag {
407 | flag := p.handshake(connPeer.localAddr,connPeer.peerAddr,connPeer.serverAddr)
408 | if flag == false {
409 | fmt.Println("handshake fail,Network Address Translation fail.")
410 | return
411 | }
412 | p.state[connPeer] = common.P2P_MESSAGT_CONNECT
413 | }
414 |
415 | fmt.Println("p2p Module")
416 |
417 | msg = append(msg,common.P2PMESSAGE)
418 | msg = append(msg,msgContent...)
419 |
420 | writeMsg := stWriteMsg{msg,peerAddress}
421 | p.writeMsg <- writeMsg
422 | }
423 |
424 | func (p *peer)logout(name string) {
425 | if name != self.UserInfo.UserName {
426 | err := "Please enter your own username"
427 | fmt.Println(err)
428 | return
429 | }
430 |
431 | user := common.StLogoutMessage{name}
432 |
433 | msg := make([]byte,0,common.BUFFSIZE)
434 | msg = append(msg,common.LOGOUT)
435 | msgContent, err := json.Marshal(user)
436 | if err != nil {
437 | fmt.Println("logout Marshal fail,err: ",err)
438 | return
439 | }
440 | msg = append(msg,msgContent...)
441 |
442 | writeMsg := stWriteMsg{msg,p.serverAddr}
443 | p.writeMsg <- writeMsg
444 |
445 |
446 | return
447 | }
448 |
449 | func (p *peer)login(name string){
450 | userName := common.StLoginMessage{name}
451 | remoteAddr,err := net.ResolveUDPAddr("udp",p.serverAddr)
452 | if err != nil {
453 | fmt.Println("login fail,err: ",err)
454 | return
455 | }
456 | doWriteLoginMsg(userName,p.conn,remoteAddr)
457 | }
458 |
459 | func (p *peer)getUser() {
460 | msg := make([]byte,0,common.BUFFSIZE)
461 | msg = append(msg,common.GETUSER)
462 |
463 | writeMsg := stWriteMsg{msg,p.serverAddr}
464 | p.writeMsg <- writeMsg
465 | }
466 |
467 | func (p *peer)exit() {
468 | name := self.UserInfo.UserName
469 |
470 | p.logout(name)
471 | //等待发送出去logout消息
472 | time.Sleep(time.Millisecond)
473 | os.Exit(0)
474 | }
475 |
476 | func (req *stP2pMsg)handle(p *peer,msg []byte,from *net.UDPAddr) error{
477 | ip := from.IP.String()
478 | port := from.Port
479 | var name string
480 | var flag bool
481 |
482 | for k,_ := range userList {
483 | if k.Port == port && k.Ip == ip {
484 | name = k.UserInfo.UserName
485 | flag = true
486 | break
487 | }
488 | }
489 |
490 | var msgBuff string
491 |
492 | err := json.Unmarshal(msg,&msgBuff)
493 | if err != nil {
494 | fmt.Println("Unmarshal fail,err: ",err)
495 | return err
496 | }
497 |
498 | if flag {
499 | fmt.Println("Recv message: ","from ","[",name,"(",ip,":",port,")]"," msg :",msgBuff)
500 | }else{
501 | fmt.Println("Recv message from unknow user"," [(",ip,":",port,")]"," msg :",msgBuff)
502 | fmt.Println("Please send command to Server ")
503 | }
504 |
505 |
506 | return nil
507 | }
508 |
509 | func (req *stUserCount)handle(p *peer,msg []byte,from *net.UDPAddr) error{
510 | var nCount int
511 | err := json.Unmarshal(msg,&nCount)
512 | if err != nil {
513 | fmt.Println("Unmarshal fail,err: ",err)
514 | return err
515 | }
516 |
517 | userList = make(map[common.StUserListNode]struct{})
518 |
519 | fmt.Println("*********login user info*********")
520 |
521 |
522 | return nil
523 | }
524 |
525 | func (req *stUserInfo)handle(p *peer,msg []byte,from *net.UDPAddr) error{
526 | var msgUserInfo common.StUserListNode
527 | err := json.Unmarshal(msg,&msgUserInfo)
528 | if err != nil {
529 | fmt.Println("Unmarshal fail,err: ",err)
530 | return err
531 | }
532 |
533 | userList[msgUserInfo] = struct{}{}
534 |
535 | fmt.Println("userName: ",msgUserInfo.UserInfo.UserName,"(",msgUserInfo.Ip,":",msgUserInfo.Port,")")
536 |
537 |
538 | return nil
539 | }
540 |
541 | func (req *stS2pTransMsg)handle(p *peer,msg []byte,from *net.UDPAddr) error {
542 | var contentMsg common.Handshack
543 | err := json.Unmarshal(msg,&contentMsg)
544 | if err != nil {
545 | fmt.Println("Unmarshal fail,err: ",err)
546 | return err
547 | }
548 |
549 | sMsg := make([]byte,0,common.BUFFSIZE)
550 | sMsg = append(sMsg,common.P2PTRANSACK)
551 |
552 |
553 | sMsg = append(sMsg,msg...)
554 |
555 | writeMsg := stWriteMsg{sMsg,contentMsg.From}
556 | p.writeMsg <- writeMsg
557 |
558 |
559 | return nil
560 | }
561 |
562 | func (req *stP2PTransAck)handle(p *peer,msg []byte,from *net.UDPAddr) error{
563 |
564 | var contentMsg common.Handshack
565 |
566 | err := json.Unmarshal(msg,&contentMsg)
567 | if err != nil {
568 | fmt.Println("Unmarshal fail,err: ",err)
569 | return err
570 | }
571 |
572 | if v,ok := p.gotreply[contentMsg];ok {
573 | delete(p.gotreply,contentMsg)
574 | v.matched <- true
575 | }else{
576 | fmt.Println("from the ACK,can't find the send SYN message")
577 | }
578 |
579 |
580 | return nil
581 | }
582 |
583 | func (req *stErrorMsg)handle(p *peer,msg []byte,from *net.UDPAddr) error {
584 | var errMsg string
585 | err := json.Unmarshal(msg,&errMsg)
586 | if err != nil {
587 | fmt.Println("stErrorMsg handle fail,err: ",err)
588 | return err
589 | }
590 |
591 | fmt.Println("Receive a server Error response"," error message: ",errMsg)
592 |
593 | return nil
594 | }
595 |
596 | func sendMsg(conn *net.UDPConn,msg []byte,reAddress string)error{
597 | remoteAddr, err := net.ResolveUDPAddr("udp",reAddress)
598 | if err != nil {
599 | fmt.Println("sendMsg ","ResolveUDPAddr fail,err:",err)
600 | return err
601 | }
602 |
603 | _, err = conn.WriteToUDP(msg,remoteAddr)
604 | if err != nil {
605 | fmt.Println("ResolveUDPAddr fail,err:",err)
606 | return err
607 | }
608 |
609 | return nil
610 | }
611 |
612 | func findUser(userName string) (bool,string,int) {
613 | for user, _ := range userList {
614 | if user.UserInfo.UserName == userName {
615 |
616 | return true,user.Ip,user.Port
617 | }
618 | }
619 |
620 | return false,"",0
621 | }
622 |
623 | func findSelf(self string) (bool,string,int){
624 | for user, _ := range userList {
625 | if user.UserInfo.UserName == self {
626 |
627 | return true,user.Ip,user.Port
628 | }
629 | }
630 |
631 | return false,"",0
632 | }
633 |
634 | //通过握手来进行打洞
635 | func (p *peer)handshake(localAddr,peerAddr,serverAddr string) bool{
636 | //先给对端发送握手消息
637 | msg := make([]byte,0,common.BUFFSIZE)
638 | msg = append(msg,common.P2PTRANSMSG)
639 |
640 | shackMsg := common.Handshack{localAddr,peerAddr,common.HANDSHACK_MESSAGE}
641 | sMsg,err := json.Marshal(shackMsg)
642 | if err != nil {
643 | fmt.Println("sendHandle: ","Marshal fail,err: ",err)
644 | return false
645 | }
646 |
647 | msg = append(msg,sMsg...)
648 |
649 | //先给对端发送握手消息
650 | writeMsg := stWriteMsg{msg,peerAddr}
651 | p.writeMsg <- writeMsg
652 |
653 | //等待对端回复握手消息的ACK
654 | resultCh := make(chan bool)
655 | p.gotreply[shackMsg] = reply{resultCh}
656 |
657 | var flag bool
658 |
659 | select {
660 | case <- resultCh:
661 | flag = true
662 | case <- time.After(common.P2PSHAKETIMEOUT):
663 | fmt.Println("handshake ","time out","send message to Server for trans")
664 | flag = false
665 | }
666 |
667 | if flag {
668 | return true
669 | }else{
670 | //请求服务器转发握手消息
671 | cMsg := make([]byte,0,common.BUFFSIZE)
672 | cMsg = append(cMsg,common.P2PTRANS)
673 | cMsg = append(cMsg,sMsg...)
674 |
675 | writeMsg = stWriteMsg{cMsg,serverAddr}
676 | p.writeMsg <- writeMsg
677 | }
678 |
679 | select {
680 | case <- resultCh:
681 | return true
682 | case <- time.After(common.P2PSHAKETIMEOUT):
683 | fmt.Println("handshake ","time out")
684 | return false
685 | }
686 |
687 | }
--------------------------------------------------------------------------------