[关闭]
@RunZhi 2016-09-13T16:14:14.000000Z 字数 28364 阅读 1269

webbench,tinyhttpd源码剖析与性能测试

操作系统实验报告

实验目的:

1.编译并运行WebBench,测试baidu服务器的性能

2.熟读并理解WebBench源码,将每一行程序的理解注释加入该任务四的实验报告中,要求注释偏口语化.

3.编译并运行TinyHttpd,并用浏览器访问该小型web服务器

4.熟读并理解TinyHttpd的源码,将每一行程序的理解注释加入该任务四的实验报告中,要求注释偏口语化

5.
将自己编译的WebBench程序去测试自己编译的TinyHttpd程序,看看会发生什么“现象”?
请分析:
A) 为什么会发生这个“现象”?
B) 是谁的问题导致该现象的发生?
C) 请修复该问题.

实验过程:

Webbench:

下载webbench源代码并且运行程序:

  1. sudo make
  2. sudo make install
  3. ./webbench -c 100 -t 30 http://www.baidu.com/

然后终端输出如下:
实验截图1

源代码的解析附在后面.

Tinyhttpd

下载tinyHttpd源代码,但此时它们是无法通过编译的,需要进行一定的修改,具体修改处详见:http://blog.csdn.net/cqu20093154/article/details/41025885.

完成编译后,运行程序:

  1. ./httpd

终端显示:
实验截图2

打开浏览器,在地址栏输入localhost:42557,浏览器将显示:
实验截图3

用Webbench测试本机运行的tinyhttpd的性能

运行tinyhttpd,然后用webbench进行测试,发现如下现象
实验截图4

所有的request都是fail的!再看看httpd的进程,早已经不见了.

上网查找资料后,得知问题出现在tinyhttpd的httpd.c的一处源代码:

  1. ...
  2. void unimplemented(int client)
  3. {
  4. char buf[1024];
  5. // HTTP method 不被支持
  6. sprintf(buf, "HTTP/1.0 501 Method Not Implemented\r\n");
  7. send(client, buf, strlen(buf), 0);
  8. // 输出服务器信息到网页上
  9. sprintf(buf, SERVER_STRING);
  10. send(client, buf, strlen(buf), 0);
  11. sprintf(buf, "Content-Type: text/html\r\n");
  12. send(client, buf, strlen(buf), 0);
  13. sprintf(buf, "\r\n");
  14. send(client, buf, strlen(buf), 0);
  15. sprintf(buf, "<HTML><HEAD><TITLE>Method Not Implemented\r\n");
  16. send(client, buf, strlen(buf), 0);
  17. sprintf(buf, "</TITLE></HEAD>\r\n");
  18. send(client, buf, strlen(buf), 0);
  19. sprintf(buf, "<BODY><P>HTTP request method not supported.\r\n");
  20. send(client, buf, strlen(buf), 0);
  21. sprintf(buf, "</BODY></HTML>\r\n");
  22. send(client, buf, strlen(buf), 0);
  23. }
  24. ...

