去中心化的網絡設計—P2P的實現

去中心化的網絡設計—P2P的實現

隨著區塊鏈的越來越火,去中心化的網絡設計再次被拿到技術人員面前。在這裡我使用非常通俗的語言,幫大家來理解去中心化的網絡設計的基礎—網絡穿透。再使用代碼來實現穿透。如果闡述不到位的地方,歡迎大家拋磚。

在有中心化服務器的網絡中,客戶端,服務器,網關構成網絡拓撲圖。如下圖1所示:由於後續出現的名詞概念很多,先約法三章,在這裡統一一下稱呼:所有的終端機器成為客戶端,不同客戶端使用大寫字母區分(A,B,C,…);客戶端上面運行的應用程序統一稱為客戶程序,不同的應用程序使用不數字區分(1,2,3,…)。作為服務器的物理機稱為服務器,而服務器上運行的程序稱為服務程序,後文中每一個拓撲組件都只有一個IP地址。為客戶端提供公網IP服務的組件稱為網關。

去中心化的網絡設計—P2P的實現

圖1 中心化服務器的網絡拓撲圖

從網關映射到客戶端中的網絡結構,這裡需要引入一個NAT的概念。什麼NAT呢?中文名叫網絡地址轉換,習慣稱為網絡地址映射。為什麼需要網絡地址映射呢?:需要說到IPV4網絡地址已經用完,全部使用IPV6又會造成很多隻支持IPV4的終端設備無法正常使用,所以網絡地址映射應運而生,忍辱負重。才會有我們現在所謂的網絡穿透的出現。到底怎麼映射的?如圖2網絡地址映射所示。客戶程序使用192.168.0.234:7890發送數據,通過網關的網絡地址映射在公網被轉換為112.93.116.102:6834,被互聯網上的大家所認知。此時在公網上使用客戶程序的ip與端口被112.93.116.102:6834代替。在這裡大家應該明白了NAT是何許物種了。

去中心化的網絡設計—P2P的實現

圖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;

去中心化的網絡設計—P2P的實現

圖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),則被網關所丟棄。

去中心化的網絡設計—P2P的實現

圖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一致。

去中心化的網絡設計—P2P的實現

圖5 對稱NAT

本節介紹三種錐形NAT和對稱NAT的概念,相信到此你還是不知道NAT類型與怎麼穿透網關友什麼關係。

穿透剖析

怎麼穿透網關來實現去中心化,如圖6穿透網絡NAT拓撲圖所示

去中心化的網絡設計—P2P的實現

在理想的情況下,在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的穿透,綠色字體的順序。

  1. 客戶程序(192.168.0.234:7890)先發送一個連接請求給服務程序,通知服務程序,需要連接客戶程序(192.168.2.168:2786)。

  2. 服務程序收到連接請求後,發送給notify消息給客戶程序(192.168.2.168:2786),通知客戶程序(192.168.2.168:2786),發送p2p連接請求給網關(112.93.116.102:6834)。

  3. 客戶程序(192.168.2.168:2786)發送p2p連接請求給網關(112.93.116.102:6834),由於NAT1為完全錐形NAT,所以客戶程序(192.168.0.234:7890)能夠收到客戶程序(192.168.2.168:2786)的請求。

  4. 客戶程序(192.168.0.234:7890)收到p2p連接請求後,從請求數據中解析出請求發送者客戶程序(192.168.2.168:2786)的IP地址與端口,並立即返回確認消息。此時雙方進入P2P的穿透模式。

然而在這裡有一點需要注意:NAT2為對稱NAT的時候,在3步驟的時候,網關會新生成另一個端口,IP地址不變,用來與NAT1中的網絡進行通信;在4步驟的時候,客戶程序(192.168.0.234:7890)返回數據的地址,就是新生成的端口。

去中心化的網絡設計—P2P的實現

圖7 完全錐形NAT的穿透

第二種:NAT 1為限制錐形NAT或者端口限制錐形NAT(兩個錐形NAT模式是一樣的,就不分開解釋了),NAT 2為錐形NAT。如圖8 限制錐形NAT的穿透所示

  1. 客戶程序(192.168.0.234:7890)發送連接請求給服務程序,通知服務程序,需要連接客戶程序(192.168.2.168:2786)。

  2. 服務程序收到連接請求後,發送給notify消息給客戶程序(192.168.2.168:2786),通知客戶程序(192.168.2.168:2786),發送p2p連接請求給網關(112.93.116.102:6834)。

  3. 客戶程序(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步驟作為配合。

  4. 客戶程序(192.168.2.168:2786)提醒服務程序通知客戶程序(192.168.0.234:7890),

  5. 服務程序馬上通知客戶程序(192.168.0.234:7890)發送請求給NAT2的網關(157.123.80.165:6954)。

  6. 客戶程序(192.168.0.234:7890)發送p2p連接請求給網關(157.123.80.165:6954),由於剛剛3步驟發出了請求,此時網關會認為是3步驟返回的響應,所以能夠p2p連接請求發送給客戶程序(192.168.2.168:2786)

  7. 客戶程序(192.168.2.168:2786)收到p2p連接請求後,立即返回確認消息給p2p連接請求包解析出來的IP地址與端口,此確認消息能夠順利到底客戶程序(192.168.0.234:7890),到此網關已經穿透,P2P已經建立。

去中心化的網絡設計—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所示

去中心化的網絡設計—P2P的實現

圖9 檢測完全錐形NAT

首先檢測Udp的可用性,客戶程序(192.168.0.234:7890)使用一個300ms定時器發送Udp請求數據包給服務器A。等待服務器A返回確認數據。如果多次發送請求並未得到服務器的確認數據,則認為Udp不能信息,則推出整個檢測過程。如果收到確認數據,同樣使用定時器再發送另一種請求數據要求服務器B發送數據給網關(112.93.116.102:6834),如果收到服務器B的數據,則認為是完全錐形網絡。如果沒有收到則進行限制錐形NAT。

第二種,檢測限制錐形網絡,如圖10所示。

去中心化的網絡設計—P2P的實現

圖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

* email: [email protected]

* github: https://github.com/wangbojing

*/

#include "udp.h"

#include

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: [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


分享到:


相關文章: