日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Tinyhttpd的实现和一些基本问题的解决

發布時間:2025/4/5 编程问答 14 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Tinyhttpd的实现和一些基本问题的解决 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

TInyhttpd是一個簡單的http服務器的實現,代碼總共有500多行,但是讀下來對http的具體實現過程和linux網絡編程的學習都很有好處。我重寫了代碼,然后進行了詳細的注釋。注釋見詳細代碼。?
具體的可以用圖表示:?
?
代碼:

/*#!/usr/bin/env * ****************************************************** * Last modified: 2018-05-06 16:27 * Filename : tinyhttpd.c * Description : * ********************************************************/ #include<stdio.h> #include<sys/wait.h> #include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h> #include<unistd.h> #include<sys/types.h> #include<stdlib.h> #include<errno.h> #include<string.h> #include<fcntl.h> #include<sys/stat.h> #include<sys/select.h> #include<ctype.h> #include<pthread.h> #include<stdint.h>#define ISspace(x) isspace((int)(x))#define SERVER_STRING "Server:jdbhttpd/0.1.0\r\n"#define STDIN 0 #define STDOUT 1 #define STDERR 2void execute_cgi(int ,const char *,const char *,const char *); void bad_requests(int ); void accept_request(void *); int startup(u_short *); void error_die(const char *); void unimplemented(int ); void not_found(int ); void serve_file(int,const char*); void headers(int,const char*); void cat(int,FILE*); void cannot_execute(int); int get_line(int,char*,int );void error_die(const char *sc) {perror(sc);exit(1); }void accept_request(void *arg) {int client = (intptr_t)arg;char buf[1024];size_t numchars;//保存請求方式char method[255];//保存請求urlchar url[255];//保存請求文件路徑char path[512];size_t i,j;struct stat st;//如果cgi=1 則執行cgi程序int cgi = 0;char *query_string =NULL;numchars = get_line(client,buf,sizeof(buf));i = 0,j = 0;while(!ISspace(buf[i])&&(i<sizeof(method)-1)){//請求報文第一行就會說明請求方式,是否為GET或者POSTmethod[i] = buf[i];i++;}j = i;method[i] = '\0';//tinyhttpd只實現了GET和POSTif(strcasecmp(method,"GET")&&strcasecmp(method,"POST")){//tinyhttpd只實現了GET和POSTunimplemented(client);return;}if(strcasecmp(method,"POST")==0) cgi = 1;i = 0;//跨過空格while(ISspace(buf[j])&&(j<numchars)) j++;//得到請求的urlwhile(!ISspace(buf[j])&&(i<sizeof(url)-1)&&(j<numchars)){url[i] = buf[j];i++;j++;}url[i] = '\0';//對于GET請求,如果有攜帶參數,則query_string指針指向url中?后面的GET參數if(strcasecmp(method,"GET")==0){query_string = url;while((*query_string!='?')&&(*query_string!='\0'))query_string++;if(*query_string=='?'){cgi = 1;*query_string = '\0';query_string++;}}sprintf(path,"htdocs%s",url);//如果path是一個目錄,默認設置為首頁index.htmlif(path[strlen(path)-1]=='/') strcat(path,"index.html");if(stat(path,&st)==-1){while((numchars>0)&&strcmp("\n",buf)) numchars = get_line(client,buf,sizeof(buf));not_found(client);}else{//如果為目錄if((st.st_mode&S_IFMT)==S_IFDIR) strcat(path,"/index.html");if((st.st_mode&S_IXUSR)||(st.st_mode&S_IXGRP)||(st.st_mode&S_IXOTH)) cgi = 1;//返回靜態文件if(!cgi) serve_file(client,path);elseexecute_cgi(client,path,method,query_string);}close(client); }void execute_cgi(int client,const char *path,const char *method,const char *query_string) {char buf[1024];int cgi_input[2];int cgi_output[2];pid_t pid;int status;int i;char c;int numchars = 1;int content_length = -1;buf[0] = 'A';buf[1] = '\0';//讀取并且舍棄頭部信息if(strcasecmp(method,"GET")==0){while((numchars>0)&&strcmp("\n",buf))numchars = get_line(client,buf,sizeof(buf));}else{numchars = get_line(client,buf,sizeof(buf));//15是因為Content-Length長度為15while((numchars>0)&&strcmp("\n",buf)){buf[15] = '\0';//求得Content-content_length的長度if(strcasecmp(buf,"Content-Length:")==0)content_length = atoi(&(buf[16]));numchars = get_line(client,buf,sizeof(buf));}if(content_length==-1){bad_requests(client);return;}}if(pipe(cgi_output)<0){cannot_execute(client);return;}if(pipe(cgi_input)<0){cannot_execute(client);return;}//fork一個子進程if((pid = fork())<0){cannot_execute(client);return;}sprintf(buf,"HTTP/1.0 200 OK\r\n");send(client,buf,strlen(buf),0);//子進程執行cgi,并將輸出傳到cgi_output[1]if(pid==0){char meth_env[255];char query_env[255];char length_env[255];//對標準輸入和標準輸出進行重定向dup2(cgi_output[1],STDOUT);dup2(cgi_input[0],STDIN);close(cgi_output[0]);close(cgi_input[1]);sprintf(meth_env,"REQUEST_METHOD=%s",method);putenv(meth_env);if(strcasecmp(method,"GET")==0){sprintf(query_env,"QUERY_STRING=%s",query_string);putenv(query_env);}else{sprintf(length_env,"CONTENT_LENGTH=%d",content_length);putenv(length_env);}execl(path,path,NULL);exit(0);}//父進程傳輸數據和把數據發送到瀏覽器else{close(cgi_input[0]);close(cgi_output[1]);if(strcasecmp(method,"POST")==0){for(i = 0;i<content_length;i++){recv(client,&c,1,0);write(cgi_input[1],&c,1);}}while(read(cgi_output[0],&c,1)>0) send(client,&c,1,0);close(cgi_output[0]);close(cgi_input[1]);waitpid(pid,&status,0);} }void cannot_execute(int client) {char buf[1024];sprintf(buf,"HTTP/1.0 500 Internal Server Error\r\n");send(client,buf,sizeof(buf),0);sprintf(buf,"Content-type: text/html\r\n");send(client,buf,sizeof(buf),0);sprintf(buf,"\r\n");send(client,buf,sizeof(buf),0);sprintf(buf,"<P>Error prohibited CGI execution. \r\n ");send(client,buf,sizeof(buf),0); }void bad_requests(int client) {char buf[1024];sprintf(buf,"HTTP/1.0 400 BAD REQUEST\r\n");send(client,buf,sizeof(buf),0);sprintf(buf,"Content-type: text/html\r\n");send(client,buf,sizeof(buf),0);sprintf(buf,"\r\n");send(client,buf,sizeof(buf),0);sprintf(buf,"<P>your browser senta bad request, ");send(client,buf,sizeof(buf),0);sprintf(buf,"such as a POST without a Content-Length.\r\n");send(client,buf,sizeof(buf),0); }void headers(int client,const char* filename) {char buf[1024];(void)filename;strcpy(buf,"HTTP/1.0 200 OK\r\n");send(client,buf,strlen(buf),0);strcpy(buf,SERVER_STRING);send(client,buf,strlen(buf),0);sprintf(buf,"Content-Type: text/html\r\n");send(client,buf,strlen(buf),0);strcpy(buf,"\r\n");send(client,buf,strlen(buf),0); }void cat(int client,FILE *resource) {char buf[1024];fgets(buf,sizeof(buf),resource);while(!feof(resource)){send(client,buf,strlen(buf),0);fgets(buf,sizeof(buf),resource);} }void serve_file(int client,const char *filename) {FILE *resource = NULL;int numchars = 1;char buf[1024];buf[0] = 'A';buf[1] = '\0';while((numchars>0)&&strcmp("\n",buf)) numchars = get_line(client,buf,sizeof(buf));resource = fopen(filename,"r");if(resource==NULL) not_found(client);else{//添加http頭部headers(client,filename);//并發送文件內容cat(client,resource);}fclose(resource); }void not_found(int client) {char buf[1024];sprintf(buf,"HTTP/1.0 404 NOT FOUND\r\n");send(client,buf,strlen(buf),0);sprintf(buf,SERVER_STRING);send(client,buf,strlen(buf),0);sprintf(buf,"Content-Type: text/html\r\n");send(client,buf,strlen(buf),0);sprintf(buf,"\r\n");send(client,buf,strlen(buf),0);sprintf(buf,"<HTML><TITLE>Not Found</TITLE>\r\n");send(client,buf,strlen(buf),0);sprintf(buf,"<BODY><P>The server could not fulfill\r\n");send(client,buf,strlen(buf),0);sprintf(buf,"your request because the resource specified\r\n");send(client,buf,strlen(buf),0);sprintf(buf,"is unavailable or nonexistent.\r\n");send(client,buf,strlen(buf),0);sprintf(buf,"</BODY></HTML>\r\n");send(client,buf,strlen(buf),0);}void unimplemented(int client) {char buf[1024];sprintf(buf,"HTTP/1.0 501 Method Not implemented\r\n");send(client,buf,strlen(buf),0);sprintf(buf,SERVER_STRING);send(client,buf,strlen(buf),0);sprintf(buf,"Content-Type: text/html\r\n");send(client,buf,strlen(buf),0);sprintf(buf,"\r\n");send(client,buf,strlen(buf),0);sprintf(buf,"<HTML><HEAD><TITLE>Method Not Implemented\r\n");send(client,buf,strlen(buf),0);sprintf(buf,"</TITLE></HEAD>\r\n");send(client,buf,strlen(buf),0);sprintf(buf,"<BODY><P>HTTP request method not supported. \r\n");send(client,buf,strlen(buf),0);sprintf(buf,"</BODY></HTML>\r\n");send(client,buf,strlen(buf),0); }//解析一行http報文 int get_line(int sock,char *buf,int size) {int i = 0;char c = '\0';int n;while((i<size-1)&&(c!='\n')){//先取一個字節n = recv(sock,&c,1,0);if(n>0){//因為請求報文末尾是以\r\n結束的if(c=='\r'){//偷窺一個字節,判斷是否為\n,是則結束n = recv(sock,&c,1,MSG_PEEK);if(n>0&&(c=='\n')) recv(sock,&c,1,0);else c = '\n';}buf[i] = c;i++;}else c = '\n';}buf[i] = '\0';return i; }int startup(u_short *port) {int httpd = 0;int on = 1;struct sockaddr_in name;httpd = socket(PF_INET,SOCK_STREAM,0);//新建一個服務器端socketif(httpd == -1) error_die("socket");memset(&name,0,sizeof(name));//對socket的基本屬性進行賦值name.sin_family = AF_INET;name.sin_port = htons(*port); //htons從主機字節序轉換為網絡字節序name.sin_addr.s_addr = htonl(INADDR_ANY);//setsockopt原型 int setsockopt(int sockfd,int level,int optname,const void *optval,socklen_t optlen)if((setsockopt(httpd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)))<0)//{error_die("setsockopt failed");}//對端口進行綁定if(bind(httpd,(struct sockaddr*)&name,sizeof(name))<0) error_die("bind");//動態分配一個端口if(*port==0){socklen_t namelen = sizeof(name);if(getsockname(httpd,(struct sockaddr*)&name,&namelen)==-1){error_die("getsockname");}*port = ntohs(name.sin_port);}//對socket進行監聽//5代表內核維護一個隊列跟蹤這些完成的連接但服務器還沒有接受處理//或者正在進行的連接,代表大小的上限if(listen(httpd,5)<0) error_die("listen");else printf("listen success\n");return httpd; }int main(void) {int server_sock = -1;u_short port = 0;int client_sock = -1;struct sockaddr_in client_name;socklen_t client_name_len = sizeof(client_name);pthread_t newthread;//對服務器端口號進行綁定server_sock = startup(&port);printf("httpd running on the port %d\n",port);while(1){//接受來自客戶端的連接client_sock = accept(server_sock,(struct sockaddr *)&client_name,&client_name_len);if(client_sock==-1) error_die("accept");if(pthread_create(&newthread,NULL,(void*)accept_request,(void*)(intptr_t)client_sock)!=0) //每次收到請求,創建一個線程來處理接受到的請求,把client_sock轉成地址參數//傳入pthread_createperror("pthread_create");}//關掉服務器端socketclose(server_sock);return 0; }
  • 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
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372
  • 373
  • 374
  • 375
  • 376
  • 377
  • 378
  • 379
  • 380
  • 381
  • 382
  • 383
  • 384
  • 385
  • 386
  • 387
  • 388
  • 389
  • 390
  • 391
  • 392
  • 393
  • 394
  • 395
  • 396
  • 397
  • 398
  • 399
  • 400
  • 401
  • 402
  • 403
  • 404
  • 405
  • 406
  • 407
  • 408
  • 409
  • 410
  • 411
  • 412
  • 413
  • 414
  • 415
  • 416
  • 417
  • 418
  • 419
  • 420
  • 421
  • 422
  • 423
  • 424
  • 425
  • 426
  • 427
  • 428
  • 429
  • 430
  • 431
  • 432
  • 433
  • 434
  • 435
  • 436
  • 437
  • 438
  • 439
  • 440
  • 441
  • 442
  • 443
  • 444
  • 445
  • 446
  • 447
  • 448
  • 449
  • 450

然后寫命令./tinyhttpd執行得到?
?
然后輸入紫色?
?
當然在這個過程中有幾個問題:?
首先index.html的不能有執行權限,還有后綴為.cgi的程序必須有執行權限,可以使用chmod命令改變執行權限。

然后使用wireshark對本地回環端口進行監聽,然后追蹤tcp流。可以看到http的請求和應答報文。

POST /color.cgi HTTP/1.1 Host: 127.0.0.1:36299 User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-GB,en;q=0.5 Accept-Encoding: gzip, deflate Referer: http://127.0.0.1:36299/ Content-Type: application/x-www-form-urlencoded Content-Length: 10 Connection: keep-alive Upgrade-Insecure-Requests: 1color=pinkHTTP/1.0 200 OK HTTP/1.0 200 OK --------------10 Content-Type: text/html; charset=ISO-8859-1<!DOCTYPE htmlPUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="en-US" xml:lang="en-US"> <head> <title>PINK</title> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> </head> <body bgcolor="pink"> <h1>This is pink</h1> </body> </html>
  • 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

版權聲明:本文為博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/u014303647/article/details/80231844

總結

以上是生活随笔為你收集整理的Tinyhttpd的实现和一些基本问题的解决的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。