您好,登錄后才能下訂單哦!
隨著區(qū)塊鏈的越來越火,去中心化的網(wǎng)絡設計再次被拿到技術人員面前。在這里我使用非常通俗的語言,幫大家來理解去中心化的網(wǎng)絡設計的基礎—網(wǎng)絡穿透。再使用代碼來實現(xiàn)穿透。如果闡述不到位的地方,歡迎大家拋磚。代碼在此: https://github.com/wangbojing/P2PServer?
??? 在有中心化服務器的網(wǎng)絡中,客戶端,服務器,網(wǎng)關構成網(wǎng)絡拓撲圖。如下圖1所示:由于后續(xù)出現(xiàn)的名詞概念很多,先約法三章,在這里統(tǒng)一一下稱呼:所有的終端機器成為客戶端,不同客戶端使用大寫字母區(qū)分(A,B,C,…);客戶端上面運行的應用程序統(tǒng)一稱為客戶程序,不同的應用程序使用不數(shù)字區(qū)分(1,2,3,…)。作為服務器的物理機稱為服務器,而服務器上運行的程序稱為服務程序,后文中每一個拓撲組件都只有一個IP地址。為客戶端提供公網(wǎng)IP服務的組件稱為網(wǎng)關。
圖1 中心化服務器的網(wǎng)絡拓撲圖
從網(wǎng)關映射到客戶端中的網(wǎng)絡結構,這里需要引入一個NAT的概念。什么NAT呢?中文名叫網(wǎng)絡地址轉換,習慣稱為網(wǎng)絡地址映射。為什么需要網(wǎng)絡地址映射呢?:需要說到IPV4網(wǎng)絡地址已經(jīng)用完,全部使用IPV6又會造成很多只支持IPV4的終端設備無法正常使用,所以網(wǎng)絡地址映射應運而生,忍辱負重。才會有我們現(xiàn)在所謂的網(wǎng)絡穿透的出現(xiàn)。到底怎么映射的?如圖2網(wǎng)絡地址映射所示??蛻舫绦蚴褂?92.168.0.234:7890發(fā)送數(shù)據(jù),通過網(wǎng)關的網(wǎng)絡地址映射在公網(wǎng)被轉換為112.93.116.102:6834,被互聯(lián)網(wǎng)上的大家所認知。此時在公網(wǎng)上使用客戶程序的ip與端口被112.93.116.102:6834代替。在這里大家應該明白了NAT是何許物種了。
圖2 網(wǎng)絡地址映射
為了保持新手福音,業(yè)界良心的態(tài)度。什么是穿透?因為NAT是客戶程序發(fā)起的,網(wǎng)絡為了保持通訊新建的一個臨時牌照,隨時可能被收回,而且重新發(fā)起后的牌照不一樣。從而外界及時知道了這個臨時牌照也沒有用。所以需要通過穿透在網(wǎng)關上面打個洞,來為外界進行服務。那NAT與穿透有什么關系呢?正因為有了NAT才需要穿透,如果是IPV6每個客戶端一個IP地址,那就不需要直接可以找到客戶端了。
???
網(wǎng)絡地址映射
?
??? 由于網(wǎng)關的安全性要求不一致,就出現(xiàn)四種不同的NAT方式。分別進行闡述:
第一種完全錐形NAT,英文名叫Full Cone NAT。如圖3完全錐形NAT所示,客戶程序(192.168.0.234:7890)與服務器A(13.44.178.98:9800)通信,通過網(wǎng)關的地址轉換產(chǎn)生的臨時牌照的公網(wǎng)地址(112.93.116.102:6834),服務器B(157.78.13.156:23456)發(fā)送數(shù)據(jù)到公網(wǎng)地址(112.93.116.102:6834),如果客戶程序(192.168.0.234:7890)能夠收到服務器B(157.78.13.156:23456)發(fā)送的數(shù)據(jù),這種NAT映射關系為完全錐形NAT;
圖3 完全錐形NAT
第二種限制錐形NAT,英文名叫RestrictedCone NAT。在圖3 完全錐形NAT中,如果客戶程序(192.168.0.234:7890)不能收到服務器B(157.78.13.156:23456)發(fā)送的數(shù)據(jù),這種NAT映射關系為限制型錐形NAT。
?
第三種端口限制錐形NAT,英文名叫Port RestrictedCone NAT??蛻舫绦?192.168.0.234:7890)發(fā)送數(shù)據(jù)給服務程序(13.44.178.98:9800),網(wǎng)關通過網(wǎng)絡地址轉換產(chǎn)生的地址(112.93.116.102:6834),同樣的服務器內的另一個服務程序(13.44.178.178:9801)發(fā)送數(shù)據(jù)給網(wǎng)關(112.93.116.102:6834)地址,如果客戶程序(192.168.0.234:7890)能夠收到,則為限制錐形NAT,如果客戶程序(192.168.0.234:7890)不能收到,則為端口限制錐形NAT。
?
??? 對于所有的錐型NAT,客戶程序(192.168.0.234:7890)對外發(fā)送的數(shù)據(jù)時,網(wǎng)關地址轉換的地址都是一樣的為(112.93.116.102:6834),那為什么在圖4 限制型錐形NAT中,客戶程序不能收到服務程序B(13.44.178.98:9801)的數(shù)據(jù)呢?因為在網(wǎng)關中沒有發(fā)生過客戶程序(192.168.0.234:7890)給服務程序B(13.44.178.98:9801),故服務程序(13.44.178.98:9801)直接發(fā)送給網(wǎng)關(112.93.116.102:6834),則被網(wǎng)關所丟棄。
圖4 限制型錐形NAT
第四種對稱NAT,英文,名叫Symmetric NAT。如圖5對稱NAT所示,客戶程序(192.168.0.234:7890)發(fā)送數(shù)據(jù)給兩個不同服務器(13.44.178.98:9800)和(157.78.13.156:23456)時,網(wǎng)關會進行不同的網(wǎng)絡地址映射產(chǎn)生(112.93.116.102:6834)和(112.93.116.102:6835)。這是對于整個NAT網(wǎng)絡發(fā)送數(shù)據(jù)出去的過程,而接收數(shù)據(jù)與端口限制錐形NAT一致。
圖5 對稱NAT
本節(jié)介紹三種錐形NAT和對稱NAT的概念,相信到此你還是不知道NAT類型與怎么穿透網(wǎng)關友什么關系。
?
穿透剖析
?
??? 怎么穿透網(wǎng)關來實現(xiàn)去中心化,如圖6穿透網(wǎng)絡NAT拓撲圖所示
在理想的情況下,在NAT 1中客戶程序(192.168.0.234:7890)知道NAT 2中客戶程序(192.168.2.168:2786)的網(wǎng)絡映射地址(157.123.80.165:6954),并給網(wǎng)絡映射地址(157.123.80.165:6954)發(fā)送數(shù)據(jù),并且客戶程序(192.168.2.168:2786)能夠收到數(shù)據(jù);而NAT 2中客戶程序(192.168.2.168:2786)也知道NAT 1中客戶程序的網(wǎng)絡映射地址,并給其網(wǎng)絡映射地址(112.93.116.102:6834)發(fā)送數(shù)據(jù),并且也能收到數(shù)據(jù)。此時對于服務器而言,就已經(jīng)沒有起到數(shù)據(jù)中轉的作用,此時客戶程序(192.168.0.234:7890)與客戶程序(192.168.2.168:2786)能夠互相收發(fā)數(shù)據(jù),服務程序(13.44.178.98:9800)已經(jīng)沒有作用,對于客戶端程序來說,已經(jīng)實現(xiàn)了去中心化。
?
??? 這只是在理論情況,現(xiàn)在具體實現(xiàn)步驟以及結合四種NAT類型來分析一下。
第一種:NAT 1為完全錐形NAT,NAT 2為任何一種NAT模式,如圖7 完全錐形NAT的穿透,綠色字體的順序。
客戶程序(192.168.0.234:7890)先發(fā)送一個連接請求給服務程序,通知服務程序,需要連接客戶程序(192.168.2.168:2786)。
服務程序收到連接請求后,發(fā)送給notify消息給客戶程序(192.168.2.168:2786),通知客戶程序(192.168.2.168:2786),發(fā)送p2p連接請求給網(wǎng)關(112.93.116.102:6834)。
客戶程序(192.168.2.168:2786)發(fā)送p2p連接請求給網(wǎng)關(112.93.116.102:6834),由于NAT1為完全錐形NAT,所以客戶程序(192.168.0.234:7890)能夠收到客戶程序(192.168.2.168:2786)的請求。
客戶程序(192.168.0.234:7890)收到p2p連接請求后,從請求數(shù)據(jù)中解析出請求發(fā)送者客戶程序(192.168.2.168:2786)的IP地址與端口,并立即返回確認消息。此時雙方進入P2P的穿透模式。
然而在這里有一點需要注意:NAT2為對稱NAT的時候,在3步驟的時候,網(wǎng)關會新生成另一個端口,IP地址不變,用來與NAT1中的網(wǎng)絡進行通信;在4步驟的時候,客戶程序(192.168.0.234:7890)返回數(shù)據(jù)的地址,就是新生成的端口。
圖7 完全錐形NAT的穿透
第二種:NAT 1為限制錐形NAT或者端口限制錐形NAT(兩個錐形NAT模式是一樣的,就不分開解釋了),NAT 2為錐形NAT。如圖8 限制錐形NAT的穿透所示
客戶程序(192.168.0.234:7890)發(fā)送連接請求給服務程序,通知服務程序,需要連接客戶程序(192.168.2.168:2786)。
服務程序收到連接請求后,發(fā)送給notify消息給客戶程序(192.168.2.168:2786),通知客戶程序(192.168.2.168:2786),發(fā)送p2p連接請求給網(wǎng)關(112.93.116.102:6834)。
客戶程序(192.168.2.168:2786)發(fā)送p2p連接請求給網(wǎng)關(112.93.116.102:6834),由于NAT1為限制錐形NAT,所以客戶程序(192.168.0.234:7890)收不到發(fā)送的p2p連接請求,此步驟最終的是在NAT2的網(wǎng)關(157.123.80.165:6954)新生成一條NAT目的地址的記錄。與后續(xù)6步驟作為配合。
客戶程序(192.168.2.168:2786)提醒服務程序通知客戶程序(192.168.0.234:7890),
服務程序馬上通知客戶程序(192.168.0.234:7890)發(fā)送請求給NAT2的網(wǎng)關(157.123.80.165:6954)。
客戶程序(192.168.0.234:7890)發(fā)送p2p連接請求給網(wǎng)關(157.123.80.165:6954),由于剛剛3步驟發(fā)出了請求,此時網(wǎng)關會認為是3步驟返回的響應,所以能夠p2p連接請求發(fā)送給客戶程序(192.168.2.168:2786)
客戶程序(192.168.2.168:2786)收到p2p連接請求后,立即返回確認消息給p2p連接請求包解析出來的IP地址與端口,此確認消息能夠順利到底客戶程序(192.168.0.234:7890),到此網(wǎng)關已經(jīng)穿透,P2P已經(jīng)建立。
圖8 限制錐形NAT的穿透
第三種:NAT1為限制錐形NAT,NAT2為對稱NAT。如圖8限制錐形NAT的穿透所示。
在步驟3和步驟6與NAT2為限制錐形NAT有些差異,其余步驟流程一致。
步驟3:客戶程序(192.168.2.168:2786)發(fā)送p2p連接請求給網(wǎng)關(112.93.116.102:6834),由于NAT2為對稱網(wǎng)絡,此時會重新生成一個端口用于對網(wǎng)關(112.93.116.102:6834)通信。新生成的端口沒有辦法能夠準確的知道。只能進行猜測。
步驟6:發(fā)送數(shù)據(jù)給網(wǎng)關(157.123.80.165:猜測端口)。
在這里提供一種思路來提高測猜的準確度,把服務程序使用兩個端口(之前9800,新加一個9801),由于網(wǎng)關NAT分配端口是順序的,在步驟4發(fā)送請求給服務程序(9801端口),因為步驟3與步驟4相隔時間短,步驟3在網(wǎng)關(157.123.80.165)所生成的新端口比步驟4的端口小。從而來提高猜測的準確度。
??? 相信已經(jīng)對穿透的具體步驟有明確的概念,怎么準確的判斷當前NAT的類型?
?
NAT分類
其實在網(wǎng)絡地址映射概念已經(jīng)有介紹分類,在這里使用更加計算機化語言描述。
第一種,檢測當前客戶程序的網(wǎng)關是否為完全錐形NAT,如圖9檢測完全錐形NAT所示
圖9 檢測完全錐形NAT
首先檢測Udp的可用性,客戶程序(192.168.0.234:7890)使用一個300ms定時器發(fā)送Udp請求數(shù)據(jù)包給服務器A。等待服務器A返回確認數(shù)據(jù)。如果多次發(fā)送請求并未得到服務器的確認數(shù)據(jù),則認為Udp不能信息,則推出整個檢測過程。如果收到確認數(shù)據(jù),同樣使用定時器再發(fā)送另一種請求數(shù)據(jù)要求服務器B發(fā)送數(shù)據(jù)給網(wǎng)關(112.93.116.102:6834),如果收到服務器B的數(shù)據(jù),則認為是完全錐形網(wǎng)絡。如果沒有收到則進行限制錐形NAT。
?
第二種,檢測限制錐形網(wǎng)絡,如圖10所示。
圖10 檢測限制錐形NAT
?
客戶程序(192.168.0.234:7890)定時發(fā)送數(shù)據(jù)包給服務程序A,并要求服務程序從另一個端口發(fā)送數(shù)據(jù)包給網(wǎng)關(112.93.116.102:6834)。若客戶程序(192.168.0.234:7890)收到回應,則該NAT為限制錐形NAT。若多次操作沒有回應,則進行對稱NAT檢測。
?
第三種,檢測當前客戶程序的網(wǎng)關是否為對稱NAT,如圖9所示
客戶程序(192.168.0.234:7890)給服務器A(13.44.178.98:9800)與服務器B(157.78.13.156:23456)發(fā)送數(shù)據(jù)包,對比兩個服務器收到客戶程序的()IP地址與端口是否一致。如果不一致則是對稱網(wǎng)絡。如果一致則該網(wǎng)絡為端口限制錐形NAT。
以下為實現(xiàn)了完全錐形網(wǎng)絡的穿透代碼
udp.h
/* ?*?Author:?WangBoJing ?*?email:?1989wangbojing@gmail.com? ?*?github:?https://github.com/wangbojing ?*/ #ifndef?__UDP_H__ #define?__UDP_H__ #include?<stdio.h> #include?<stdlib.h> #include?<string.h> #include?<sys/socket.h> #include?<netinet/in.h> #include?<unistd.h> #include?<time.h> typedef?unsigned?int?U32; typedef?unsigned?short?U16; typedef?unsigned?char?U8; typedef?volatile?long?UATOMIC; typedef?void*?(*KING_CALLBACK)(void?*arg); typedef?enum?{ ?KING_RESULT_FAILED?=?-1, ?KING_RESULT_SUCCESS?=?0, }?KING_RESULT; typedef?enum?{ ?KING_STATUS_NULL, ?KING_STATUS_LOGIN, ?KING_STATUS_HEARTBEAT, ?KING_STATUS_CONNECT, ?KING_STATUS_MESSAGE, ?KING_STATUS_NOTIFY, ?KING_STATUS_P2P_CONNECT, ?KING_STATUS_P2P_MESSAGE, }?KING_STATUS_SET; #define?KING_CLIENT_MAX????1024 #define?KING_CLIENT_ADDR_LENGTH??6 #define?KING_BUFFER_LENGTH??512 #define?KING_NUMBER_ID_LENGTH???4 typedef?struct?_CLIENT_TABLE?{ ?U8?addr[KING_CLIENT_ADDR_LENGTH];? ?U32?client_id; ?long?stamp; }?client_table; /****************************?status?define?****************************/ #define?KING_PROTO_LOGIN_REQ????0x01 #define?KING_PROTO_LOGIN_ACK????0x81 #define?KING_PROTO_HEARTBEAT_REQ???0x02 #define?KING_PROTO_HEARTBEAT_ACK???0x82 #define?KING_PROTO_CONNECT_REQ????0x11 #define?KING_PROTO_CONNECT_ACK????0x91 #define?NTY_PROTO_NOTIFY_REQ????0x12 #define?NTY_PROTO_NOTIFY_ACK????0x92 #define?NTY_PROTO_P2P_CONNECT_REQ???0x13 #define?NTY_PROTO_P2P_CONNECT_ACK???0x93 #define?NTY_RPORO_MESSAGE_REQ????0x21 #define?NTY_RPORO_MESSAGE_ACK????0xA1 /****************************?context?define?****************************/ #define?KING_PROTO_BUFFER_VERSION_IDX??0 #define?KING_PROTO_BUFFER_STATUS_IDX??1 #define?KING_PROTO_BUFFER_LENGTH_IDX??(KING_PROTO_BUFFER_STATUS_IDX+1) #define?KING_PROTO_BUFFER_SELFID_IDX??(KING_PROTO_BUFFER_LENGTH_IDX+2) //login #define?KING_PROTO_LOGIN_SELFID_IDX???KING_PROTO_BUFFER_SELFID_IDX //heartbeat #define?KING_PROTO_HEARTBEAT_SELFID_IDX??KING_PROTO_BUFFER_SELFID_IDX //connect #define?KING_PROTO_CONNECT_SELFID_IDX??KING_PROTO_BUFFER_SELFID_IDX #define?KING_PROTO_CONNECT_OTHERID_IDX??(KING_PROTO_BUFFER_SELFID_IDX+KING_NUMBER_ID_LENGTH) //notify #define?KING_PROTO_NOTIFY_SELFID_IDX???KING_PROTO_BUFFER_SELFID_IDX #define?KING_PROTO_NOTIFY_ADDR_IDX???(KING_PROTO_BUFFER_SELFID_IDX+KING_NUMBER_ID_LENGTH) //p2p?connect #define?KING_PROTO_P2P_CONNECT_SELFID_IDX?KING_PROTO_BUFFER_SELFID_IDX //p2p?connect?ack #define?KING_PROTO_P2P_CONNECT_ACK_SELFID_IDX?KING_PROTO_BUFFER_SELFID_IDX //message #define?KING_RPORO_MESSAGE_SELFID_IDX??KING_PROTO_BUFFER_SELFID_IDX #define?KING_PROTO_MESSAGE_OTHERID_IDX??(KING_RPORO_MESSAGE_SELFID_IDX+KING_NUMBER_ID_LENGTH) #define?KING_RPORO_MESSAGE_CONTENT_IDX??(KING_PROTO_MESSAGE_OTHERID_IDX+KING_NUMBER_ID_LENGTH) //message?ack #define?KING_RPORO_MESSAGE_ACK_SELFID_IDX?KING_PROTO_BUFFER_SELFID_IDX static?unsigned?long?cmpxchg(UATOMIC?*addr,?unsigned?long?_old,?unsigned?long?_new)?{ ?U8?res; ?__asm__?volatile?( ????????"lock;?cmpxchg?%3,?%1;sete?%0;" ????????:?"=a"?(res) ????????:?"m"?(*addr),?"a"?(_old),?"r"?(_new) ????????:?"cc",?"memory"); ?return?res; } static?long?time_genrator(void)?{ ?static?long?lTimeStamp?=?0; ?static?long?timeStampMutex?=?0; ?if(cmpxchg(&timeStampMutex,?0,?1))?{ ??lTimeStamp?=?time(NULL); ??timeStampMutex?=?0; ?} ?return?lTimeStamp; } static?int?addr_to_array(U8?*array,?struct?sockaddr_in?*p_addr)?{ ?int?i?=?0; ?for?(i?=?0;i?<?4;i?++)?{ ??array[i]?=?*((unsigned?char*)(&p_addr->sin_addr.s_addr)?+?i); ?} ?for?(i?=?0;i?<?2;i?++)?{ ??array[4+i]?=?*((unsigned?char*)(&p_addr->sin_port)+i); ?} } static?int?array_to_addr(U8?*array,?struct?sockaddr_in?*p_addr)?{ ?int?i?=?0; ? ?for?(i?=?0;i?<?4;i?++)?{ ??*((unsigned?char*)(&p_addr->sin_addr.s_addr)?+?i)?=?array[i]; ?} ?for?(i?=?0;i?<?2;i?++)?{ ??*((unsigned?char*)(&p_addr->sin_port)+i)?=?array[4+i]; ?} } static?int?king_send_login(int?sockfd,?int?self_id,?struct?sockaddr_in?*paddr)?{ ?U8?buffer[KING_BUFFER_LENGTH]?=?{0}; ? ?buffer[KING_PROTO_BUFFER_STATUS_IDX]?=?KING_PROTO_LOGIN_REQ; ?*(int?*)(buffer+KING_PROTO_LOGIN_SELFID_IDX)?=?self_id; ?int?n?=?KING_PROTO_LOGIN_SELFID_IDX?+?KING_NUMBER_ID_LENGTH; ? ?n?=?sendto(sockfd,?buffer,?n,?0,?(struct?sockaddr*)paddr,?sizeof(struct?sockaddr_in)); ?if?(n?<?0)?{ ??perror("sendto"); ?} ? ?return?n; } static?int?king_send_heartbeat(int?sockfd,?int?self_id,?struct?sockaddr_in?*paddr)?{ ? ?U8?buffer[KING_BUFFER_LENGTH]?=?{0}; ? ?buffer[KING_PROTO_BUFFER_STATUS_IDX]?=?KING_PROTO_HEARTBEAT_REQ; ?*(int?*)(buffer+KING_PROTO_HEARTBEAT_SELFID_IDX)?=?self_id; ?int?n?=?KING_PROTO_HEARTBEAT_SELFID_IDX?+?KING_NUMBER_ID_LENGTH; ? ?n?=?sendto(sockfd,?buffer,?n,?0,?(struct?sockaddr*)paddr,?sizeof(struct?sockaddr_in)); ?if?(n?<?0)?{ ??perror("sendto"); ?} ? ?return?n; } static?int?king_send_connect(int?sockfd,?int?self_id,?int?other_id,?struct?sockaddr_in?*paddr)?{ ? ?U8?buffer[KING_BUFFER_LENGTH]?=?{0}; ? ?buffer[KING_PROTO_BUFFER_STATUS_IDX]?=?KING_PROTO_CONNECT_REQ; ?*(int?*)(buffer+KING_PROTO_CONNECT_SELFID_IDX)?=?self_id; ?*(int?*)(buffer+KING_PROTO_CONNECT_OTHERID_IDX)?=?other_id; ?int?n?=?KING_PROTO_CONNECT_OTHERID_IDX?+?KING_NUMBER_ID_LENGTH; ? ?n?=?sendto(sockfd,?buffer,?n,?0,?(struct?sockaddr*)paddr,?sizeof(struct?sockaddr_in)); ?if?(n?<?0)?{ ??perror("sendto"); ?} ? ?return?n;? } static?int?king_send_p2pconnect(int?sockfd,?int?self_id,?struct?sockaddr_in?*paddr)?{ ?U8?buffer[KING_BUFFER_LENGTH]?=?{0}; ? ?buffer[KING_PROTO_BUFFER_STATUS_IDX]?=?NTY_PROTO_P2P_CONNECT_REQ; ?*(int?*)(buffer+KING_PROTO_P2P_CONNECT_SELFID_IDX)?=?self_id; ?int?n?=?KING_PROTO_P2P_CONNECT_SELFID_IDX?+?KING_NUMBER_ID_LENGTH; ? ?n?=?sendto(sockfd,?buffer,?n,?0,?(struct?sockaddr*)paddr,?sizeof(struct?sockaddr_in)); ?if?(n?<?0)?{ ??perror("sendto"); ?} ? ?return?n; } static?int?king_send_p2pconnectack(int?sockfd,?int?self_id,?struct?sockaddr_in?*paddr)?{ ? ?U8?buffer[KING_BUFFER_LENGTH]?=?{0}; ? ?buffer[KING_PROTO_BUFFER_STATUS_IDX]?=?NTY_PROTO_P2P_CONNECT_ACK; ?*(int?*)(buffer+KING_PROTO_P2P_CONNECT_ACK_SELFID_IDX)?=?self_id; ?int?n?=?KING_PROTO_P2P_CONNECT_ACK_SELFID_IDX?+?KING_NUMBER_ID_LENGTH; ? ?n?=?sendto(sockfd,?buffer,?n,?0,?(struct?sockaddr*)paddr,?sizeof(struct?sockaddr_in)); ?if?(n?<?0)?{ ??perror("sendto"); ?} ? ?return?n; } static?int?king_client_send_message(int?sockfd,?int?self_id,?int?other_id,?struct?sockaddr_in?*paddr,?U8?*msg,?int?length)?{ ? ?U8?buffer[KING_BUFFER_LENGTH]?=?{0}; ? ?buffer[KING_PROTO_BUFFER_STATUS_IDX]?=?NTY_RPORO_MESSAGE_REQ;? ?*(int?*)(buffer+KING_RPORO_MESSAGE_SELFID_IDX)?=?self_id; ?*(int?*)(buffer+KING_PROTO_MESSAGE_OTHERID_IDX)?=?other_id; ? ?memcpy(buffer+KING_RPORO_MESSAGE_CONTENT_IDX,?msg,?length); ?int?n?=?KING_RPORO_MESSAGE_CONTENT_IDX?+?length; ?*(U16*)(buffer+KING_PROTO_BUFFER_LENGTH_IDX)?=?(U16)?n; ? ?n?=?sendto(sockfd,?buffer,?n,?0,?(struct?sockaddr*)paddr,?sizeof(struct?sockaddr_in)); ?if?(n?<?0)?{ ??perror("sendto"); ?} ?return?n; } static?int?king_send_messageack(int?sockfd,?int?self_id,?struct?sockaddr_in?*paddr)?{ ? ?U8?buffer[KING_BUFFER_LENGTH]?=?{0}; ? ?buffer[KING_PROTO_BUFFER_STATUS_IDX]?=?NTY_RPORO_MESSAGE_ACK; ?*(int?*)(buffer+KING_RPORO_MESSAGE_ACK_SELFID_IDX)?=?self_id; ?int?n?=?KING_RPORO_MESSAGE_ACK_SELFID_IDX?+?KING_NUMBER_ID_LENGTH; ? ?n?=?sendto(sockfd,?buffer,?n,?0,?(struct?sockaddr*)paddr,?sizeof(struct?sockaddr_in)); ?if?(n?<?0)?{ ??perror("sendto"); ?} ? ?return?n; } client_table?table[KING_CLIENT_MAX]?=?{0}; int?client_count?=?0; static?int?get_index_by_clientid(int?client_id)?{ ?int?i?=?0; ?int?now_count?=?client_count; ? ?for?(i?=?0;i?<?now_count;i?++)?{ ??if?(table[i].client_id?==?client_id)?return?i; ?} ? } static?int?king_send_message(int?sockfd,?int?client_id,?U8?*buffer,?int?length)?{ ? ?int?index?=?get_index_by_clientid(client_id); ? ?struct?sockaddr_in?c_addr; ?c_addr.sin_family?=?AF_INET; ?array_to_addr(table[index].addr,?&c_addr); ? ?int?n?=?sendto(sockfd,?buffer,?length,?0,?(struct?sockaddr*)&c_addr,?sizeof(c_addr)); ?if?(n?<?0)?{ ??perror("sendto"); ?} ?return?n; } static?int?king_send_notify(int?sockfd,?int?client_id,?int?self_id)?{ ?U8?buffer[KING_BUFFER_LENGTH]?=?{0}; ?int?index?=?get_index_by_clientid(self_id); ? ?buffer[KING_PROTO_BUFFER_STATUS_IDX]?=?NTY_PROTO_NOTIFY_REQ; ?*(int*)(buffer+KING_PROTO_NOTIFY_SELFID_IDX)?=?self_id; ?memcpy(buffer+KING_PROTO_NOTIFY_ADDR_IDX,?table[index].addr,?KING_CLIENT_ADDR_LENGTH); ? ?index?=?get_index_by_clientid(client_id); ?struct?sockaddr_in?c_addr; ?c_addr.sin_family?=?AF_INET; ?array_to_addr(table[index].addr,?&c_addr); ?int?n?=?KING_PROTO_NOTIFY_ADDR_IDX?+?KING_CLIENT_ADDR_LENGTH; ? ?n?=?sendto(sockfd,?buffer,?n,?0,?(struct?sockaddr*)&c_addr,?sizeof(c_addr)); ?if?(n?<?0)?{ ??perror("sendto"); ?} ?return?n; } #endif
udp_client.c
/* ?*?Author:?WangBoJing ?*?email:?1989wangbojing@gmail.com? ?*?github:?https://github.com/wangbojing ?*/ #include?"udp.h" #include?<pthread.h> static?int?status_machine?=?KING_STATUS_LOGIN; static?int?client_selfid?=?0x0; struct?sockaddr_in?server_addr; client_table?p2p_clients[KING_CLIENT_MAX]?=?{0}; static?int?p2p_count?=?0; static?int?king_client_buffer_parser(int?sockfd,?U8?*buffer,?U32?length,?struct?sockaddr_in?*addr)?{ ? ?U8?status?=?buffer[KING_PROTO_BUFFER_STATUS_IDX]; ? ?switch?(status)?{ ??case?NTY_PROTO_NOTIFY_REQ:?{ ?? ???struct?sockaddr_in?other_addr; ???other_addr.sin_family?=?AF_INET; ??? ???array_to_addr(buffer+KING_PROTO_NOTIFY_ADDR_IDX,?&other_addr); ???king_send_p2pconnect(sockfd,?client_selfid,?&other_addr); ??? ???break; ??} ??case?NTY_PROTO_P2P_CONNECT_REQ:?{ ?? ???int?now_count?=?p2p_count++; ???p2p_clients[now_count].stamp?=?time_genrator(); ??? ???p2p_clients[now_count].client_id?=?*(int*)(buffer+KING_PROTO_P2P_CONNECT_SELFID_IDX); ???addr_to_array(p2p_clients[now_count].addr,?addr); ?? ???king_send_p2pconnectack(sockfd,?client_selfid,?addr); ???printf("Enter?P2P?Model\n"); ???status_machine?=?KING_STATUS_P2P_MESSAGE; ??? ???break; ??} ??case?NTY_PROTO_P2P_CONNECT_ACK:?{ ?? ???int?now_count?=?p2p_count++; ??? ???p2p_clients[now_count].stamp?=?time_genrator(); ???p2p_clients[now_count].client_id?=?*(int*)(buffer+KING_PROTO_P2P_CONNECT_SELFID_IDX); ???addr_to_array(p2p_clients[now_count].addr,?addr); ??? ???printf("Enter?P2P?Model\n"); ???status_machine?=?KING_STATUS_P2P_MESSAGE; ??? ???break; ??} ??case?NTY_RPORO_MESSAGE_REQ:?{ ?? ???U8?*msg?=?buffer+KING_RPORO_MESSAGE_CONTENT_IDX; ???U32?other_id?=?*(U32*)(buffer+KING_RPORO_MESSAGE_SELFID_IDX); ??? ???printf("?from?client:%d?-->?%s\n",?other_id,?msg); ???king_send_messageack(sockfd,?client_selfid,?addr); ???//status_machine?=?KING_STATUS_P2P_MESSAGE; ??? ???break; ??} ??case?KING_PROTO_LOGIN_ACK:?{ ?? ???printf("?Connect?Server?Success\nPlease?Enter?Message?:?"); ???status_machine?=?KING_STATUS_MESSAGE; ??? ???break; ??} ??case?KING_PROTO_HEARTBEAT_ACK: ??case?KING_PROTO_CONNECT_ACK: ??case?NTY_PROTO_NOTIFY_ACK: ???break; ??case?NTY_RPORO_MESSAGE_ACK: ???break; ?} ? } void*?king_recv_callback(void?*arg)?{ ?int?sockfd?=?*(int?*)arg; ?struct?sockaddr_in?addr; ?int?length?=?sizeof(struct?sockaddr_in); ?U8?buffer[KING_BUFFER_LENGTH]?=?{0}; ?//printf("king_recv_callback?-->?enter\n"); ? ?while?(1)?{ ? ??int?n?=?recvfrom(sockfd,?buffer,?KING_BUFFER_LENGTH,?0,?(struct?sockaddr*)&addr,?&length); ??if?(n?>?0)?{ ?? ???buffer[n]?=?0; ???king_client_buffer_parser(sockfd,?buffer,?n,?&addr); ??? ??}?else?if?(n?==?0)?{ ???printf("server?closed\n"); ???close(sockfd); ???break; ??}?else?if?(n?==?-1)?{ ???perror("recvfrom"); ???close(sockfd); ???break; ??} ?} } void?*king_send_callback(void?*arg)?{ ?int?sockfd?=?*(int?*)arg; ?char?buffer[KING_BUFFER_LENGTH]?=?{0}; ?//printf("king_send_callback?-->?enter\n"); ? ?while?(1)?{ ??bzero(buffer,?KING_BUFFER_LENGTH); ?? ??scanf("%s",?buffer); ??//getchar(); ??if?(status_machine?==?KING_STATUS_MESSAGE)?{ ??? ???printf("?-->?please?enter?bt?:?"); ??? ???int?other_id?=?buffer[1]-0x30; ???if?(buffer[0]?==?'C')?{ ??? ????king_send_connect(sockfd,?client_selfid,?other_id,?&server_addr); ???? ???}?else?{ ??? ????int?length?=?strlen(buffer); ????king_client_send_message(sockfd,?client_selfid,?other_id,?&server_addr,?buffer,?length); ???} ?? ??}?else?if?(status_machine?==?KING_STATUS_P2P_MESSAGE)?{ ?? ???printf("?-->?please?enter?message?to?send?:?"); ??? ???int?now_count?=?p2p_count; ???struct?sockaddr_in?c_addr; ???c_addr.sin_family?=?AF_INET; ???array_to_addr(p2p_clients[now_count-1].addr,?&c_addr); ????int?length?=?strlen(buffer); ???king_client_send_message(sockfd,?client_selfid,?0,?&c_addr,?buffer,?length); ??? ??} ?} } int?main(int?argc,?char?*argv[])?{ ?printf("?This?is?a?UDP?Client\n"); ?if?(argc?!=?4)?{ ??printf("Usage:?%s?ip?port\n",?argv[0]); ??exit(1); ?} ? ?int?sockfd?=?socket(AF_INET,?SOCK_DGRAM,?0); ?if?(sockfd?<?0)?{ ??perror("socket"); ??exit(1); ?} ?pthread_t?thread_id[2]?=?{0}; ?KING_CALLBACK?cb[2]?=?{king_send_callback,?king_recv_callback}; ? ?int?i?=?0; ?for?(i?=?0;i?<?2;i?++)?{ ??int?ret?=?pthread_create(&thread_id[i],?NULL,?cb[i],?&sockfd); ??if?(ret)?{ ???perror("pthread_create"); ???exit(1); ??} ??sleep(1); ?} ? ?server_addr.sin_family?=?AF_INET; ?server_addr.sin_port?=?htons(atoi(argv[2])); ?server_addr.sin_addr.s_addr?=?inet_addr(argv[1]); ? ?client_selfid?=?atoi(argv[3]); ?king_send_login(sockfd,?client_selfid,?&server_addr); ?for?(i?=?0;i?<?2;i?++)?{ ??pthread_join(thread_id[i],?NULL); ?} ? ?return?0; }
udp_server.c
/* ?*?Author:?WangBoJing ?*?email:?1989wangbojing@gmail.com? ?*?github:?https://github.com/wangbojing ?*/ #include?"udp.h" int?king_buffer_parser(int?sockfd,?U8?*buffer,?U32?length,?struct?sockaddr_in?*addr)?{ ? ?U8?status?=?buffer[KING_PROTO_BUFFER_STATUS_IDX]; ?printf("king_buffer_parser?-->?%x\n",?status); ? ?switch?(status)?{ ??case?KING_PROTO_LOGIN_REQ:?{ #if?1 ???int?old?=?client_count; ???int?now?=?old+1; ???if(0?==?cmpxchg((UATOMIC*)&client_count,?old,?now))?{? ????printf("client_count?-->?%d,?old:%d,?now:%d\n",?client_count,?old,?now); ????return?KING_RESULT_FAILED; ???} #else ???client_count?=?client_count+1; ???int?now?=?client_count; #endif ???U8?array[KING_CLIENT_ADDR_LENGTH]?=?{0}; ???addr_to_array(array,?addr); ???printf("login?-->?%d.%d.%d.%d:%d\n",?*(unsigned?char*)(&addr->sin_addr.s_addr),?*((unsigned?char*)(&addr->sin_addr.s_addr)+1),????????????? ????*((unsigned?char*)(&addr->sin_addr.s_addr)+2),?*((unsigned?char*)(&addr->sin_addr.s_addr)+3),????????????? ????addr->sin_port); ??? ???table[now].client_id?=??*(U32*)(buffer+KING_PROTO_LOGIN_SELFID_IDX); ???memcpy(table[now].addr,?array,?KING_CLIENT_ADDR_LENGTH); ???break; ??} ??case?KING_PROTO_HEARTBEAT_REQ:?{ ?? ???int?client_id?=?*(unsigned?int*)(buffer+KING_PROTO_HEARTBEAT_SELFID_IDX); ???int?index?=?get_index_by_clientid(client_id); ???table[index].stamp?=?time_genrator(); ??? ???break; ??} ??case?KING_PROTO_CONNECT_REQ:?{ ?? ???int?client_id?=?*(unsigned?int*)(buffer+KING_PROTO_CONNECT_SELFID_IDX); ???int?other_id?=?*(unsigned?int*)(buffer+KING_PROTO_CONNECT_OTHERID_IDX); ???king_send_notify(sockfd,?other_id,?client_id); ??? ???break; ??} ??case?NTY_RPORO_MESSAGE_REQ:?{ ?? ???U8?*msg?=?buffer+KING_RPORO_MESSAGE_CONTENT_IDX; ???int?client_id?=?*(unsigned?int*)(buffer+KING_RPORO_MESSAGE_SELFID_IDX); ???int?other_id?=?*(unsigned?int*)(buffer+KING_PROTO_MESSAGE_OTHERID_IDX); ??? ???printf("?from?client:%d?-->?%s\n",?client_id,?msg); #if?0 ???king_send_message(sockfd,?other_id,?buffer,?length); #endif ???break; ??} ?} ?return?KING_RESULT_SUCCESS; ? } int?main(int?argc,?char?*argv[])?{ ?printf("?This?is?a?UDP?Server\n"); ? ?int?sockfd?=?socket(AF_INET,?SOCK_DGRAM,?0); ?if?(sockfd?<?0)?{ ??perror("socket"); ??exit(0); ?} ? ?struct?sockaddr_in?addr; ?addr.sin_family?=?AF_INET; ?addr.sin_port?=?htons(atoi(argv[1])); ?addr.sin_addr.s_addr?=?htonl(INADDR_ANY); ? ?if?(bind(sockfd,?(struct?sockaddr*)&addr,?sizeof(addr))?<?0)?{ ??perror("bind"); ??exit(1); ?} ? ?char?buffer[KING_BUFFER_LENGTH]?=?{0}; ?struct?sockaddr_in?c_addr; ? ?int?n; ?int?length?=?sizeof(struct?sockaddr_in); ? ?while(1)?{ ?? ??n?=?recvfrom(sockfd,?buffer,?KING_BUFFER_LENGTH,?0,?(struct?sockaddr*)&c_addr,?&length); ??if?(n?>?0)?{ ?? ???buffer[n]?=?0x0; ???printf("%d.%d.%d.%d:%d?say:?%s\n",?*(unsigned?char*)(&c_addr.sin_addr.s_addr),?*((unsigned?char*)(&c_addr.sin_addr.s_addr)+1),????????????? ????*((unsigned?char*)(&c_addr.sin_addr.s_addr)+2),?*((unsigned?char*)(&c_addr.sin_addr.s_addr)+3),????????????? ????c_addr.sin_port,?buffer); ???int?ret?=?king_buffer_parser(sockfd,?buffer,?n,?&c_addr); ???if?(ret?==?KING_RESULT_FAILED)?continue; ???buffer[KING_PROTO_BUFFER_STATUS_IDX]?+=?0x80; ???n?=?sendto(sockfd,?buffer,?n,?0,?(struct?sockaddr*)&c_addr,?sizeof(c_addr)); ???if?(n?<?0)?{ ????perror("sendto"); ????break; ???} ??}?else?if?(n?==?0)?{ ???printf("server?closed\n"); ??}?else?{ ???perror("recv"); ???break; ??} ?} ? ?return?0; }
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內容。