去中心化的网络设计 — P2P的实现

2019年12月08日 阅读数:78
这篇文章主要向大家介绍去中心化的网络设计 — P2P的实现,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

随着区块链的愈来愈火,去中心化的网络设计再次被拿到技术人员面前。在这里我使用很是通俗的语言,帮你们来理解去中心化的网络设计的基础—网络穿透。再使用代码来实现穿透。若是阐述不到位的地方,欢迎你们抛砖。git

 

    在有中心化服务器的网络中,客户端,服务器,网关构成网络拓扑图。以下图1所示:因为后续出现的名词概念不少,先约法三章,在这里统一一下称呼:全部的终端机器成为客户端,不一样客户端使用大写字母区分(A,B,C,…);客户端上面运行的应用程序统一称为客户程序,不一样的应用程序使用不数字区分(1,2,3,…)。做为服务器的物理机称为服务器,而服务器上运行的程序称为服务程序,后文中每个拓扑组件都只有一个IP地址。为客户端提供公网IP服务的组件称为网关。github

wKioL1nGkdbRpj8jAAB4putmiMQ017.png-wh_50

图1 中心化服务器的网络拓扑图安全


从网关映射到客户端中的网络结构,这里须要引入一个NAT的概念。什么NAT呢?中文名叫网络地址转换,习惯称为网络地址映射。为何须要网络地址映射呢?:须要说到IPV4网络地址已经用完,所有使用IPV6又会形成不少只支持IPV4的终端设备没法正常使用,因此网络地址映射应运而生,忍辱负重。才会有咱们如今所谓的网络穿透的出现。到底怎么映射的?如图2网络地址映射所示。客户程序使用192.168.0.234:7890发送数据,经过网关的网络地址映射在公网被转换为112.93.116.102:6834,被互联网上的你们所认知。此时在公网上使用客户程序的ip与端口被112.93.116.102:6834代替。在这里你们应该明白了NAT是何许物种了。
服务器

wKiom1nGkg_Q03LBAABXmGw3UOQ516.png-wh_50

图2 网络地址映射网络

为了保持新手福音,业界良心的态度。什么是穿透?由于NAT是客户程序发起的,网络为了保持通信新建的一个临时牌照,随时可能被收回,并且从新发起后的牌照不同。从而外界及时知道了这个临时牌照也没有用。因此须要经过穿透在网关上面打个洞,来为外界进行服务。那NAT与穿透有什么关系呢?正由于有了NAT才须要穿透,若是是IPV6每一个客户端一个IP地址,那就不须要直接能够找到客户端了。app

   

网络地址映射socket

 

    因为网关的安全性要求不一致,就出现四种不一样的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;字体

wKiom1nGkhGRaHQkAABqja7Jt88832.png-wh_50



图3 彻底锥形NATspa


第二种限制锥形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),则被网关所丢弃。

wKiom1nGkhCjX6CuAABYmhK5jE4855.png-wh_50

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

wKiom1nGkhCzHbmVAABstzph6sM258.png-wh_50


图5 对称NAT


本节介绍三种锥形NAT和对称NAT的概念,相信到此你仍是不知道NAT类型与怎么穿透网关友什么关系。

 

穿透剖析

 

    怎么穿透网关来实现去中心化,如图6穿透网络NAT拓扑图所示

wKiom1nGkhDyDwdxAAB4Jpb2pAA676.png-wh_50



在理想的状况下,在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)返回数据的地址,就是新生成的端口。

wKioL1nGkdejVvCGAACSBtVxl_E171.png-wh_50


图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已经创建。


wKioL1nGkdiAKZ-cAAChO9RNALs726.png-wh_50

图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所示

wKioL1nGkdeyikmBAABxHWZavbg981.png-wh_50


图9 检测彻底锥形NAT


首先检测Udp的可用性,客户程序(192.168.0.234:7890)使用一个300ms定时器发送Udp请求数据包给服务器A。等待服务器A返回确认数据。若是屡次发送请求并未获得服务器的确认数据,则认为Udp不能信息,则推出整个检测过程。若是收到确认数据,一样使用定时器再发送另外一种请求数据要求服务器B发送数据给网关(112.93.116.102:6834),若是收到服务器B的数据,则认为是彻底锥形网络。若是没有收到则进行限制锥形NAT。

 

第二种,检测限制锥形网络,如图10所示。

wKioL1nGkdeQXaSUAABtIOVt-Ik423.png-wh_50

图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

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
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
323
324
/*
  * 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

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

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
/*
  * 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;
}