├── 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 | 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 | ![image](https://github.com/coderInk/udpnat/blob/master/udpHole.png) 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 | 11 | 12 | 22 | 23 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 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 | 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 | } --------------------------------------------------------------------------------