因为webbench在访问一次之后就断掉了,tinyhttpd是否好几次把信息发送过去,由于webbench连接成功并且收到一次消息后便断了连接(具体看webbench.c的如下代码片段)

  1. static int bench(void)
  2. {
  3. ....
  4. /* check avaibility of target server */
  5. // host/proxyhost为服务器端ip, proxyport为服务器端口号建立socket连接
  6. i=Socket(proxyhost==NULL?host:proxyhost, proxyport);
  7. if(i<0) {
  8. // 出现错误
  9. fprintf(stderr, "\nConnect to server failed. Aborting benchmark.\n");
  10. return 1;
  11. }
  12. //对套接字i的引用计数-1,当引用计数为0的时候,关闭套接字
  13. close(i);
  14. ....

而webbench给tinyhttpd发送的是无method的请求,因此tinyhttpd将会执行unimplemented函数:

  1. void unimplemented(int client)
  2. {
  3. char buf[1024];
  4. // HTTP method 不被支持
  5. sprintf(buf, "HTTP/1.0 501 Method Not Implemented\r\n");
  6. send(client, buf, strlen(buf), 0);
  7. // 输出服务器信息到网页上
  8. sprintf(buf, SERVER_STRING);
  9. send(client, buf, strlen(buf), 0);
  10. sprintf(buf, "Content-Type: text/html\r\n");
  11. send(client, buf, strlen(buf), 0);
  12. sprintf(buf, "\r\n");
  13. send(client, buf, strlen(buf), 0);
  14. sprintf(buf, "<HTML><HEAD><TITLE>Method Not Implemented\r\n");
  15. send(client, buf, strlen(buf), 0);
  16. sprintf(buf, "</TITLE></HEAD>\r\n");
  17. send(client, buf, strlen(buf), 0);
  18. sprintf(buf, "<BODY><P>HTTP request method not supported.\r\n");
  19. send(client, buf, strlen(buf), 0);
  20. sprintf(buf, "</BODY></HTML>\r\n");
  21. send(client, buf, strlen(buf), 0);
  22. }

由于webbench是收到了一次消息便断了连接,因此在tinyhttpd在执行完上面函数的第11行的时候(send函数),就会向系统发送异常消息,而程序没有对消息进行处理,因此程序会dump掉。

要改的方法很多,其中一种方法就是,把要发送的消息一次性发送完.把那一大段sprintf send的直接改成:

  1. strcat(buf, "HTTP/1.0 501 Method Not Implemented\r\n");
  2. strcat(buf, SERVER_STRING);
  3. strcat(buf, "Content-Type: text/html\r\n\r\n<HTML><HEAD><TITLE>Method Not Implemented\r\n</TITLE></HEAD>\r\n<BODY><P>HTTP request method not supported.\r\n</BODY></HTML>\r\n");
  4. printf("%s\n",buf);
  5. send(client, buf, strlen(buf), 0);

也可以让send不发异常消息给系统,做法是:把send函数的最后一个参数设为:MSG_NOSIGNAL.

然后重新开始测试,即可.测试结果如下:
实验截图5

(出现了Cannot allocate memory错误,但个人认为这不是错误而是电脑硬件条件不够好的缘故)

源代码解析

Webbench

  1. //webbench.c
  2. /*
  3. * (C) Radim Kolar 1997-2004
  4. * This is free software, see GNU Public License version 2 for
  5. * details.
  6. *
  7. * Simple forking WWW Server benchmark:
  8. *
  9. * Usage:
  10. * webbench --help
  11. *
  12. * Return codes:
  13. * 0 - sucess
  14. * 1 - benchmark failed (server is not on-line)
  15. * 2 - bad param
  16. * 3 - internal error, fork failed
  17. *
  18. */
  19. #include "socket.c"
  20. #include <unistd.h>
  21. #include <sys/param.h>
  22. #include <rpc/types.h>
  23. #include <getopt.h>
  24. #include <strings.h>
  25. #include <time.h>
  26. #include <signal.h>
  27. /* values */
  28. volatile int timerexpired = 0; //判断压力测试是否已经达到预定时间
  29. int speed = 0; //记录成功得到服务器响应的进程数量
  30. int failed = 0; //记录失败的进程数量
  31. int bytes = 0; //记录进程成功读取的字节数
  32. /* globals */
  33. int http10 = 1; /* http版本,0 - http/0.9, 1 - http/1.0, 2 - http/1.1 */
  34. /* Allow: GET, HEAD, OPTIONS, TRACE */
  35. #define METHOD_GET 0
  36. #define METHOD_HEAD 1
  37. #define METHOD_OPTIONS 2
  38. #define METHOD_TRACE 3
  39. #define PROGRAM_VERSION "1.5"
  40. int method = METHOD_GET; //请求方式,默认为get方式
  41. int clients = 1; //并发数量,默认为1
  42. int force = 0; //0表示等待读取从服务器返回的数据,1表示不等待
  43. int force_reload = 0; //0表示缓存,1表示不缓存
  44. int proxyport = 80; //代理服务器的端口号
  45. char *proxyhost = NULL; //代理服务器的ip
  46. int benchtime = 30; //压力测试时间,默认为30秒
  47. /* internal */
  48. int mypipe[2]; //使用管道进行父进程与子进程的通信
  49. char host[MAXHOSTNAMELEN]; //服务器端的ip
  50. #define REQUEST_SIZE 2048
  51. char request[REQUEST_SIZE]; //所要发送的http请求
  52. static const struct option long_options[]=
  53. {
  54. {"force", no_argument, &force, 1},
  55. {"reload", no_argument, &force_reload, 1},
  56. {"time", required_argument, NULL, 't'},
  57. {"help", no_argument, NULL, '?'},
  58. {"http09", no_argument, NULL, '9'},
  59. {"http10", no_argument, NULL, '1'},
  60. {"http11", no_argument, NULL, '2'},
  61. {"get", no_argument, &method, METHOD_GET},
  62. {"head", no_argument, &method, METHOD_HEAD},
  63. {"options", no_argument, &method, METHOD_OPTIONS},
  64. {"trace", no_argument, &method, METHOD_TRACE},
  65. {"version", no_argument, NULL, 'V'},
  66. {"proxy", required_argument, NULL, 'p'},
  67. {"clients", required_argument, NULL, 'c'},
  68. {NULL, 0, NULL, 0}
  69. };
  70. /* prototypes */
  71. static void benchcore(const char* host, const int port, const char *request);
  72. static int bench(void);
  73. static void build_request(const char *url);
  74. static void alarm_handler(int signal)
  75. {
  76. //设置标记,表示已经达到了预定的测试时间
  77. timerexpired=1;
  78. }
  79. static void usage(void)
  80. {
  81. //输出如何使用webbench信息的函数
  82. fprintf(stderr,
  83. "webbench [option]... URL\n"
  84. " -f|--force Don't wait for reply from server.\n"
  85. " -r|--reload Send reload request - Pragma: no-cache.\n"
  86. " -t|--time <sec> Run benchmark for <sec> seconds. Default 30.\n"
  87. " -p|--proxy <server:port> Use proxy server for request.\n"
  88. " -c|--clients <n> Run <n> HTTP clients at once. Default one.\n"
  89. " -9|--http09 Use HTTP/0.9 style requests.\n"
  90. " -1|--http10 Use HTTP/1.0 protocol.\n"
  91. " -2|--http11 Use HTTP/1.1 protocol.\n"
  92. " --get Use GET request method.\n"
  93. " --head Use HEAD request method.\n"
  94. " --options Use OPTIONS request method.\n"
  95. " --trace Use TRACE request method.\n"
  96. " -?|-h|--help This information.\n"
  97. " -V|--version Display program version.\n"
  98. );
  99. };
  100. int main(int argc, char *argv[])
  101. {
  102. int opt=0;
  103. int options_index=0;
  104. char *tmp=NULL;
  105. // 命令行参数不合法,输出使用手册
  106. if(argc==1)
  107. {
  108. usage();
  109. return 2;
  110. }
  111. // 检查参数,使用getopt_long
  112. while((opt=getopt_long(argc, argv, "912Vfrt:p:c:?h", long_options, &options_index))!=EOF )
  113. {
  114. switch(opt)
  115. {
  116. case 0 :
  117. break;
  118. case 'f':
  119. force=1;
  120. break;
  121. case 'r':
  122. force_reload=1;
  123. break;
  124. case '9':
  125. http10=0;
  126. break;
  127. case '1':
  128. http10=1;
  129. break;
  130. case '2':
  131. http10=2;
  132. break;
  133. case 'V':
  134. printf(PROGRAM_VERSION"\n");
  135. exit(0);
  136. case 't':
  137. benchtime=atoi(optarg);
  138. break;
  139. case 'p':
  140. /* proxy server parsing server:port */
  141. tmp=strrchr(optarg, ':');
  142. proxyhost=optarg;
  143. if(tmp==NULL)
  144. {
  145. break;
  146. }
  147. if(tmp==optarg)
  148. {
  149. fprintf(stderr, "Error in option --proxy %s: Missing hostname.\n", optarg);
  150. return 2;
  151. }
  152. if(tmp==optarg+strlen(optarg)-1)
  153. {
  154. fprintf(stderr, "Error in option --proxy %s Port number is missing.\n", optarg);
  155. return 2;
  156. }
  157. *tmp='\0';
  158. proxyport=atoi(tmp+1);break;
  159. case ':':
  160. case 'h':
  161. case '?': usage();return 2;break;
  162. case 'c': clients=atoi(optarg);break;
  163. }
  164. }
  165. // optind为一个不包含选项的命令行参数,这里就是指URL值在参数表中的index
  166. if(optind==argc) {
  167. fprintf(stderr, "webbench: Missing URL!\n");
  168. usage();
  169. return 2;
  170. }
  171. // 设置默认参数
  172. if(clients==0) clients=1;
  173. if(benchtime==0) benchtime=60;
  174. /* Copyright */
  175. // 输出copyright
  176. fprintf(stderr, "Webbench - Simple Web Benchmark "PROGRAM_VERSION"\n"
  177. "Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.\n"
  178. );
  179. build_request(argv[optind]);
  180. /* print bench info */
  181. // 打印测试的信息
  182. printf("\nBenchmarking: ");
  183. switch(method)
  184. {
  185. case METHOD_GET:
  186. default:
  187. printf("GET");
  188. break;
  189. case METHOD_OPTIONS:
  190. printf("OPTIONS");
  191. break;
  192. case METHOD_HEAD:
  193. printf("HEAD");
  194. break;
  195. case METHOD_TRACE:
  196. printf("TRACE");
  197. break;
  198. }
  199. printf(" %s", argv[optind]);
  200. switch(http10)
  201. {
  202. case 0: printf(" (using HTTP/0.9)");break;
  203. case 2: printf(" (using HTTP/1.1)");break;
  204. }
  205. printf("\n");
  206. if(clients==1)
  207. printf("1 client");
  208. else
  209. printf("%d clients", clients);
  210. printf(", running %d sec", benchtime);
  211. if(force)
  212. printf(", early socket close");
  213. if(proxyhost!=NULL)
  214. printf(", via proxy server %s:%d", proxyhost, proxyport);
  215. if(force_reload)
  216. printf(", forcing reload");
  217. printf(".\n");
  218. return bench();
  219. }
  220. void build_request(const char *url)
  221. {
  222. // 对全局变量char request[REQUEST_SIZE]进行操作,根据URL构建http请求
  223. char tmp[10];
  224. int i;
  225. //字节清零
  226. bzero(host, MAXHOSTNAMELEN);
  227. bzero(request, REQUEST_SIZE);
  228. //确定http版本
  229. if(force_reload && proxyhost!=NULL && http10<1) http10=1;
  230. if(method==METHOD_HEAD && http10<1) http10=1;
  231. if(method==METHOD_OPTIONS && http10<2) http10=2;
  232. if(method==METHOD_TRACE && http10<2) http10=2;
  233. //根据请求,设置内容
  234. switch(method)
  235. {
  236. default:
  237. case METHOD_GET:
  238. strcpy(request, "GET");
  239. break;
  240. case METHOD_HEAD:
  241. strcpy(request, "HEAD");
  242. break;
  243. case METHOD_OPTIONS:
  244. strcpy(request, "OPTIONS");
  245. break;
  246. case METHOD_TRACE:
  247. strcpy(request, "TRACE");
  248. break;
  249. }
  250. strcat(request, " ");
  251. // 检测URL内容是否合法
  252. if(NULL==strstr(url, "://"))
  253. {
  254. fprintf(stderr, "\n%s: is not a valid URL.\n", url);
  255. exit(2);
  256. }
  257. // 检测URL长度是否合法
  258. if(strlen(url)>1500)
  259. {
  260. fprintf(stderr, "URL is too long.\n");
  261. exit(2);
  262. }
  263. if(proxyhost==NULL)
  264. if (0!=strncasecmp("http://", url, 7))
  265. {
  266. //不使用代理服务器,只允许使用HTTP协议
  267. fprintf(stderr, "\nOnly HTTP protocol is directly supported, set --proxy for others.\n");
  268. exit(2);
  269. }
  270. /* protocol/host delimiter */
  271. // 获得://后面第一个字符的相对索引号
  272. i=strstr(url, "://")-url+3;
  273. /* printf("%d\n", i); */
  274. if(strchr(url+i, '/')==NULL) {
  275. // 没有其它的'/'符号,不合法
  276. fprintf(stderr, "\nInvalid URL syntax - hostname don't ends with '/'.\n");
  277. exit(2);
  278. }
  279. if(proxyhost==NULL)
  280. {
  281. //不使用代理服务器, 必定是http协议
  282. /* get port from hostname */
  283. if( index(url+i, ':')!=NULL && index(url+i, ':')<index(url+i,'/') )
  284. {
  285. // 如果是 server:port的格式
  286. // 获取主机地址
  287. strncpy(host, url+i, strchr(url+i, ':')-url-i);
  288. bzero(tmp, 10);
  289. strncpy(tmp, index(url+i, ':')+1, strchr(url+i, '/')-index(url+i, ':')-1);
  290. /* printf("tmp=%s\n", tmp); */
  291. // 目标端口
  292. proxyport=atoi(tmp);
  293. if(proxyport==0)
  294. // 默认端口号为80
  295. proxyport=80;
  296. }
  297. else
  298. {
  299. // 获取主机地址
  300. strncpy(host, url+i, strcspn(url+i, "/"));
  301. }
  302. // printf("Host=%s\n", host);
  303. // 获取域名后的目标地址
  304. strcat(request+strlen(request), url+i+strcspn(url+i, "/"));
  305. }
  306. else
  307. {
  308. // printf("ProxyHost=%s\nProxyPort=%d\n", proxyhost, proxyport);
  309. // 使用了代理服务器
  310. strcat(request, url);
  311. }
  312. // 确定http版本号
  313. if(http10==1)
  314. strcat(request, " HTTP/1.0");
  315. else if (http10==2)
  316. strcat(request, " HTTP/1.1");
  317. // 换行
  318. strcat(request, "\r\n");
  319. //添加 User-Agent信息
  320. if(http10>0)
  321. strcat(request, "User-Agent: WebBench "PROGRAM_VERSION"\r\n");
  322. if(proxyhost==NULL && http10>0)
  323. {
  324. // 如果http版本大于0.9,则添加Host信息
  325. strcat(request, "Host: ");
  326. strcat(request, host);
  327. strcat(request, "\r\n");
  328. }
  329. if(force_reload && proxyhost!=NULL)
  330. {
  331. // 如果不使用缓存且有代理服务器,则不缓存
  332. strcat(request, "Pragma: no-cache\r\n");
  333. }
  334. if(http10>1)
  335. // 如果HTTP1.1,则存在长链接,应将Connection设置为close
  336. strcat(request, "Connection: close\r\n");
  337. /* add empty line at end */
  338. if(http10>0) strcat(request, "\r\n"); //再添加换行
  339. // printf("Req=%s\n", request);
  340. }
  341. /* vraci system rc error kod */
  342. static int bench(void)
  343. {
  344. int i, j, k;
  345. pid_t pid=0;
  346. FILE *f;
  347. /* check avaibility of target server */
  348. // host/proxyhost为服务器端ip, proxyport为服务器端口号建立socket连接
  349. i=Socket(proxyhost==NULL?host:proxyhost, proxyport);
  350. if(i<0) {
  351. // 出现错误
  352. fprintf(stderr, "\nConnect to server failed. Aborting benchmark.\n");
  353. return 1;
  354. }
  355. //对套接字i的引用计数-1,当引用计数为0的时候,关闭套接字
  356. close(i);
  357. /* create pipe */
  358. // 子进程向父进程回报数据
  359. if(pipe(mypipe))
  360. {
  361. perror("pipe failed.");
  362. return 3;
  363. }
  364. /* not needed, since we have alarm() in childrens */
  365. /* wait 4 next system clock tick */
  366. /*
  367. cas=time(NULL);
  368. while(time(NULL)==cas)
  369. sched_yield();
  370. */
  371. /* fork childs */
  372. // 通过fork来产生clients个子程序来进行测试
  373. for(i=0;i<clients;i++)
  374. {
  375. pid=fork();
  376. if(pid <= (pid_t) 0)
  377. {
  378. // fork返回了0,要么此时是子进程, 要么是产生了错误
  379. /* child process or error*/
  380. // 子进程休眠,动机不明
  381. sleep(1); /* make childs faster */
  382. break;
  383. }
  384. }
  385. if( pid< (pid_t) 0)
  386. {
  387. // fork函数运行错误
  388. fprintf(stderr, "problems forking worker no. %d\n", i);
  389. perror("fork failed.");
  390. return 3;
  391. }
  392. if(pid== (pid_t) 0)
  393. {
  394. // 子进程运行此处的代码
  395. /* I am a child */
  396. //进行压力测试
  397. if(proxyhost==NULL)
  398. benchcore(host, proxyport, request);
  399. else
  400. benchcore(proxyhost, proxyport, request);
  401. /* write results to pipe */
  402. // 向管道写数据
  403. f=fdopen(mypipe[1], "w");
  404. if(f==NULL)
  405. {
  406. // 管道打开错误处理
  407. perror("open pipe for writing failed.");
  408. return 3;
  409. }
  410. /* fprintf(stderr, "Child - %d %d\n", speed, failed); */
  411. fprintf(f, "%d %d %d\n", speed, failed, bytes);
  412. fclose(f);
  413. return 0;
  414. }
  415. else
  416. {
  417. // 父进程运行此处的代码
  418. f=fdopen(mypipe[0], "r");
  419. if(f==NULL)
  420. {
  421. perror("open pipe for reading failed.");
  422. return 3;
  423. }
  424. // 设置没有缓冲区
  425. setvbuf(f, NULL, _IONBF, 0);
  426. // 初始化
  427. speed=0;
  428. failed=0;
  429. bytes=0;
  430. while(1)
  431. {
  432. // 通过f从管道读取数据
  433. // fscanf是阻塞式函数
  434. pid=fscanf(f, "%d %d %d", &i, &j, &k);
  435. if(pid<2)
  436. {
  437. fprintf(stderr, "Some of our childrens died.\n");
  438. break;
  439. }
  440. // 统计数据
  441. speed+=i;
  442. failed+=j;
  443. bytes+=k;
  444. /* fprintf(stderr, "*Knock* %d %d read=%d\n", speed, failed, pid); */
  445. // 子进程数据读完后,退出
  446. if(--clients==0) break;
  447. }
  448. fclose(f);
  449. // 输出测试数据
  450. printf("\nSpeed=%d pages/min, %d bytes/sec.\nRequests: %d susceed, %d failed.\n",
  451. (int)((speed+failed)/(benchtime/60.0f)),
  452. (int)(bytes/(float)benchtime),
  453. speed,
  454. failed);
  455. }
  456. return i;
  457. }
  458. void benchcore(const char *host, const int port, const char *req)
  459. {
  460. int rlen;
  461. char buf[1500];
  462. int s, i;
  463. struct sigaction sa;
  464. /* setup alarm signal handler */
  465. sa.sa_handler=alarm_handler; //设置alarm_handler为信号处理函数
  466. sa.sa_flags=0;
  467. // sigaction函数 成功会返回0,失败会返回-1
  468. // 设置超时将产生信息SIGALRM, 用sa的sa_handler函数处理
  469. if(sigaction(SIGALRM, &sa, NULL))
  470. exit(3);
  471. //开始计时
  472. alarm(benchtime);
  473. rlen = strlen(req);
  474. nexttry:while(1)
  475. {
  476. if(timerexpired)
  477. {
  478. // 超时返回
  479. if(failed>0)
  480. {
  481. /* fprintf(stderr, "Correcting failed by signal\n"); */
  482. failed--;
  483. }
  484. return;
  485. }
  486. s=Socket(host, port);
  487. if(s<0)
  488. { // 连接失败, failed数加1
  489. failed++;
  490. continue;
  491. }
  492. if( rlen!=write(s, req, rlen) )
  493. {
  494. // 实际写入数据长度和预期写入数据长度不符,失败
  495. failed++;
  496. // 套接字引用计数-1
  497. close(s);
  498. continue;
  499. }
  500. if( http10 == 0 ) //对http0.9做的处理,不懂
  501. if( shutdown(s, 1) )
  502. {
  503. // 禁止在一个套接口上进行数据的接收与发送?
  504. failed++;
  505. close(s);
  506. continue;
  507. }
  508. if(force==0)
  509. {
  510. // 等待读取服务器返回的数据
  511. /* read all available data from socket */
  512. while(1)
  513. {
  514. //超时退出
  515. if(timerexpired)
  516. break;
  517. // 读取返回数据,i为读取到的字节数
  518. i=read(s, buf, 1500);
  519. /* fprintf(stderr, "%d\n", i); */
  520. if(i<0)
  521. {
  522. // 读取文件失败,失败数加1,并对套接字引用计数减1
  523. failed++;
  524. close(s);
  525. // 跳回循环继续测试
  526. goto nexttry;
  527. }
  528. else
  529. if(i==0)
  530. break;
  531. else
  532. // 成功读取的字节数增加
  533. bytes+=i;
  534. }
  535. }
  536. if(close(s))
  537. {
  538. // 若套接字引用计数变为0,则失败数减1
  539. failed++;
  540. continue;
  541. }
  542. // 成功次数+1
  543. speed++;
  544. }
  545. }
  1. // socket.c
  2. /* $Id: socket.c 1.1 1995/01/01 07:11:14 cthuang Exp $
  3. *
  4. * This module has been modified by Radim Kolar for OS/2 emx
  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. #include <sys/types.h>
  16. #include <sys/socket.h>
  17. #include <fcntl.h>
  18. #include <netinet/in.h>
  19. #include <arpa/inet.h>
  20. #include <netdb.h>
  21. #include <sys/time.h>
  22. #include <string.h>
  23. #include <unistd.h>
  24. #include <stdio.h>
  25. #include <stdlib.h>
  26. #include <stdarg.h>
  27. /*
  28. 为了方便,把sockaddr_in定义放在这方便阅读
  29. struct sockaddr_in {
  30. short int sin_family; //协议族
  31. unsigned short int sin_port; //存储端口号
  32. struct in_addr sin_addr; //IP地址
  33. unsigned char sin_zero[8]; //作内存对齐用
  34. };
  35. */
  36. /*
  37. struct hostent {
  38. char *h_name; //主机的规范名
  39. char **h_aliases; //主机的别名
  40. int h_addrtype; //主机ip地址的类型
  41. int h_length; //主机ip地址的长度
  42. char **h_addr_list; //主机ip地址
  43. };
  44. */
  45. /*
  46. const char* host: 目标主机地址
  47. int clientPort: 主机地址的端口
  48. */
  49. int Socket(const char *host, int clientPort)
  50. {
  51. int sock;
  52. unsigned long inaddr;
  53. struct sockaddr_in ad;
  54. struct hostent *hp;
  55. memset(&ad, 0, sizeof(ad)); //初始化
  56. ad.sin_family = AF_INET; //协议族使用AF_INET,AF_INET是一种很常用的地址协议族
  57. inaddr = inet_addr(host); //将点分十进制转为整型数
  58. if (inaddr != INADDR_NONE) //如果host是一个合法IP地址
  59. memcpy(&ad.sin_addr, &inaddr, sizeof(inaddr)); //将转换得到的IP地址复制到结构体ad的sin_addr中
  60. else
  61. {
  62. //否则,host可能是一个域名或主机名,或是非法名称
  63. hp = gethostbyname(host); //根据host获得IP地址
  64. if (hp == NULL) //获取失败,为非法名称
  65. return -1; //返回-1
  66. //获得了IP地址,复制到ad中(可能有多个IP地址,复制第一个)
  67. memcpy(&ad.sin_addr, hp->h_addr, hp->h_length);
  68. }
  69. ad.sin_port = htons(clientPort); //转换字节顺序为高序字节
  70. //创建TCP套接字,系统自动推断用什么协议
  71. sock = socket(AF_INET, SOCK_STREAM, 0);
  72. if (sock < 0)
  73. //创建失败
  74. return sock;
  75. if (connect(sock, (struct sockaddr *)&ad, sizeof(ad)) < 0)
  76. //三次握手失败则返回-1
  77. return -1;
  78. return sock;
  79. }

Tinyhttpd

  1. /* J. David's webserver */
  2. /* This is a simple webserver.
  3. * Created November 1999 by J. David Blackstone.
  4. * CSE 4344 (Network concepts), Prof. Zeigler
  5. * University of Texas at Arlington
  6. */
  7. /* This program compiles for Sparc Solaris 2.6.
  8. * To compile for Linux:
  9. * 1) Comment out the #include <pthread.h> line.
  10. * 2) Comment out the line that defines the variable newthread.
  11. * 3) Comment out the two lines that run pthread_create().
  12. * 4) Uncomment the line that runs accept_request().
  13. * 5) Remove -lsocket from the Makefile.
  14. */
  15. #include <stdio.h>
  16. #include <sys/socket.h>
  17. #include <sys/types.h>
  18. #include <netinet/in.h>
  19. #include <arpa/inet.h>
  20. #include <unistd.h>
  21. #include <ctype.h>
  22. #include <strings.h>
  23. #include <string.h>
  24. #include <sys/stat.h>
  25. #include <pthread.h>
  26. #include <sys/wait.h>
  27. #include <stdlib.h>
  28. #define ISspace(x) isspace((int)(x))
  29. #define SERVER_STRING "Server: jdbhttpd/0.1.0\r\n"
  30. void* accept_request(void *pclient); //处理从套接字上监听到的一个HTTP请求
  31. void bad_request(int); //返回给客户端这是个错误请求
  32. void cat(int, FILE *); //读取服务器上某个文件写到socket套接字
  33. void cannot_execute(int); //处理发生在执行cgi程序时出现的错误
  34. void error_die(const char *); //把错误信息写到perror并退出
  35. void execute_cgi(int, const char *, const char *, const char *); //运行cgi程序的处理
  36. int get_line(int, char *, int); //读取套接字的一行
  37. void headers(int, const char *); //把HTTP响应的头部写到套接字
  38. void not_found(int); //处理找不到请求的文件时的情况
  39. void serve_file(int, const char *); //调用cat把服务器文件返回给浏览器
  40. int startup(u_short *); //初始化httpd服务
  41. void unimplemented(int); //返回给浏览器表明收到的HTTP请求所用的method不被支持
  42. /**********************************************************************/
  43. /* A request has caused a call to accept() on the server port to
  44. * return. Process the request appropriately.
  45. * Parameters: the socket connected to the client */
  46. /**********************************************************************/
  47. void* accept_request(void *pclient)
  48. {
  49. int client = *(int*)pclient;
  50. char buf[1024];
  51. int numchars;
  52. char method[255];
  53. char url[255];
  54. char path[512];
  55. size_t i, j;
  56. struct stat st; //文件状态
  57. // 指定是cgi程序的标记
  58. int cgi = 0; /* becomes true if server decides this is a CGI
  59. * program */
  60. char *query_string = NULL;
  61. // 获得请求的第一行
  62. numchars = get_line(client, buf, sizeof(buf));
  63. i = 0;
  64. j = 0;
  65. //把请求方法存到method数组
  66. while (!ISspace(buf[j]) && (i < sizeof(method) - 1))
  67. {
  68. method[i] = buf[j];
  69. i++;
  70. j++;
  71. }
  72. // 结束标记
  73. method[i] = '\0';
  74. // 如果不是GET也不是POST,则无法处理
  75. if (strcasecmp(method, "GET") && strcasecmp(method, "POST"))
  76. {
  77. unimplemented(client);
  78. return NULL;
  79. }
  80. // POST请求,开启cgi
  81. if (strcasecmp(method, "POST") == 0)
  82. cgi = 1;
  83. i = 0;
  84. // 读取 URL. j用于定位URL的位置,i作为储存url数组的index
  85. while (ISspace(buf[j]) && (j < sizeof(buf)))
  86. // 略过空格字符
  87. j++;
  88. while (!ISspace(buf[j]) && (i < sizeof(url) - 1) && (j < sizeof(buf)))
  89. {
  90. url[i] = buf[j];
  91. i++;
  92. j++;
  93. }
  94. url[i] = '\0';
  95. if (strcasecmp(method, "GET") == 0)
  96. {
  97. // 处理GET method
  98. query_string = url;
  99. while ((*query_string != '?') && (*query_string != '\0'))
  100. query_string++;
  101. if (*query_string == '?')
  102. {
  103. // 开启cgi
  104. cgi = 1;
  105. *query_string = '\0';
  106. query_string++;
  107. }
  108. }
  109. // 格式化url到path数组,html文件都在htdocs中
  110. sprintf(path, "htdocs%s", url);
  111. if (path[strlen(path) - 1] == '/')
  112. // 默认情况为访问index.html
  113. strcat(path, "index.html");
  114. if (stat(path, &st) == -1) {
  115. // 找不到文件,则把所有的headers的信息都丢弃
  116. while ((numchars > 0) && strcmp("\n", buf)) /* read & discard headers */
  117. numchars = get_line(client, buf, sizeof(buf));
  118. not_found(client); //发送寻找失败的信息
  119. }
  120. else
  121. {
  122. if ((st.st_mode & S_IFMT) == S_IFDIR)
  123. // 如果找到的是目录,访问目录下的index.html
  124. strcat(path, "/index.html");
  125. if ((st.st_mode & S_IXUSR) ||
  126. (st.st_mode & S_IXGRP) ||
  127. (st.st_mode & S_IXOTH) )
  128. // 如果: 文件所有者具有可执行权限 或是 用户组具有可执行权限 或是 其它用户具有可执行权限,则设置cgi
  129. cgi = 1;
  130. if (!cgi)
  131. // 不是cgi, 把服务器文件返回,否则执行cgi
  132. serve_file(client, path);
  133. else
  134. // 执行cgi
  135. execute_cgi(client, path, method, query_string);
  136. }
  137. // 断开与客户端的链接,http具有无连接特点,每次连接只处理一个请求
  138. close(client);
  139. return NULL;
  140. }
  141. /**********************************************************************/
  142. /* Inform the client that a request it has made has a problem.
  143. * Parameters: client socket */
  144. /**********************************************************************/
  145. void bad_request(int client)
  146. {
  147. char buf[1024];
  148. // 回应客户端错误的HTTP请求
  149. sprintf(buf, "HTTP/1.0 400 BAD REQUEST\r\n");
  150. send(client, buf, sizeof(buf), 0);
  151. sprintf(buf, "Content-type: text/html\r\n");
  152. send(client, buf, sizeof(buf), 0);
  153. sprintf(buf, "\r\n");
  154. send(client, buf, sizeof(buf), 0);
  155. sprintf(buf, "<P>Your browser sent a bad request, ");
  156. send(client, buf, sizeof(buf), 0);
  157. sprintf(buf, "such as a POST without a Content-Length.\r\n");
  158. send(client, buf, sizeof(buf), 0);
  159. }
  160. /**********************************************************************/
  161. /* Put the entire contents of a file out on a socket. This function
  162. * is named after the UNIX "cat" command, because it might have been
  163. * easier just to do something like pipe, fork, and exec("cat").
  164. * Parameters: the client socket descriptor
  165. * FILE pointer for the file to cat */
  166. /**********************************************************************/
  167. void cat(int client, FILE *resource)
  168. {
  169. char buf[1024];
  170. // 读取文件中的所有数据写到socket中
  171. fgets(buf, sizeof(buf), resource);
  172. while (!feof(resource))
  173. {
  174. send(client, buf, strlen(buf), 0);
  175. fgets(buf, sizeof(buf), resource);
  176. }
  177. }
  178. /**********************************************************************/
  179. /* Inform the client that a CGI script could not be executed.
  180. * Parameter: the client socket descriptor. */
  181. /**********************************************************************/
  182. void cannot_execute(int client)
  183. {
  184. char buf[1024];
  185. // 回应客户端 cgi无法执行
  186. sprintf(buf, "HTTP/1.0 500 Internal Server Error\r\n");
  187. send(client, buf, strlen(buf), 0);
  188. sprintf(buf, "Content-type: text/html\r\n");
  189. send(client, buf, strlen(buf), 0);
  190. sprintf(buf, "\r\n");
  191. send(client, buf, strlen(buf), 0);
  192. sprintf(buf, "<P>Error prohibited CGI execution.\r\n");
  193. send(client, buf, strlen(buf), 0);
  194. }
  195. /**********************************************************************/
  196. /* Print out an error message with perror() (for system errors; based
  197. * on value of errno, which indicates system call errors) and exit the
  198. * program indicating an error. */
  199. /**********************************************************************/
  200. void error_die(const char *sc)
  201. {
  202. // 显示出错信息
  203. perror(sc);
  204. exit(1);
  205. }
  206. /**********************************************************************/
  207. /* Execute a CGI script. Will need to set environment variables as
  208. * appropriate.
  209. * Parameters: client socket descriptor
  210. * path to the CGI script */
  211. /**********************************************************************/
  212. void execute_cgi(int client, const char *path,
  213. const char *method, const char *query_string)
  214. {
  215. char buf[1024];
  216. int cgi_output[2];
  217. int cgi_input[2];
  218. pid_t pid;
  219. int status;
  220. int i;
  221. char c;
  222. int numchars = 1;
  223. int content_length = -1;
  224. buf[0] = 'A';
  225. buf[1] = '\0';
  226. if (strcasecmp(method, "GET") == 0)
  227. // GET请求下,把所有的HTTP header读取并丢弃
  228. while ((numchars > 0) && strcmp("\n", buf)) /* read & discard headers */
  229. numchars = get_line(client, buf, sizeof(buf));
  230. else /* POST */
  231. {
  232. // 找出 content_length
  233. numchars = get_line(client, buf, sizeof(buf));
  234. while ((numchars > 0) && strcmp("\n", buf))
  235. {
  236. // 使用 \0 进行分隔
  237. buf[15] = '\0';
  238. if (strcasecmp(buf, "Content-Length:") == 0)
  239. // 找到Content-Length并且处理
  240. content_length = atoi(&(buf[16]));
  241. numchars = get_line(client, buf, sizeof(buf));
  242. }
  243. if (content_length == -1) {
  244. // 找不到Content-Length,发送请求错误信息
  245. bad_request(client);
  246. return;
  247. }
  248. }
  249. // HTTP 状态码 200
  250. sprintf(buf, "HTTP/1.0 200 OK\r\n");
  251. send(client, buf, strlen(buf), 0);
  252. // 建立管道
  253. if (pipe(cgi_output) < 0) {
  254. // 错误处理
  255. cannot_execute(client);
  256. return;
  257. }
  258. if (pipe(cgi_input) < 0) {
  259. cannot_execute(client);
  260. return;
  261. }
  262. // 建立一个进程进行处理
  263. if ( (pid = fork()) < 0 ) {
  264. cannot_execute(client);
  265. return;
  266. }
  267. if (pid == 0) /* child: CGI script */
  268. {
  269. // 子进程处理CGI部分
  270. char meth_env[255];
  271. char query_env[255];
  272. char length_env[255];
  273. // 把 stdout 重定向到 cgi_output的写入端
  274. dup2(cgi_output[1], 1);
  275. // 把 stdin 重定向到 cgi_input的读取端
  276. dup2(cgi_input[0], 0);
  277. // 关闭cgi_input的写入端和cgi_output的读取端
  278. close(cgi_output[0]);
  279. close(cgi_input[1]);
  280. // 设置request_method的环境变量
  281. sprintf(meth_env, "REQUEST_METHOD=%s", method);
  282. putenv(meth_env);
  283. if (strcasecmp(method, "GET") == 0) {
  284. // 设置 query_string 的环境变量
  285. sprintf(query_env, "QUERY_STRING=%s", query_string);
  286. putenv(query_env);
  287. }
  288. else { /* POST */
  289. // 设置 content_length的环境变量
  290. sprintf(length_env, "CONTENT_LENGTH=%d", content_length);
  291. putenv(length_env);
  292. }
  293. // 用execl运行cgi程序
  294. execl(path, query_string, NULL);
  295. exit(0);
  296. }
  297. else
  298. { /* parent */
  299. // 父进程处理部分,关闭cgi_output的写入端和cgi_input的读取端
  300. close(cgi_output[1]);
  301. close(cgi_input[0]);
  302. if (strcasecmp(method, "POST") == 0)
  303. // 接收POST过来的数据
  304. for (i = 0; i < content_length; i++) {
  305. recv(client, &c, 1, 0);
  306. // 把POST数据写入cgi_input(重定向到stdin)
  307. write(cgi_input[1], &c, 1);
  308. }
  309. // 读取cgi_output的管道输出到客户端(管道的输入是stdin)
  310. while (read(cgi_output[0], &c, 1) > 0)
  311. send(client, &c, 1, 0);
  312. // 关闭管道
  313. close(cgi_output[0]);
  314. close(cgi_input[1]);
  315. // 等待子进程
  316. waitpid(pid, &status, 0);
  317. }
  318. }
  319. /**********************************************************************/
  320. /* Get a line from a socket, whether the line ends in a newline,
  321. * carriage return, or a CRLF combination. Terminates the string read
  322. * with a null character. If no newline indicator is found before the
  323. * end of the buffer, the string is terminated with a null. If any of
  324. * the above three line terminators is read, the last character of the
  325. * string will be a linefeed and the string will be terminated with a
  326. * null character.
  327. * Parameters: the socket descriptor
  328. * the buffer to save the data in
  329. * the size of the buffer
  330. * Returns: the number of bytes stored (excluding null) */
  331. /**********************************************************************/
  332. /*
  333. int sock: 套接字描述符
  334. char *buf: 用于储存收到数据的缓冲区
  335. int size: 缓冲区大小
  336. */
  337. int get_line(int sock, char *buf, int size)
  338. {
  339. int i = 0;
  340. char c = '\0';
  341. int n;
  342. while ((i < size - 1) && (c != '\n'))
  343. {
  344. // 一次接受一个字节
  345. n = recv(sock, &c, 1, 0);
  346. /* DEBUG printf("%02X\n", c); */
  347. if (n > 0)
  348. {
  349. // n大于0说明收到了数据(收到一个字节)
  350. if (c == '\r')
  351. {
  352. // 如果收到'\r',则再接受一个字节,用于处理换行符为\r\n的情况
  353. n = recv(sock, &c, 1, MSG_PEEK); //MSG_PEEK标记用于防止读取成功后从tcp buffer中删除已读取的数据
  354. /* DEBUG printf("%02X\n", c); */
  355. if ((n > 0) && (c == '\n'))
  356. // 读取
  357. recv(sock, &c, 1, 0);
  358. else
  359. // 使得buf统一以\n结尾
  360. c = '\n';
  361. }
  362. // 把收到的字节储存起来
  363. buf[i] = c;
  364. i++; //收到一个字节累加一次
  365. }
  366. else
  367. c = '\n'; //统一让buf数组以\n结尾
  368. }
  369. buf[i] = '\0';
  370. return(i); // 返回收到并存储的字节数
  371. }
  372. /**********************************************************************/
  373. /* Return the informational HTTP headers about a file. */
  374. /* Parameters: the socket to print the headers on
  375. * the name of the file */
  376. /**********************************************************************/
  377. void headers(int client, const char *filename)
  378. {
  379. char buf[1024];
  380. (void)filename; /* could use filename to determine file type */
  381. // 发送http头
  382. strcpy(buf, "HTTP/1.0 200 OK\r\n");
  383. send(client, buf, strlen(buf), 0);
  384. strcpy(buf, SERVER_STRING);
  385. send(client, buf, strlen(buf), 0);
  386. sprintf(buf, "Content-Type: text/html\r\n");
  387. send(client, buf, strlen(buf), 0);
  388. strcpy(buf, "\r\n");
  389. send(client, buf, strlen(buf), 0);
  390. }
  391. /**********************************************************************/
  392. /* Give a client a 404 not found status message. */
  393. /**********************************************************************/
  394. void not_found(int client)
  395. {
  396. char buf[1024];
  397. // 404页面
  398. sprintf(buf, "HTTP/1.0 404 NOT FOUND\r\n");
  399. send(client, buf, strlen(buf), 0);
  400. sprintf(buf, SERVER_STRING);
  401. send(client, buf, strlen(buf), 0);
  402. sprintf(buf, "Content-Type: text/html\r\n");
  403. send(client, buf, strlen(buf), 0);
  404. sprintf(buf, "\r\n");
  405. send(client, buf, strlen(buf), 0);
  406. sprintf(buf, "<HTML><TITLE>Not Found</TITLE>\r\n");
  407. send(client, buf, strlen(buf), 0);
  408. sprintf(buf, "<BODY><P>The server could not fulfill\r\n");
  409. send(client, buf, strlen(buf), 0);
  410. sprintf(buf, "your request because the resource specified\r\n");
  411. send(client, buf, strlen(buf), 0);
  412. sprintf(buf, "is unavailable or nonexistent.\r\n");
  413. send(client, buf, strlen(buf), 0);
  414. sprintf(buf, "</BODY></HTML>\r\n");
  415. send(client, buf, strlen(buf), 0);
  416. }
  417. /**********************************************************************/
  418. /* Send a regular file to the client. Use headers, and report
  419. * errors to client if they occur.
  420. * Parameters: a pointer to a file structure produced from the socket
  421. * file descriptor
  422. * the name of the file to serve */
  423. /**********************************************************************/
  424. void serve_file(int client, const char *filename)
  425. {
  426. FILE *resource = NULL;
  427. int numchars = 1;
  428. char buf[1024];
  429. buf[0] = 'A';
  430. buf[1] = '\0';
  431. // 读取并丢弃header
  432. while ((numchars > 0) && strcmp("\n", buf)) /* read & discard headers */
  433. numchars = get_line(client, buf, sizeof(buf));
  434. resource = fopen(filename, "r");
  435. // 打开server文件爱你
  436. if (resource == NULL)
  437. not_found(client);
  438. else
  439. {
  440. // 写http头并且复制文件
  441. headers(client, filename);
  442. cat(client, resource);
  443. }
  444. fclose(resource);
  445. }
  446. /**********************************************************************/
  447. /* This function starts the process of listening for web connections
  448. * on a specified port. If the port is 0, then dynamically allocate a
  449. * port and modify the original port variable to reflect the actual
  450. * port.
  451. * Parameters: pointer to variable containing the port to connect on
  452. * Returns: the socket */
  453. /**********************************************************************/
  454. int startup(u_short *port)
  455. {
  456. int httpd = 0;
  457. struct sockaddr_in name;
  458. // 建立socket
  459. httpd = socket(PF_INET, SOCK_STREAM, 0);
  460. if (httpd == -1)
  461. // 建立失败
  462. error_die("socket");
  463. //初始化
  464. memset(&name, 0, sizeof(name));
  465. name.sin_family = AF_INET;
  466. name.sin_port = htons(*port);
  467. name.sin_addr.s_addr = htonl(INADDR_ANY);
  468. if (bind(httpd, (struct sockaddr *)&name, sizeof(name)) < 0)
  469. error_die("bind");
  470. if (*port == 0) /* if dynamically allocating a port */
  471. {
  472. // 动态随机分配一个端口
  473. socklen_t namelen = sizeof(name);
  474. if (getsockname(httpd, (struct sockaddr *)&name, &namelen) == -1)
  475. error_die("getsockname");
  476. *port = ntohs(name.sin_port);
  477. }
  478. //监听
  479. if (listen(httpd, 5) < 0)
  480. error_die("listen");
  481. // 返回socket id
  482. return(httpd);
  483. }
  484. /**********************************************************************/
  485. /* Inform the client that the requested web method has not been
  486. * implemented.
  487. * Parameter: the client socket */
  488. /**********************************************************************/
  489. void unimplemented(int client)
  490. {
  491. char buf[1024];
  492. *buf = '\0' //方便进行strcat
  493. // HTTP method 不被支持
  494. strcat(buf, "HTTP/1.0 501 Method Not Implemented\r\n");
  495. strcat(buf, SERVER_STRING);
  496. strcat(buf, "Content-Type: text/html\r\n\r\n<HTML><HEAD><TITLE>Method Not Implemented\r\n</TITLE></HEAD>\r\n<BODY><P>HTTP request method not supported.\r\n</BODY></HTML>\r\n");
  497. printf("%s\n",buf);
  498. send(client, buf, strlen(buf), 0);
  499. /*
  500. sprintf(buf, "HTTP/1.0 501 Method Not Implemented\r\n");
  501. send(client, buf, strlen(buf), 0);
  502. // 输出服务器信息到网页上
  503. sprintf(buf, SERVER_STRING);
  504. send(client, buf, strlen(buf), 0);
  505. sprintf(buf, "Content-Type: text/html\r\n");
  506. send(client, buf, strlen(buf), 0);
  507. sprintf(buf, "\r\n");
  508. send(client, buf, strlen(buf), 0);
  509. sprintf(buf, "<HTML><HEAD><TITLE>Method Not Implemented\r\n");
  510. send(client, buf, strlen(buf), 0);
  511. sprintf(buf, "</TITLE></HEAD>\r\n");
  512. send(client, buf, strlen(buf), 0);
  513. sprintf(buf, "<BODY><P>HTTP request method not supported.\r\n");
  514. send(client, buf, strlen(buf), 0);
  515. sprintf(buf, "</BODY></HTML>\r\n");
  516. send(client, buf, strlen(buf), 0);
  517. */
  518. }
  519. /**********************************************************************/
  520. int main(void)
  521. {
  522. int server_sock = -1;
  523. u_short port = 0; //端口为0
  524. int client_sock = -1;
  525. struct sockaddr_in client_name;
  526. socklen_t client_name_len = sizeof(client_name);
  527. pthread_t newthread;
  528. server_sock = startup(&port); //服务器端监听套接字设置
  529. printf("httpd running on port %d\n", port);
  530. while (1)
  531. {
  532. // 主线程阻塞等待客户端请求
  533. client_sock = accept(server_sock,
  534. (struct sockaddr *)&client_name,
  535. &client_name_len);
  536. if (client_sock == -1)
  537. error_die("accept");
  538. /* accept_request(client_sock); */
  539. // 监听到请求,则创建工作线程.
  540. if (pthread_create(&newthread , NULL, accept_request, (void*)&client_sock) != 0)
  541. perror("pthread_create");
  542. }
  543. // 关闭套接字,关闭TCP连接
  544. close(server_sock);
  545. return(0);
  546. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注