码迷,mamicode.com
首页 > Web开发 > 详细

三种网络数据传输方式比较(byte stream,protobuf,json)

时间:2015-10-23 16:17:34      阅读:738      评论:0      收藏:0      [点我收藏+]

标签:

    针对于tinynet进行了三种数据传输方式的测试,包括最初的byte stream,protobuf,以及比较流行json方式。跟之前的几次测试一样,模型为echo模型,都是以epoll为例,每个连接每秒发送10个包,每个数据包约100bytes,数据包括包头以及数据,包头12bytes,包括长度以及其他8bytes信息,客户端连接为每秒5个,此次测试总计连接10000个.
    之前的测试报告:
    工程源代码:
    服务器是多线程模型,主线程负责接收处理网络数据,并添加的输入缓冲区中;读线程从输入缓冲中读取数据并进行处理,将处理的数据添加的输出缓冲区中;写线程将输出缓冲区的数据发送到指定的客户端连接。由于数据处理只是在读线程处理,所以只需要变更读线程的处理代码就可以了。( 默认读写线程的sleep时间为500ms)
    下面分别为服务器端读线程byte stream,protobuf,json三种方式的部分代码:

点击(此处)折叠或打开

  1. // byte stream
  2. void Server_Impl::_read_thread()
  3. {
  4.     static const int __head_size = 12;
  5.     char __read_buf[max_buffer_size_] = {};
  6.     while (true)
  7.     {
  8.         lock_.acquire_lock();
  9. #ifdef __DEBUG
  10.         struct timeval __start_timeval;
  11.         gettimeofday(&__start_timeval, NULL);
  12.         long __start_time = __start_timeval.tv_usec;
  13. #endif //__DEBUG
  14.         for (std::vector<Buffer*>::iterator __it = connects_copy.begin(); __it != connects_copy.end(); ++__it)
  15.         {
  16.             if(*__it)
  17.             {
  18.                 Buffer::ring_buffer* __input = (*__it)->input_;
  19.                 Buffer::ring_buffer* __output = (*__it)->output_;
  20.                 if (!__input || !__output)
  21.                 {
  22.                     continue;
  23.                 }
  24. #ifdef __DEBUG
  25.                 if(0 == (*__it)->fd_ % 1000)
  26.                 {
  27.                     printf("fd =%d,rpos = %d, wpos = %d\n",(*__it)->fd_,__input->rpos(),__input->wpos());
  28.                 }
  29. #endif //__DEBUG
  30.                 while (!__input->read_finish())
  31.                 {
  32.                     int __packet_length = 0;
  33.                     int __log_level = 0;
  34.                     int __frame_number = 0;
  35.                     unsigned char __packet_head[__head_size] = {};
  36.                     int __head = 0;
  37.                     unsigned int __guid = 0;
  38.                     if(!__input->pre_read((unsigned char*)&__packet_head,__head_size))
  39.                     {
  40.                         //    not enough data for read
  41.                         break;
  42.                     }
  43.                     memcpy(&__packet_length,__packet_head,4);
  44. #if 0
  45.                     memcpy(&__head,__packet_head + 4,4);
  46.                     memcpy(&__guid,__packet_head + 8,4);
  47. #endif
  48.                     if(!__packet_length)
  49.                     {
  50.                         printf("__packet_length error\n");
  51.                         break;
  52.                     }
  53. #if 0
  54.                     __log_level = (__head) & 0x000000ff;
  55.                     __frame_number = (__head >> 8);
  56. #endif
  57.                     memset(__read_buf,0,max_buffer_size_);
  58.                     if(__input->read((unsigned char*)__read_buf,__packet_length + __head_size))
  59.                     {
  60.                         __output->append((unsigned char*)__read_buf,__packet_length + __head_size);
  61.                     }
  62.                     else
  63.                     {
  64.                         break;
  65.                     }
  66.                 }
  67.             }
  68.         }
  69. #ifdef __DEBUG
  70.         struct timeval __end_timeval;
  71.         gettimeofday(&__end_timeval, NULL);
  72.         long __end_time = __end_timeval.tv_usec;
  73.         long __time_read = __end_time - __start_time;
  74.         printf("start time = %ld, end time = %ld,server impl time read = %ld\n",__start_time,__end_time,__time_read);
  75. #endif //__DEBUG
  76.         lock_.release_lock();
  77. #ifdef __LINUX
  78.         usleep(max_sleep_time_);
  79. #endif // __LINUX
  80.     }
  81. }
  82. // protobuf
  83. void Server_Impl::_read_thread()
  84. {
  85.     transfer::Packet __packet_protobuf;
  86.     std::string      __string_packet;
  87.     static const int __head_size = 4;
  88.     while (true)
  89.     {
  90.         lock_.acquire_lock();
  91. #ifdef __DEBUG
  92.         struct timeval __start_timeval;
  93.         gettimeofday(&__start_timeval, NULL);
  94.         long __start_time = __start_timeval.tv_usec;
  95. #endif //__DEBUG
  96.         for (std::vector<Buffer*>::iterator __it = connects_copy.begin(); __it != connects_copy.end(); ++__it)
  97.         {
  98.             if(*__it)
  99.             {
  100.                 Buffer::ring_buffer* __input = (*__it)->input_;
  101.                 Buffer::ring_buffer* __output = (*__it)->output_;
  102.                 if (!__input || !__output)
  103.                 {
  104.                     continue;
  105.                 }
  106. #ifdef __DEBUG
  107.                 if(0 == (*__it)->fd_ % 1000)
  108.                 {
  109.                     printf("fd =%d,rpos = %d, wpos = %d\n",(*__it)->fd_,__input->rpos(),__input->wpos());
  110.                 }
  111. #endif //__DEBUG
  112.                 while (!__input->read_finish())
  113.                 {
  114.                     int __packet_length = 0;
  115.                     int __log_level = 0;
  116.                     int __frame_number = 0;
  117.                     unsigned char __packet_head[__head_size] = {};
  118.                     int __head = 0;
  119.                     unsigned int __guid = 0;
  120.                     if(!__input->pre_read(__packet_head,__head_size))
  121.                     {
  122.                         //    not enough data for read
  123.                         break;
  124.                     }
  125.                     __packet_length = (int)*__packet_head;
  126.                     if(!__packet_length)
  127.                     {
  128.                         printf("__packet_length error\n");
  129.                         break;
  130.                     }
  131. #if 0
  132.                     //    easy_bool read(easy_uint8* des,size_t len)
  133.                     char __read_buf[max_buffer_size_] = {};
  134.                     if(__input->read((unsigned char*)__read_buf,__packet_length + __head_size))
  135.                     {
  136.                         __string_packet = __read_buf + __head_size;
  137.                         __packet_protobuf.ParseFromString(__string_packet);
  138.                         __output->append((unsigned char*)__read_buf,__packet_length + __head_size);
  139. #else
  140.                     //    easy_bool read(std::string& des,size_t len)
  141.                     __packet_protobuf.Clear();
  142.                     __string_packet.clear();
  143.                     if(__input->read(__string_packet,__packet_length + __head_size))
  144.                     {
  145.                         __packet_protobuf.ParseFromString(__string_packet.c_str() + __head_size);
  146.                         __output->append((unsigned char*)__string_packet.c_str(),__packet_length + __head_size);
  147. #endif
  148. #ifdef __DEBUG
  149.                         printf("__packet_protobuf.head = %d\n",__packet_protobuf.head());
  150.                         printf("__packet_protobuf.guid = %d\n",__packet_protobuf.guid());
  151.                         printf("__packet_protobuf.content = %s\n",__packet_protobuf.content().c_str());
  152. #endif //__DEBUG
  153.                     }
  154.                     else
  155.                     {
  156.                         break;
  157.                     }
  158.                 }
  159.             }
  160.         }
  161. #ifdef __DEBUG
  162.         struct timeval __end_timeval;
  163.         gettimeofday(&__end_timeval, NULL);
  164.         long __end_time = __end_timeval.tv_usec;
  165.         long __time_read = __end_time - __start_time;
  166.         printf("start time = %ld, end time = %ld,server protobuf impl time read = %ld\n",__start_time,__end_time,__time_read);
  167. #endif //__DEBUG
  168.         lock_.release_lock();
  169. #ifdef __LINUX
  170.         usleep(max_sleep_time_);
  171. #endif // __LINUX
  172.     }
  173. }
  174. // json
  175. void Server_Impl::_read_thread()
  176. {
  177.     static const int __head_size = 4;
  178.     while (true)
  179.     {
  180.         lock_.acquire_lock();
  181. #ifdef __DEBUG
  182.         struct timeval __start_timeval;
  183.         gettimeofday(&__start_timeval, NULL);
  184.         long __start_time = __start_timeval.tv_usec;
  185. #endif //__DEBUG
  186.         for (std::vector<Buffer*>::iterator __it = connects_copy.begin(); __it != connects_copy.end(); ++__it)
  187.         {
  188.             if(*__it)
  189.             {
  190.                 Buffer::ring_buffer* __input = (*__it)->input_;
  191.                 Buffer::ring_buffer* __output = (*__it)->output_;
  192.                 if (!__input || !__output)
  193.                 {
  194.                     continue;
  195.                 }
  196. #ifdef __DEBUG
  197.                 if(0 == (*__it)->fd_ % 1000)
  198.                 {
  199.                     printf("fd =%d,rpos = %d, wpos = %d\n",(*__it)->fd_,__input->rpos(),__input->wpos());
  200.                 }
  201. #endif //__DEBUG
  202.                 while (!__input->read_finish())
  203.                 {
  204.                     int __packet_length = 0;
  205.                     int __log_level = 0;
  206.                     int __frame_number = 0;
  207.                     unsigned char __packet_head[__head_size] = {};
  208.                     int __head = 0;
  209.                     unsigned int __guid = 0;
  210.                     if(!__input->pre_read(__packet_head,__head_size))
  211.                     {
  212.                         //    not enough data for read
  213.                         break;
  214.                     }
  215.                     __packet_length = (int)*__packet_head;
  216.                     if(!__packet_length)
  217.                     {
  218.                         printf("__packet_length error\n");
  219.                         break;
  220.                     }
  221.                     char __read_buf[max_buffer_size_] = {};
  222.                     if(__input->read((unsigned char*)__read_buf,__packet_length + __head_size))
  223.                     {
  224.                         json_error_t* __json_error = NULL;
  225.                         json_t* __json_loads = json_loads(__read_buf + __head_size,JSON_DECODE_ANY,__json_error);
  226.                         __output->append((unsigned char*)__read_buf,__packet_length + __head_size);
  227. #ifdef __DEBUG
  228.                         json_t* __json_loads_head = json_object_get(__json_loads,"head");
  229.                         json_t* __json_loads_guid = json_object_get(__json_loads,"guid");
  230.                         json_t* __json_loads_content = json_object_get(__json_loads,"content");
  231.                         printf("json.head = %d\n",json_integer_value(__json_loads_head));
  232.                         printf("json.guid = %d\n",json_integer_value(__json_loads_guid));
  233.                         printf("json.content = %s\n",json_string_value(__json_loads_content));
  234. #endif //__DEBUG
  235.                         json_decref(__json_loads);
  236.                     }
  237.                     else
  238.                     {
  239.                         break;
  240.                     }
  241.                 }
  242.             }
  243.         }
  244. #ifdef __DEBUG
  245.         struct timeval __end_timeval;
  246.         gettimeofday(&__end_timeval, NULL);
  247.         long __end_time = __end_timeval.tv_usec;
  248.         long __time_read = __end_time - __start_time;
  249.         printf("start time = %ld, end time = %ld,server json impl time read = %ld\n",__start_time,__end_time,__time_read);
  250. #endif //__DEBUG
  251.         lock_.release_lock();
  252. #ifdef __LINUX
  253.         usleep(max_sleep_time_);
  254. #endif // __LINUX
  255.     }
  256. }
nmon监控部分结果:
    sys summ
    byte stream
    技术分享
    protobuf
    技术分享
    json
    技术分享

    cpu summ
 
    byte stream
    技术分享
    protobuf
    技术分享
    json
    技术分享

    memory
    byte stream
    技术分享
    protobuf
    技术分享
    json
    技术分享

    net
    byte stream
    技术分享
    protobuf
    技术分享
    json
    技术分享

    netpackets
    byte stream
    技术分享
    protobuf
    技术分享
    json
    技术分享
    我们主要通过cpu,内存,网络流量,网络发包数量来分析:
    (1)    就cpu消耗来说,使用byte stream所消耗的cpu要比使用protobuf,json要小的多,主要是因为后者需要额外的创建对象,序列化,还原的开销;
    (2)    内存变化都不太大,基本保持稳定;
    (3)    就网络流量来说,byte stream使用流量比较小,也比较恒定,平均流量在5M左右,而protobuf,json方式平均值都在20M左右,而且前者每秒处理的数据包约为30000个,而后两者在150000~200000之间。这看起来比较奇怪,客户端都是每秒发送10个包,阻塞,服务器读写线程sleep的时间都是500ms,那么正常来说,如果按服务器的处理来看,每秒处理约2个数据包,客户端时间间隔再小,也是如此,这还是都能完全处理的情况下,那么,byte stream的结果应该更加接近事实。但是后两者与前者为何相差这么大呢?
    带着疑问,进行了下面的测试,服务器相关代码只是更改了读线程部分,所以我在线程的每次tick前后加上时间戳,记录一次tick所花费的时间,并记录输入缓冲区读写的位置,因为连接数比较多,日志太多,而且刷屏太快,容易影响测试结果,仅仅采样fd为1000的倍数,下面是结果的截图:
    byte stream
    技术分享
    protobuf
    技术分享
    json
    技术分享
    由上述结果可知:byte stream每次tick时间为10+毫秒,而且输入缓冲区的r/w pos基本是一致的,也就是说数据每次都处理完毕;而protobuf每次tick时间为100+毫秒,r/w pos的位置差在600~700bytes左右; 使用json方式,每次tick时间多达数百毫秒,且r/w pos的位置差在2000~3000bytes之间。从这组数据基本可以确定,在传输效率上来说:byte stream > protobuf > json,其差距差不多在几倍到一个数量级之间。
    为了进一步验证,又做了一组测试,对一个模拟的数据包分别按三种方式进行打包,解包操作,执行10000次,查看执行的总时间:

点击(此处)折叠或打开

  1. /****************************************************************************
  2.  Copyright (c) 2013-2014 King Lee
  3.  Permission is hereby granted, free of charge, to any person obtaining a copy
  4.  of this software and associated documentation files (the "Software"), to deal
  5.  in the Software without restriction, including without limitation the rights
  6.  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7.  copies of the Software, and to permit persons to whom the Software is
  8.  furnished to do so, subject to the following conditions:
  9.  The above copyright notice and this permission notice shall be included in
  10.  all copies or substantial portions of the Software.
  11.  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  12.  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  13.  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  14.  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  15.  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  16.  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  17.  THE SOFTWARE.
  18.    general:
  19.     $export LD_LIBRARY_PATH=$LD_LIBRARY_PATH../easy/dep/protobuf/src/.libs:../easy/dep/jansson/src/.libs
  20.         $../easy/dep/protobuf/src/.libs/protoc -I./ --cpp_out=. transfer.proto
  21.     
  22.     compiler:
  23.         $g++ -g -o byte_stream_vs_protobuf_vs_json transfer.pb.h transfer.pb.cc byte_stream_vs_protobuf_vs_json.cc -I../easy/dep/protobuf/src/ -I../easy/dep/jansson/src/ -L../easy/dep/protobuf/src/.libs -L../easy/dep/jansson/src/.libs -lprotobuf -ljansson
  24.  ****************************************************************************/
  25. #include <stdlib.h>            //    exit
  26. #include <stdio.h>
  27. #include <stdarg.h>
  28. #include <string>
  29. #include <sys/time.h>
  30. #include <string.h>
  31. #include "transfer.pb.h"
  32. #include <jansson.h>
  33. static std::string __random_string[] = 
  34. {
  35.     "[0x000085e4][T]AdvertisingIndentitifer: ‘‘, IdentifierForVendor: ‘‘, DeviceName: ‘PC‘, ModelName: ‘x86‘, SystemName: ‘‘, SystemVersion: ‘‘, HardwareID: ‘74d435046509‘",
  36.     "nice to meet you!",
  37.     "It is the tears of the earth that keep here smiles in bloom.",
  38.     "The mighty desert is burning for the love of a blade of grass who shakes her head and laughs and flies away.",
  39.     "If you shed tears when you miss the sun, you also miss the stars.",
  40.     "Her wishful face haunts my dreams like the rain at night.",
  41.     "Once we dreamt that we were strangers.We wake up to find that we were dear to each other.",
  42.     "Sorrow is hushed into peace in my heart like the evening among the silent trees.",
  43.     "Some unseen fingers, like an idle breeze, are playing upon my heart the music of the ripples.",
  44.     "Listen, my heart, to the whispers of the world with which it makes love to you.",
  45.     "Do not seat your love upon a precipice because it is high.",
  46.     "I sit at my window this morning where the world like a passer-by stops for a moment, nods to me and goes.",
  47.     "There little thoughts are the rustle of leaves; they have their whisper of joy in my mind.",
  48.     "What you are you do not see, what you see is your shadow.",
  49.     "My wishes are fools, they shout across thy song, my Master.Let me but listen.",
  50.     "They throw their shadows before them who carry their lantern on their back.",
  51.     "That I exist is a perpetual surprise which is life.",
  52.     "We, the rustling leaves, have a voice that answers the storms,but who are you so silent?I am a mere flower.",
  53.     "Do not blame your food because you have no appetite.",
  54.     "Success is not final, failure is not fatal: it is the courage to continue that counts.",
  55.     "I cannot tell why this heart languishes in silence.It is for small needs it never asks, or knows or remembers.",
  56.     "The bird wishes it were a cloud.The cloud wishes it were a bird."
  57. };
  58. static const int __loop_count = 10000;
  59. static int __buf_size = 256;
  60. void byte_stream_test()
  61. {
  62.     int __guid = 15;
  63.     int __head = 567;
  64.     int __length2 = 0;
  65.     int __head2 = 0;
  66.     int __guid2 = 0;
  67.     char __buf[__buf_size];
  68.     int __packet_head_size = 12;
  69.     int __random_index = 0;
  70.     unsigned char __packet_head[__packet_head_size];
  71.     int __length = __random_string[__random_index].size();
  72.     
  73.     struct timeval __start_timeval;
  74.     gettimeofday(&__start_timeval, NULL);
  75.     long __start_time = __start_timeval.tv_usec;
  76.     for(int __i = 0; __i < __loop_count; ++__i)
  77.     {
  78.         memset(__buf,0,__buf_size);
  79.         memset(__packet_head,0,__packet_head_size);
  80.         //    pack
  81.         memcpy(__buf,(void*)&__length,4);
  82.         memcpy(__buf + 4,(void*)&__head,4);
  83.         memcpy(__buf + 8,(void*)&__guid,4);
  84.         strcpy(__buf + __packet_head_size,__random_string[__random_index].c_str());
  85.         //    unpack
  86.         memcpy(&__length2,(void*)__packet_head,4);
  87.         memcpy(&__head2,(void*)(__packet_head + 4),4);
  88.         memcpy(&__guid2,(void*)(__packet_head + 8),4);
  89.     }
  90.     struct timeval __end_timeval;
  91.     gettimeofday(&__end_timeval, NULL);
  92.     long __end_time = __end_timeval.tv_usec;
  93.     long __time_total = __end_time - __start_time;
  94.     printf("start time = %ld, end time = %ld,byte stream total time = %ld\n",__start_time,__end_time,__time_total);
  95. }
  96. void protobuf_test()
  97. {
  98.     transfer::Packet __packet_protobuf;
  99.     std::string __string_packet;
  100.     int __guid = 15;
  101.     int __head = 567;
  102.     int __length2 = 0;
  103.     int __head2 = 0;
  104.     int __guid2 = 0;
  105.     char __buf[__buf_size];
  106.     int __packet_head_size = 4;
  107.     int __random_index = 0;
  108.     unsigned char __packet_head[__packet_head_size];
  109.     
  110.     struct timeval __start_timeval;
  111.     gettimeofday(&__start_timeval, NULL);
  112.     long __start_time = __start_timeval.tv_usec;
  113.     for(int __i = 0; __i < __loop_count; ++__i)
  114.     {
  115.         memset(__buf,0,__buf_size);
  116.         //    pack
  117.         __packet_protobuf.Clear();
  118.         __string_packet.clear();
  119.         __packet_protobuf.set_head(__head);
  120.         __packet_protobuf.set_guid(__guid);
  121.         __packet_protobuf.set_content(__random_string[__random_index]);
  122.         __packet_protobuf.SerializeToString(&__string_packet);
  123.         int __length = __string_packet.length();
  124.         memcpy(__buf,(void*)&__length,4);
  125.         //    unpack
  126.         __packet_protobuf.Clear();
  127.         __packet_protobuf.ParseFromString(__string_packet);
  128.         __head2 = __packet_protobuf.head();
  129.         __guid2 = __packet_protobuf.guid();
  130.     }
  131.     struct timeval __end_timeval;
  132.     gettimeofday(&__end_timeval, NULL);
  133.     long __end_time = __end_timeval.tv_usec;
  134.     long __time_total = __end_time - __start_time;
  135.     printf("start time = %ld, end time = %ld,protobuf total time = %ld\n",__start_time,__end_time,__time_total);    
  136. }
  137. void json_test()
  138. {
  139.     int __guid = 15;
  140.     int __head = 567;
  141.     int __length2 = 0;
  142.     int __head2 = 0;
  143.     int __guid2 = 0;
  144.     char __buf[__buf_size];
  145.     int __random_index = 0;
  146.     
  147.     struct timeval __start_timeval;
  148.     gettimeofday(&__start_timeval, NULL);
  149.     long __start_time = __start_timeval.tv_usec;
  150.     for(int __i = 0; __i < __loop_count; ++__i)
  151.     {
  152.         //    pack
  153.         json_t* __json_msg = json_object();
  154.         json_t* __json_head = json_integer(__head);
  155.         json_t* __json_guid = json_integer(__guid);
  156.         json_t* __json_content = json_string(__random_string[__random_index].c_str());
  157.         json_object_set(__json_msg, "head", __json_head);
  158.         json_object_set(__json_msg, "guid", __json_guid);
  159.         json_object_set(__json_msg, "content", __json_content);
  160.         char* __json_dumps_string = json_dumps(__json_msg,0);
  161.         int __length = strlen(__json_dumps_string);
  162.         json_decref(__json_head);
  163.         json_decref(__json_guid);
  164.         json_decref(__json_content);
  165.         json_decref(__json_msg);
  166.         
  167.         //    unpack
  168.         json_error_t* __json_error = NULL;
  169.         json_t* __json_loads = json_loads(__json_dumps_string,JSON_DECODE_ANY,__json_error);
  170.         json_t* __json_loads_head = json_object_get(__json_loads,"head");
  171.         json_t* __json_loads_guid = json_object_get(__json_loads,"guid");
  172.         __head2 = json_integer_value(__json_loads_head);
  173.         __guid2 = json_integer_value(__json_loads_guid);
  174.         json_decref(__json_loads);
  175.         free(__json_dumps_string);
  176.     }
  177.     struct timeval __end_timeval;
  178.     gettimeofday(&__end_timeval, NULL);
  179.     long __end_time = __end_timeval.tv_usec;
  180.     long __time_total = __end_time - __start_time;
  181.     printf("start time = %ld, end time = %ld,json total time = %ld\n",__start_time,__end_time,__time_total);
  182. }
  183. int main(int __arg_num, char** __args)
  184. {
  185.     byte_stream_test();
  186.     protobuf_test();
  187.     json_test();
  188. }

   技术分享
    其结果与上面的推断基本一致.
    其他疑问:使用protobuf,json的方式,读线程的每个tick时间较长,意味着处理的数据可能更少,但是为什么带宽和处理数据包更多了,且接近于满值。目前想到的可能性是使用byte stream的方式服务器基本能及时处理客户端的请求,大部分时间都在sleep了,因此cpu使用量也比较低。(也额外做过测试,将服务器线程的sleep时间更改为10毫秒,这样带宽,以及数据包数量也与后两者相近了,是不是原因就如此呢?)

三种网络数据传输方式比较(byte stream,protobuf,json)

标签:

原文地址:http://www.cnblogs.com/4Dream/p/4904568.html

(0)
(1)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!