隨著區塊鏈的越來越火,去中心化的網絡設計再次被拿到技術人員面前。在這裡我使用非常通俗的語言,幫大家來理解去中心化的網絡設計的基礎—網絡穿透。再使用代碼來實現穿透。如果闡述不到位的地方,歡迎大家拋磚。
在有中心化服務器的網絡中,客戶端,服務器,網關構成網絡拓撲圖。如下圖1所示:由於後續出現的名詞概念很多,先約法三章,在這裡統一一下稱呼:所有的終端機器成為客戶端,不同客戶端使用大寫字母區分(A,B,C,…);客戶端上面運行的應用程序統一稱為客戶程序,不同的應用程序使用不數字區分(1,2,3,…)。作為服務器的物理機稱為服務器,而服務器上運行的程序稱為服務程序,後文中每一個拓撲組件都只有一個IP地址。為客戶端提供公網IP服務的組件稱為網關。
圖1 中心化服務器的網絡拓撲圖
從網關映射到客戶端中的網絡結構,這裡需要引入一個NAT的概念。什麼NAT呢?中文名叫網絡地址轉換,習慣稱為網絡地址映射。為什麼需要網絡地址映射呢?:需要說到IPV4網絡地址已經用完,全部使用IPV6又會造成很多隻支持IPV4的終端設備無法正常使用,所以網絡地址映射應運而生,忍辱負重。才會有我們現在所謂的網絡穿透的出現。到底怎麼映射的?如圖2網絡地址映射所示。客戶程序使用192.168.0.234:7890發送數據,通過網關的網絡地址映射在公網被轉換為112.93.116.102:6834,被互聯網上的大家所認知。此時在公網上使用客戶程序的ip與端口被112.93.116.102:6834代替。在這裡大家應該明白了NAT是何許物種了。
圖2 網絡地址映射
為了保持新手福音,業界良心的態度。什麼是穿透?因為NAT是客戶程序發起的,網絡為了保持通訊新建的一個臨時牌照,隨時可能被收回,而且重新發起後的牌照不一樣。從而外界及時知道了這個臨時牌照也沒有用。所以需要通過穿透在網關上面打個洞,來為外界進行服務。那NAT與穿透有什麼關係呢?正因為有了NAT才需要穿透,如果是IPV6每個客戶端一個IP地址,那就不需要直接可以找到客戶端了。
網絡地址映射
由於網關的安全性要求不一致,就出現四種不同的NAT方式。分別進行闡述:
第一種完全錐形NAT,英文名叫Full Cone NAT。如圖3完全錐形NAT所示,客戶程序(192.168.0.234:7890)與服務器A(13.44.178.98:9800)通信,通過網關的地址轉換產生的臨時牌照的公網地址(112.93.116.102:6834),服務器B(157.78.13.156:23456)發送數據到公網地址(112.93.116.102:6834),如果客戶程序(192.168.0.234:7890)能夠收到服務器B(157.78.13.156:23456)發送的數據,這種NAT映射關係為完全錐形NAT;
圖3 完全錐形NAT
第二種限制錐形NAT,英文名叫RestrictedCone NAT。在圖3 完全錐形NAT中,如果客戶程序(192.168.0.234:7890)不能收到服務器B(157.78.13.156:23456)發送的數據,這種NAT映射關係為限制型錐形NAT。
第三種端口限制錐形NAT,英文名叫Port RestrictedCone NAT。客戶程序(192.168.0.234:7890)發送數據給服務程序(13.44.178.98:9800),網關通過網絡地址轉換產生的地址(112.93.116.102:6834),同樣的服務器內的另一個服務程序(13.44.178.178:9801)發送數據給網關(112.93.116.102:6834)地址,如果客戶程序(192.168.0.234:7890)能夠收到,則為限制錐形NAT,如果客戶程序(192.168.0.234:7890)不能收到,則為端口限制錐形NAT。
對於所有的錐型NAT,客戶程序(192.168.0.234:7890)對外發送的數據時,網關地址轉換的地址都是一樣的為(112.93.116.102:6834),那為什麼在圖4 限制型錐形NAT中,客戶程序不能收到服務程序B(13.44.178.98:9801)的數據呢?因為在網關中沒有發生過客戶程序(192.168.0.234:7890)給服務程序B(13.44.178.98:9801),故服務程序(13.44.178.98:9801)直接發送給網關(112.93.116.102:6834),則被網關所丟棄。
圖4 限制型錐形NAT
第四種對稱NAT,英文,名叫Symmetric NAT。如圖5對稱NAT所示,客戶程序(192.168.0.234:7890)發送數據給兩個不同服務器(13.44.178.98:9800)和(157.78.13.156:23456)時,網關會進行不同的網絡地址映射產生(112.93.116.102:6834)和(112.93.116.102:6835)。這是對於整個NAT網絡發送數據出去的過程,而接收數據與端口限制錐形NAT一致。
圖5 對稱NAT
本節介紹三種錐形NAT和對稱NAT的概念,相信到此你還是不知道NAT類型與怎麼穿透網關友什麼關係。
穿透剖析
怎麼穿透網關來實現去中心化,如圖6穿透網絡NAT拓撲圖所示
在理想的情況下,在NAT 1中客戶程序(192.168.0.234:7890)知道NAT 2中客戶程序(192.168.2.168:2786)的網絡映射地址(157.123.80.165:6954),並給網絡映射地址(157.123.80.165:6954)發送數據,並且客戶程序(192.168.2.168:2786)能夠收到數據;而NAT 2中客戶程序(192.168.2.168:2786)也知道NAT 1中客戶程序的網絡映射地址,並給其網絡映射地址(112.93.116.102:6834)發送數據,並且也能收到數據。此時對於服務器而言,就已經沒有起到數據中轉的作用,此時客戶程序(192.168.0.234:7890)與客戶程序(192.168.2.168:2786)能夠互相收發數據,服務程序(13.44.178.98:9800)已經沒有作用,對於客戶端程序來說,已經實現了去中心化。
這只是在理論情況,現在具體實現步驟以及結合四種NAT類型來分析一下。
第一種:NAT 1為完全錐形NAT,NAT 2為任何一種NAT模式,如圖7 完全錐形NAT的穿透,綠色字體的順序。
客戶程序(192.168.0.234:7890)先發送一個連接請求給服務程序,通知服務程序,需要連接客戶程序(192.168.2.168:2786)。
服務程序收到連接請求後,發送給notify消息給客戶程序(192.168.2.168:2786),通知客戶程序(192.168.2.168:2786),發送p2p連接請求給網關(112.93.116.102:6834)。
客戶程序(192.168.2.168:2786)發送p2p連接請求給網關(112.93.116.102:6834),由於NAT1為完全錐形NAT,所以客戶程序(192.168.0.234:7890)能夠收到客戶程序(192.168.2.168:2786)的請求。
客戶程序(192.168.0.234:7890)收到p2p連接請求後,從請求數據中解析出請求發送者客戶程序(192.168.2.168:2786)的IP地址與端口,並立即返回確認消息。此時雙方進入P2P的穿透模式。
然而在這裡有一點需要注意:NAT2為對稱NAT的時候,在3步驟的時候,網關會新生成另一個端口,IP地址不變,用來與NAT1中的網絡進行通信;在4步驟的時候,客戶程序(192.168.0.234:7890)返回數據的地址,就是新生成的端口。
圖7 完全錐形NAT的穿透
第二種:NAT 1為限制錐形NAT或者端口限制錐形NAT(兩個錐形NAT模式是一樣的,就不分開解釋了),NAT 2為錐形NAT。如圖8 限制錐形NAT的穿透所示
客戶程序(192.168.0.234:7890)發送連接請求給服務程序,通知服務程序,需要連接客戶程序(192.168.2.168:2786)。
服務程序收到連接請求後,發送給notify消息給客戶程序(192.168.2.168:2786),通知客戶程序(192.168.2.168:2786),發送p2p連接請求給網關(112.93.116.102:6834)。
客戶程序(192.168.2.168:2786)發送p2p連接請求給網關(112.93.116.102:6834),由於NAT1為限制錐形NAT,所以客戶程序(192.168.0.234:7890)收不到發送的p2p連接請求,此步驟最終的是在NAT2的網關(157.123.80.165:6954)新生成一條NAT目的地址的記錄。與後續6步驟作為配合。
客戶程序(192.168.2.168:2786)提醒服務程序通知客戶程序(192.168.0.234:7890),
服務程序馬上通知客戶程序(192.168.0.234:7890)發送請求給NAT2的網關(157.123.80.165:6954)。
客戶程序(192.168.0.234:7890)發送p2p連接請求給網關(157.123.80.165:6954),由於剛剛3步驟發出了請求,此時網關會認為是3步驟返回的響應,所以能夠p2p連接請求發送給客戶程序(192.168.2.168:2786)
客戶程序(192.168.2.168:2786)收到p2p連接請求後,立即返回確認消息給p2p連接請求包解析出來的IP地址與端口,此確認消息能夠順利到底客戶程序(192.168.0.234:7890),到此網關已經穿透,P2P已經建立。
圖8 限制錐形NAT的穿透
第三種:NAT1為限制錐形NAT,NAT2為對稱NAT。如圖8限制錐形NAT的穿透所示。
在步驟3和步驟6與NAT2為限制錐形NAT有些差異,其餘步驟流程一致。
步驟3:客戶程序(192.168.2.168:2786)發送p2p連接請求給網關(112.93.116.102:6834),由於NAT2為對稱網絡,此時會重新生成一個端口用於對網關(112.93.116.102:6834)通信。新生成的端口沒有辦法能夠準確的知道。只能進行猜測。
步驟6:發送數據給網關(157.123.80.165:猜測端口)。
在這裡提供一種思路來提高測猜的準確度,把服務程序使用兩個端口(之前9800,新加一個9801),由於網關NAT分配端口是順序的,在步驟4發送請求給服務程序(9801端口),因為步驟3與步驟4相隔時間短,步驟3在網關(157.123.80.165)所生成的新端口比步驟4的端口小。從而來提高猜測的準確度。
相信已經對穿透的具體步驟有明確的概念,怎麼準確的判斷當前NAT的類型?
NAT分類
其實在網絡地址映射概念已經有介紹分類,在這裡使用更加計算機化語言描述。
第一種,檢測當前客戶程序的網關是否為完全錐形NAT,如圖9檢測完全錐形NAT所示
圖9 檢測完全錐形NAT
首先檢測Udp的可用性,客戶程序(192.168.0.234:7890)使用一個300ms定時器發送Udp請求數據包給服務器A。等待服務器A返回確認數據。如果多次發送請求並未得到服務器的確認數據,則認為Udp不能信息,則推出整個檢測過程。如果收到確認數據,同樣使用定時器再發送另一種請求數據要求服務器B發送數據給網關(112.93.116.102:6834),如果收到服務器B的數據,則認為是完全錐形網絡。如果沒有收到則進行限制錐形NAT。
第二種,檢測限制錐形網絡,如圖10所示。
圖10 檢測限制錐形NAT
客戶程序(192.168.0.234:7890)定時發送數據包給服務程序A,並要求服務程序從另一個端口發送數據包給網關(112.93.116.102:6834)。若客戶程序(192.168.0.234:7890)收到回應,則該NAT為限制錐形NAT。若多次操作沒有回應,則進行對稱NAT檢測。
第三種 ,檢測當前客戶程序的網關是否為對稱NAT,如圖9所示
客戶程序(192.168.0.234:7890)給服務器A(13.44.178.98:9800)與服務器B(157.78.13.156:23456)發送數據包,對比兩個服務器收到客戶程序的()IP地址與端口是否一致。如果不一致則是對稱網絡。如果一致則該網絡為端口限制錐形NAT。
以下為實現了完全錐形網絡的穿透代碼
udp.h
/*
* Author: WangBoJing
* email: [email protected]
* github: https://github.com/wangbojing
*/
#ifndef __UDP_H__
#define __UDP_H__
#include
#include
#include
#include
#include
#include
#include
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
udp_client.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 |
* Author: WangBoJing
|
udp_server.c
/*
* Author: WangBoJing
* email: [email protected]
* 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
;
}
}
每天會更新論文和視頻,還有如果想學習c++知識推薦在晚上8.30免費觀看這個直播:https://ke.qq.com/course/131973#tuin=b52b9a80
閱讀更多 IT布丁老師 的文章