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

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

 

    在有中心化服务器的网络中,客户端,服务器,网关构成网络拓扑图。如下图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的穿透,绿色字体的顺序。

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


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


图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

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

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

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

 

    在有中心化服务器的网络中,客户端,服务器,网关构成网络拓扑图。如下图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的穿透,绿色字体的顺序。

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


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


图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

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