Web Bench 1.5介紹 html
1.用GDB單步走的,走了父進程也走了子進程。父進程負責從管道里讀取數據,每一個子進程通過socket,connect,write,read,負責Get請求寫數據到管道。子進程至關於socket網絡編程裏的client客戶端。web
2.GDB調試時,經過設置set follow-fork-mode [parent][child]分別執行父子進程。編程
3.調試時輸入指令爲:-c 10 -t 5 http://www.baidu.com/數組
4.代碼很少可是牽扯的東西仍是很多的。建議本身跟一邊。服務器
1 /* 2 * Return codes: 3 * 0 - sucess 4 * 1 - benchmark failed (server is not on-line) 5 * 2 - bad param 6 * 3 - internal error, fork failed 7 */ 8 #include "socket.c" 9 #include <unistd.h> 10 #include <sys/param.h> 11 #include <rpc/types.h> //pid_t 12 #include <getopt.h> 13 #include <strings.h> 14 #include <time.h> 15 #include <signal.h> 16 17 /* values */ 18 volatile int timerexpired=0; //volatile 在多進程中,變量是隨時可能發生變化的,與volatile變量有關的運算,不要進行編譯優化,以避免出錯 http://www.cnblogs.com/chio/archive/2007/11/24/970632.html 19 int speed=0; 20 int failed=0; 21 int bytes=0; 22 /* globals */ 23 int http10=1; /* 0 - http/0.9, 1 - http/1.0, 2 - http/1.1 */ 24 /* Allow: GET, HEAD, OPTIONS, TRACE */ 25 #define METHOD_GET 0 26 #define METHOD_HEAD 1 27 #define METHOD_OPTIONS 2 28 #define METHOD_TRACE 3 29 #define PROGRAM_VERSION "1.5" 30 int method=METHOD_GET; 31 int clients=1; 32 int force=0; 33 int force_reload=0; 34 int proxyport=80; 35 char *proxyhost=NULL; 36 int benchtime=30; 37 /* internal */ 38 int mypipe[2]; 39 char host[MAXHOSTNAMELEN]; 40 #define REQUEST_SIZE 2048 41 char request[REQUEST_SIZE]; 42 43 static const struct option long_options[]= 44 { 45 {"force",no_argument,&force,1}, 46 {"reload",no_argument,&force_reload,1}, 47 {"time",required_argument,NULL,'t'}, 48 {"help",no_argument,NULL,'?'}, 49 {"http09",no_argument,NULL,'9'}, 50 {"http10",no_argument,NULL,'1'}, 51 {"http11",no_argument,NULL,'2'}, 52 {"get",no_argument,&method,METHOD_GET}, 53 {"head",no_argument,&method,METHOD_HEAD}, 54 {"options",no_argument,&method,METHOD_OPTIONS}, 55 {"trace",no_argument,&method,METHOD_TRACE}, 56 {"version",no_argument,NULL,'V'}, 57 {"proxy",required_argument,NULL,'p'}, 58 {"clients",required_argument,NULL,'c'}, 59 {NULL,0,NULL,0} 60 }; 61 62 /* prototypes */ 63 static void benchcore(const char* host,const int port, const char *request); 64 static int bench(void); 65 static void build_request(const char *url); 66 67 static void alarm_handler(int signal) 68 { 69 timerexpired=1; 70 } 71 72 static void usage(void) 73 { 74 fprintf(stderr, 75 "webbench [option]... URL\n" 76 " -f|--force Don't wait for reply from server.\n" 77 " -r|--reload Send reload request - Pragma: no-cache.\n" 78 " -t|--time <sec> Run benchmark for <sec> seconds. Default 30.\n" 79 " -p|--proxy <server:port> Use proxy server for request.\n" 80 " -c|--clients <n> Run <n> HTTP clients at once. Default one.\n" 81 " -9|--http09 Use HTTP/0.9 style requests.\n" 82 " -1|--http10 Use HTTP/1.0 protocol.\n" 83 " -2|--http11 Use HTTP/1.1 protocol.\n" 84 " --get Use GET request method.\n" 85 " --head Use HEAD request method.\n" 86 " --options Use OPTIONS request method.\n" 87 " --trace Use TRACE request method.\n" 88 " -?|-h|--help This information.\n" 89 " -V|--version Display program version.\n" 90 ); 91 }; 92 int main(int argc, char *argv[]) 93 { 94 int opt=0; 95 int options_index=0; 96 char *tmp=NULL; 97 98 if(argc==1) 99 { 100 usage(); 101 return 2; 102 } 103 104 while((opt=getopt_long(argc,argv,"912Vfrt:p:c:?h",long_options,&options_index))!=EOF ) 105 { 106 switch(opt) 107 { 108 case 0 : break; 109 case 'f': force=1;break; 110 case 'r': force_reload=1;break; 111 case '9': http10=0;break; 112 case '1': http10=1;break; 113 case '2': http10=2;break; 114 case 'V': printf(PROGRAM_VERSION"\n");exit(0); 115 case 't': benchtime=atoi(optarg);break; 116 case 'p': 117 /* proxy server parsing server:port */ 118 tmp=strrchr(optarg,':'); 119 proxyhost=optarg; 120 if(tmp==NULL) 121 { 122 break; 123 } 124 if(tmp==optarg) 125 { 126 fprintf(stderr,"Error in option --proxy %s: Missing hostname.\n",optarg); 127 return 2; 128 } 129 if(tmp==optarg+strlen(optarg)-1) 130 { 131 fprintf(stderr,"Error in option --proxy %s Port number is missing.\n",optarg); 132 return 2; 133 } 134 *tmp='\0'; 135 proxyport=atoi(tmp+1);break; 136 case ':': 137 case 'h': 138 case '?': usage();return 2;break; 139 case 'c': clients=atoi(optarg);break; 140 } 141 } 142 143 if(optind==argc) { 144 fprintf(stderr,"webbench: Missing URL!\n"); 145 usage(); 146 return 2; 147 } 148 149 if(clients==0) clients=1; 150 if(benchtime==0) benchtime=60; 151 /* Copyright */ 152 fprintf(stderr,"Webbench - Simple Web Benchmark "PROGRAM_VERSION"\n" 153 "Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.\n" 154 ); 155 build_request(argv[optind]);//傳送url //結果:request = "GET // HTTP/1.0 HTTP/1.0 HTTP/1.0\r\nUser-Agent: WebBench 1.5\r\nHost: www.baidu.com\r\n\r\n" 156 /* print bench info */ 157 printf("\nBenchmarking: "); 158 switch(method) 159 { 160 case METHOD_GET: 161 default: 162 printf("GET");break; 163 case METHOD_OPTIONS: 164 printf("OPTIONS");break; 165 case METHOD_HEAD: 166 printf("HEAD");break; 167 case METHOD_TRACE: 168 printf("TRACE");break; 169 } 170 printf(" %s",argv[optind]); //argv[optind] 爲 "http://www.baidu.com/" 171 switch(http10) //http10==1不執行 172 { 173 case 0: printf(" (using HTTP/0.9)");break; 174 case 2: printf(" (using HTTP/1.1)");break; 175 } 176 printf("\n"); 177 if(clients==1) printf("1 client"); 178 else 179 printf("%d clients",clients); // 180 181 printf(", running %d sec", benchtime); // 182 if(force) printf(", early socket close"); 183 if(proxyhost!=NULL) printf(", via proxy server %s:%d",proxyhost,proxyport); 184 if(force_reload) printf(", forcing reload"); 185 printf(".\n"); 186 return bench(); 187 } 188 189 void build_request(const char *url) 190 { 191 char tmp[10]; 192 int i; 193 194 bzero(host,MAXHOSTNAMELEN); //函數原型 void bzero(void *s,int n)將s所指的區域前n個字節置爲零 195 bzero(request,REQUEST_SIZE); 196 197 if(force_reload && proxyhost!=NULL && http10<1) http10=1; 198 if(method==METHOD_HEAD && http10<1) http10=1; 199 if(method==METHOD_OPTIONS && http10<2) http10=2; 200 if(method==METHOD_TRACE && http10<2) http10=2; 201 202 switch(method) 203 { 204 default: 205 case METHOD_GET: strcpy(request,"GET");break; 206 case METHOD_HEAD: strcpy(request,"HEAD");break; 207 case METHOD_OPTIONS: strcpy(request,"OPTIONS");break; 208 case METHOD_TRACE: strcpy(request,"TRACE");break; 209 } 210 211 strcat(request," "); 212 213 if(NULL==strstr(url,"://"))//函數原型:extern char *strstr(char *str1,char *str2)返回str2(不包括\0)在str1中的位置,沒有則返回NULL 214 { 215 fprintf(stderr, "\n%s: is not a valid URL.\n",url); 216 exit(2); 217 } 218 if(strlen(url)>1500) 219 { 220 fprintf(stderr,"URL is too long.\n"); 221 exit(2); 222 } 223 if(proxyhost==NULL) 224 if (0!=strncasecmp("http://",url,7)) 225 { 226 fprintf(stderr,"\nOnly HTTP protocol is directly supported, set --proxy for others.\n"); 227 exit(2); 228 } 229 /* protocol/host delimiter分隔符 */ 230 i=strstr(url,"://")-url+3;//去掉http://得www.aa.com/ 231 /* printf("%d\n",i); */ 232 233 if(strchr(url+i,'/')==NULL) //查找某字符在字符串首次出現的位置 234 { 235 fprintf(stderr,"\nInvalid URL syntax - hostname don't ends with '/'.\n"); 236 exit(2); 237 } 238 if(proxyhost==NULL) 239 { 240 /* get port from hostname */ 241 if(index(url+i,':')!=NULL && 242 index(url+i,':')<index(url+i,'/')) //函數原型char *index(const char*s,char c)返回c在s中的索引 243 { 244 strncpy(host,url+i,strchr(url+i,':')-url-i); 245 bzero(tmp,10); 246 strncpy(tmp,index(url+i,':')+1,strchr(url+i,'/')-index(url+i,':')-1); 247 /* printf("tmp=%s\n",tmp); */ 248 proxyport=atoi(tmp); 249 if(proxyport==0) proxyport=80; 250 } 251 else 252 {// 函數原型char * strncpy(char *dest,const char *src,size_t n);複製目標字符串src的前n個字符到dest //函數原型int strcspn(char *str,char *accept);返回字符串str開頭連續不含字符串accept內的字符數目 如 www.baidu.com/ 返回13*/ 253 strncpy(host,url+i,strcspn(url+i,"/")); 254 } 255 // printf("Host=%s\n",host); 256 strcat(request+strlen(request),url+i+strcspn(url+i,"/")); //request=="GET //" 257 } 258 else 259 { 260 // printf("ProxyHost=%s\nProxyPort=%d\n",proxyhost,proxyport); 261 strcat(request,url); 262 } 263 if(http10==1) 264 strcat(request," HTTP/1.0"); //"GET // HTTP/1.0 HTTP/1.0 HTTP/1.0" 265 else if (http10==2) 266 strcat(request," HTTP/1.1"); 267 strcat(request,"\r\n"); 268 if(http10>0) 269 strcat(request,"User-Agent: WebBench "PROGRAM_VERSION"\r\n");//"GET // HTTP/1.0 HTTP/1.0 HTTP/1.0\r\nUser-Agent: WebBench 1.5\r\n" 270 if(proxyhost==NULL && http10>0) 271 { 272 strcat(request,"Host: "); 273 strcat(request,host); 274 strcat(request,"\r\n"); //request = "GET // HTTP/1.0 HTTP/1.0 HTTP/1.0\r\nUser-Agent: WebBench 1.5\r\nHost: www.baidu.com\r\n" 275 } 276 if(force_reload && proxyhost!=NULL) 277 { 278 strcat(request,"Pragma: no-cache\r\n"); 279 } 280 if(http10>1) 281 strcat(request,"Connection: close\r\n"); 282 /* add empty line at end */ 283 if(http10>0) strcat(request,"\r\n"); 284 // printf("Req=%s\n",request); 285 } 286 287 /* vraci system rc error kod */ 288 static int bench(void) 289 { 290 int i,j,k; 291 pid_t pid=0; 292 FILE *f; 293 294 /* check avaibility of target server */ 295 i=Socket(proxyhost==NULL?host:proxyhost,proxyport); //i==3 296 if(i<0) { 297 fprintf(stderr,"\nConnect to server failed. Aborting benchmark.\n"); 298 return 1; 299 } 300 close(i); 301 /* create pipe */ 302 if(pipe(mypipe)) //建立管道 返回值==0 filedes[0]爲管道里的讀取端filedes[1]則爲管道的寫入端 303 { 304 perror("pipe failed."); 305 return 3; 306 } 307 308 /* not needed, since we have alarm() in childrens */ 309 /* wait 4 next system clock tick */ 310 /* 311 cas=time(NULL); 312 while(time(NULL)==cas) 313 sched_yield(); 314 */ 315 316 /* fork childs */ 317 for(i=0;i<clients;i++) 318 { //進入gdb時經過設置 set follow-fork-mode [parent][child]來選擇調試父進程或子進程 319 pid=fork(); //fork()函數產生子進程分別執行下面的代碼.父進程返回子進程pid>0,子進程返回0,失敗返回-1 320 if(pid <= (pid_t) 0) //調試子進程時pid==0,進入if.若調試父進程不進入if 321 { 322 /* child process or error*/ 323 sleep(1); /* make childs faster */ 324 break; 325 } 326 } 327 328 if( pid< (pid_t) 0) 329 { 330 fprintf(stderr,"problems forking worker no. %d\n",i); 331 perror("fork failed."); 332 return 3; 333 } 334 335 if(pid== (pid_t) 0) 336 { 337 /* I am a child */ 338 if(proxyhost==NULL) 339 benchcore(host,proxyport,request); 340 else 341 benchcore(proxyhost,proxyport,request); 342 343 /* write results to pipe */ 344 f=fdopen(mypipe[1],"w"); 345 if(f==NULL) 346 { 347 perror("open pipe for writing failed."); 348 return 3; 349 } 350 /* fprintf(stderr,"Child - %d %d\n",speed,failed); */ 351 fprintf(f,"%d %d %d\n",speed,failed,bytes); 352 fclose(f); 353 return 0; 354 } 355 else 356 { 357 f=fdopen(mypipe[0],"r"); //FILE *fdopen(int filedes,const char *type)用於在一個已經打開的文件描述符(參數1)上按哪一種方式(參數2)打開一個流 358 if(f==NULL) 359 { 360 perror("open pipe for reading failed."); 361 return 3; 362 } 363 setvbuf(f,NULL,_IONBF,0); //爲流指定緩衝區。參數1,指定流指針。參數2,爲NULL函數自動分配緩衝區。_IONBF表示不使用緩衝區,真是醉了,當即寫每次I/O操做 364 speed=0; 365 failed=0; 366 bytes=0; 367 //{i,j,k,speed,failed,bytes,pid} = {10, -1073745752, -1208015616, 0, 0, 0, 10752} 368 while(1) 369 { 370 pid=fscanf(f,"%d %d %d",&i,&j,&k); //從流中讀取格式化數據。返回成功添加到參數列表元素的數目 pid:7328變爲3 371 if(pid<2) 372 { 373 fprintf(stderr,"Some of our childrens died.\n"); 374 break; 375 } 376 speed+=i; 377 failed+=j; 378 bytes+=k; //{speed, failed,bytes} = {41, 0, 4077115} 379 /* fprintf(stderr,"*Knock* %d %d read=%d\n",speed,failed,pid); */ 380 if(--clients==0) break; 381 } 382 fclose(f); 383 //{i,j,k,speed,failed,bytes,pid} = {2, 0, 6041, 141, 0, 1871545, 3} 384 printf("\nSpeed=%d pages/min, %d bytes/sec.\nRequests: %d susceed, %d failed.\n", 385 (int)((speed+failed)/(benchtime/60.0f)), 386 (int)(bytes/(float)benchtime), 387 speed, 388 failed); 389 } 390 return i; 391 } 392 393 void benchcore(const char *host,const int port,const char *req) 394 { 395 int rlen; 396 char buf[1500]; 397 int s,i; 398 struct sigaction sa; //信號產生時觸發某些動做咱們就能夠安裝信號。sigaction安裝時有3個參數第一個參數是信號的值,第二個是sigaction結構這個結構說明了信號發生時調用的函數和其它的一些信息:sa_handler =<alarm_handler>,sa_mask = 3085037896...sa_flags = 0, sa_restorer = 0x0} 399 400 /* setup alarm signal handler */ 401 sa.sa_handler=alarm_handler; //alarm_handler警報處理過程 402 sa.sa_flags=0; 403 if(sigaction(SIGALRM,&sa,NULL)) 404 exit(3); 405 alarm(benchtime);//系統中的每一個進程都有一個私有的鬧鐘。這個鬧鐘很像一個計時器,能夠設置在必定秒數後鬧鐘。時間一到,時鐘就發送一個信號SIGALRM到進程。 406 407 rlen=strlen(req); 408 nexttry:while(1) 409 { 410 if(timerexpired) //定時1s超時退出,gdb調試的時候注意不要超時 411 { 412 if(failed>0) 413 { 414 /* fprintf(stderr,"Correcting failed by signal\n"); */ 415 failed--; 416 } 417 return; 418 } 419 s=Socket(host,port); 420 if(s<0) { failed++;continue;} 421 if(rlen!=write(s,req,rlen)) {failed++;close(s);continue;} //rlen == 65 write() 422 if(http10==0) 423 if(shutdown(s,1)) { failed++;close(s);continue;} 424 if(force==0) 425 { 426 /* read all available data from socket */ 427 while(1) 428 { 429 if(timerexpired) break; //gdb:set var timerexpired=0,防止超時退出 430 i=read(s,buf,1500); //從緩衝區讀取數據read() 431 /* fprintf(stderr,"%d\n",i); */ 432 if(i<0) 433 { 434 failed++; 435 close(s); 436 goto nexttry; 437 } 438 else 439 if(i==0) break; 440 else 441 bytes+=i; 442 } 443 } 444 if(close(s)) {failed++;continue;} 445 speed++; 446 } 447 }
1 /* $Id: socket.c 1.1 1995/01/01 07:11:14 cthuang Exp $ 2 * 3 * This module has been modified by Radim Kolar for OS/2 emx 4 */ 5 6 /*********************************************************************** 7 module: socket.c 8 program: popclient 9 SCCS ID: @(#)socket.c 1.5 4/1/94 10 programmer: Virginia Tech Computing Center 11 compiler: DEC RISC C compiler (Ultrix 4.1) 12 environment: DEC Ultrix 4.3 13 description: UNIX sockets code. 14 ***********************************************************************/ 15 16 #include <sys/types.h> 17 #include <sys/socket.h> 18 #include <fcntl.h> 19 #include <netinet/in.h> 20 #include <arpa/inet.h> 21 #include <netdb.h> 22 #include <sys/time.h> 23 #include <string.h> 24 #include <unistd.h> 25 #include <stdio.h> 26 #include <stdlib.h> 27 #include <stdarg.h> 28 29 int Socket(const char *host, int clientPort) //host==www.baidu.com port==80 30 { 31 int sock; 32 unsigned long inaddr; 33 struct sockaddr_in ad; 34 struct hostent *hp; 35 36 //{ad} = {{sin_family = 2, sin_port = 0, sin_addr = {s_addr = 0}, 37 38 memset(&ad, 0, sizeof(ad)); 39 ad.sin_family = AF_INET; 40 41 inaddr = inet_addr(host); 42 if (inaddr != INADDR_NONE) 43 memcpy(&ad.sin_addr, &inaddr, sizeof(inaddr)); 44 else 45 { 46 hp = gethostbyname(host); 47 if (hp == NULL) 48 return -1; 49 memcpy(&ad.sin_addr, hp->h_addr, hp->h_length); 50 } 51 ad.sin_port = htons(clientPort); 52 //ad = {sin_family = 2, sin_port = 20480, sin_addr = {s_addr = 2471192266} 53 sock = socket(AF_INET, SOCK_STREAM, 0); //sock==3n 54 if (sock < 0) 55 return sock; 56 if (connect(sock, (struct sockaddr *)&ad, sizeof(ad)) < 0) 57 return -1; 58 return sock; 59 } 60 /*---------------------------------------------------------------------- 61 請求鏈接的客戶端套接字(client端): 62 1.調用socket函數建立套接字:int socket(int domain, int type, int protocol) //成功返回文件描述符,失敗返回-1 63 2.調用connect函數向服務器端發送鏈接請求:int connect(int sockfd, struct sockaddr* serveraddr, socklen_t addrlen) //鏈接時serveraddr 64 3.read()/write():數據交換 65 4.close函數關閉鏈接 66 ----------------------------------------------------------------------*/
include<getopt.h>//功能:主要用來處理接受的命令行參數argc,argv[]。函數主要包括getopt()和getopt_long(),不用本身處理用戶輸入的命令行參數了。網絡
[指令]webbench -c 10 -t 20 http://www.baidu.com/ 其中argc==6從1開始計數,argv[0-5]分別==webbench -c 10 -t 20 http://...從0開始計數數據結構
getopt()函數原型:int getopt(int argc,char *const argv[],const char *optstring);dom
全局變量 extern char *optarg; extern int optind, opterr, optopt; optarg選項的參數指針,optind這個索引指向argv裏當前分析的字符串的下一個索引。socket
getopt()處理以-開頭的命令行參數,如optstring="912Vfrt:p:c:?h"表示-9 -V -f -p -c -tide
調用該函數一次返回一個選項,直到結束返回-1。
getopt_long()函數原型:int getopt_long(int argc,char *const argv[],const char *optstring,const struct option *longopts,
webbench中爲:
while((opt=getopt_long(argc,argv,"912Vfrt:p:c:?h",long_options,&options_index))!=EOF )//:後表明須要參數
struct option 類型數組
該數據結構中的每一個元素對應了一個長選項,而且每一個元素是由四個域組成。一般狀況下,能夠按如下規則使用。第一個元素,描述長選項的名稱;第二個選項,表明 該選項是否須要跟着參數,須要參數則爲1,反之爲0。no_argument 0 選項沒有參數 required_argument 1 選項須要參數 optional_argument 2 選項參數是可選的;第三個選項,能夠賦爲NULL;第四個選項,是該長選項對應的短選項名稱。另外,數據結構的最後一個元素,要求全部域的內容均爲0,即{NULL,0,NULL,0}。
longopts是一個struct結構體實例,webbench中爲:
static const struct option long_options[]={
默認的測試時間:30s,主要是給子進程的鬧鐘,時間到了timerexpired會置1,從內存讀的。
父進程與子進程通訊經過管道進行通訊:
1. mypipe[0] : 是讀的管道端口; 2. mypipe[1] : 是寫的管道端口;
alarm_handler函數功能: 1. 鬧鐘信號處理函數,當時間到了的時候,timerexpired被置1,表示時間到了; 2. benchcore()中會根據timerexpired值來判斷子進程的運行;
建立發送給http服務器的請求頭
612 * Socket函數完成的工做: 613 * 1. 轉換IP,域名,填充struct sockaddr_in,獲取對應的socket描述符; 614 * 2. 鏈接服務器;
578 * bench函數完成如下功能: 579 * 1. 試探性的嘗試一次是否可以正常鏈接服務器,若是鏈接失敗,也就不必繼續後續處理了; 580 * 2. 建立管道,用於父子進程通訊; 581 * 3. 建立clients對應數量的子進程; 582 * 4. 子進程: 583 * 1. 對服務器進行benchtime秒的鏈接訪問,獲取對應的failed,speed,bytes值; 584 * 2. 當時間到了benchtime秒之後,打開寫管道; 585 * 3. 將子進程本身測試獲得的failed,speed,bytes值發送給父進程; 586 * 4. 關閉寫管道文件描述符; 587 * 5. 父進程: 588 * 1. 打開讀管道; 589 * 2. 設置管道一些參數,初始化父進程的failed,speed,bytes變量; 590 * 3. while循環不斷去獲取子進程傳輸過來的數據,直到子進程所有退出; 591 * 4. 關閉讀管道; 592 * 5. 對數據進行處理,並打印輸出; 593 */
740 * benchcore函數完成功能: 741 * 1. 註冊鬧鐘處理函數,設置鬧鐘時間,具體時間由benchtime給出,默認是30s; 742 * 2. while循環裏判斷鬧鐘時間是否到了,若是到了退出循環; 743 * 3. while循環裏鏈接服務器; 744 * 4. while循環裏發送http請求頭給服務器; 745 * 5. while循環裏判斷是否須要接受服務器數據; 746 * 6. 關閉與服務器的鏈接; 747 * 7. 在整個過程當中,如下變量會統計在benchtime給出的時間內的一些信息: 748 * 1. failed : 鏈接服務器失敗和傳輸數據過程當中失敗的鏈接數; 749 * 2. speed : 正常鏈接服務器,而且正常傳輸數據的鏈接數 750 * 3. bytes : 從服務器獲取到的字節數; 751 */