分类: LINUX
2010-09-20 11:19:45
最近想自己写一个http server 找了几个开源代码看了下 比如mini_httpd 写网络程序,必然要和socket打交道,既然是http server
那么http协议也要很熟悉
涉及下面这些东西: http 协议的具体格式?
socket的I/O模型?
首先是socket I/O模型的选者,这个很重要,直接关系到以后写出来的代码效率和性能
先看看mini httpd如何实现的
mini_httpd使用的fork来实现的
//建立socket
initialize_listen_socket( usockaddr* usaP ); ////////////////////////////////////////////// //initialize_listen_socket的代码
///////////////////////////////////////////// static int initialize_listen_socket( usockaddr* usaP ) { int listen_fd; int i; /* Check sockaddr. */ if ( ! sockaddr_check( usaP ) ) { syslog( LOG_ERR, "unknown sockaddr family on listen socket - %d", usaP->sa.sa_family ); (void) fprintf( stderr, "%s: unknown sockaddr family on listen socket - %d\n", argv0, usaP->sa.sa_family ); return -1; } listen_fd = socket( usaP->sa.sa_family, SOCK_STREAM, 0 ); if ( listen_fd < 0 ) { syslog( LOG_CRIT, "socket %.80s - %m", ntoa( usaP ) ); perror( "socket" ); return -1; } (void) fcntl( listen_fd, F_SETFD, 1 ); i = 1; if ( setsockopt( listen_fd, SOL_SOCKET, SO_REUSEADDR, (void*) &i, sizeof(i) ) < 0 ) { syslog( LOG_CRIT, "setsockopt SO_REUSEADDR - %m" ); perror( "setsockopt SO_REUSEADDR" ); return -1; } if ( bind( listen_fd, &usaP->sa, sockaddr_len( usaP ) ) < 0 ) { syslog( LOG_CRIT, "bind %.80s - %m", ntoa( usaP ) ); perror( "bind" ); return -1; } if ( listen( listen_fd, 1024 ) < 0 ) { syslog( LOG_CRIT, "listen - %m" ); perror( "listen" ); return -1; } -代码片段 for (;;) { /* Do we need to re-open the log file? */ if ( got_hup ) { re_open_logfile(); got_hup = 0; } /* Do a select() on at least one and possibly two listen fds. ** If there's only one listen fd then we could skip the select ** and just do the (blocking) accept(), saving one system call; ** that's what happened up through version 1.18. However there ** is one slight drawback to that method: the blocking accept() ** is not interrupted by a signal call. Since we definitely want ** signals to interrupt a waiting server, we use select() even ** if there's only one fd. */ FD_ZERO( &lfdset ); maxfd = -1; if ( listen4_fd != -1 ) { FD_SET( listen4_fd, &lfdset ); if ( listen4_fd > maxfd ) maxfd = listen4_fd; } if ( listen6_fd != -1 ) { FD_SET( listen6_fd, &lfdset ); if ( listen6_fd > maxfd ) maxfd = listen6_fd; } //int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,struct timeval *timeout); //注意((struct timeval*) 0 ),是一个NULL,不是0.select会block
//当socket可以读时候,select返回,这里只设置了fd_set *readfds,表示有数据可以读了,或者来了一个新的连接请求
//返回<0,错误处理
if ( select( maxfd + 1, &lfdset, (fd_set*) 0, (fd_set*) 0, (struct timeval*) 0 ) < 0 ) { if ( errno == EINTR || errno == EAGAIN ) continue; /* try again */ syslog( LOG_CRIT, "select - %m" ); perror( "select" ); exit( 1 ); } /* Accept the new connection. */ //用FD_ISSET检测我们关心的这个socket是不是可读 >0表可读写
//然后accept()
sz = sizeof(usa); if ( listen4_fd != -1 && FD_ISSET( listen4_fd, &lfdset ) ) conn_fd = accept( listen4_fd, &usa.sa, &sz ); else if ( listen6_fd != -1 && FD_ISSET( listen6_fd, &lfdset ) ) conn_fd = accept( listen6_fd, &usa.sa, &sz ); //错误处理
else { syslog( LOG_CRIT, "select failure" ); (void) fprintf( stderr, "%s: select failure\n", argv0 ); exit( 1 ); } //出错了?返回for继续等待新连接
if ( conn_fd < 0 ) { if ( errno == EINTR || errno == EAGAIN ) continue; /* try again */ #ifdef EPROTO if ( errno == EPROTO ) continue; /* try again */ #endif /* EPROTO */ syslog( LOG_CRIT, "accept - %m" ); perror( "accept" ); exit( 1 ); } /* Fork a sub-process to handle the connection. */ //fork一个子进程
r = fork(); //<0,出错,结束
if ( r < 0 ) { syslog( LOG_CRIT, "fork - %m" ); perror( "fork" ); exit( 1 ); } //对于子进程fork返回0
if ( r == 0 ) { /* Child process. */ client_addr = usa; if ( listen4_fd != -1 ) (void) close( listen4_fd ); if ( listen6_fd != -1 ) (void) close( listen6_fd ); //处理接受到的数据
handle_request(); //处理完了?退出子进程
exit( 0 ); } //对于父进程r返回>0,
//关掉accept返回的socket,返回for(;;)开始处
//select会阻塞,继续等待新的连接
(void) close( conn_fd ); } 其实不是很难 -那么windows何如现实的喃?
好久没啃过书了<> 大概有几中方法
-用select(),像这样的
大概的实现过程 #include #include #include #pragma comment(lib,"ws2_32.lib") typedef struct _SOCKET_INFORMATION { char buff[1024]; SOCKET socket; }SOCKET_INFORMATION, * LPSOCKET_INFORMATION; SOCKET_INFORMATION sockarray[FD_SETSIZE]; DWORD totalsockets = 0; int main ( ) { DWORD total; DWORD i; WSADATA wsa; SOCKET listensocket,clisocket; SOCKADDR_IN addr_server; addr_server.sin_family=2; addr_server.sin_port=htons(8888); addr_server.sin_addr.s_addr=htonl(INADDR_ANY); fd_set readset; fd_set writeset; WSAStartup(0x0202,&wsa); listensocket = socket(AF_INET,SOCK_STREAM,0); bind(listensocket, (sockaddr*)&addr_server, sizeof(addr_server)); listen(listensocket, 5); ULONG nonblock = 1; ioctlsocket(listensocket, FIONBIO, &nonblock); while(1) { FD_ZERO(&readset); FD_ZERO(&writeset); FD_SET(listensocket,&readset); for(i = 0; i { FD_SET(sockarray[i].socket, &readset); FD_SET(sockarray[i].socket, &writeset); } if((total = select(0, &readset, &writeset, NULL, NULL)) == SOCKET_ERROR) { printf("select() returned with error %d\n", WSAGetLastError()); return -1; } if(FD_ISSET(listensocket,&readset)) { clisocket=accept(listensocket, NULL, NULL); total--; sockarray[totalsockets].socket=clisocket; totalsockets++; //next } for(i = 0; total > 0 && i < totalsockets; i++) { if(FD_ISSET(sockarray[i].socket,&readset)) { total--; recv(sockarray[i].socket,sockarray[i].buff,1024,0); printf("%s",sockarray[i].buff); } if(FD_ISSET(sockarray[i].socket,&writeset)) { total--; // //... // } } } return 0; } 这个方法其实很不好 它只能处理64个socket,因为FD_SETSIZE在windows下的值为64,因为要处理一个listen socket,所以实际上只能处理
63个连接的socket,注意,在不同平台下FD_SETSIZE的值是不一样的,比如freebsd下FD_SETSIZE为1024.
如果要管理更多的socket,而又不使用线程的话,会写的很复杂,以后维护代码会很困难
执行效率也非常底,比如 我们的程序执行到 recv(sockarray[i].socket,sockarray[i].buff,1024,0); 时。 这个时候来了个新连接,但是这个时候还在 执行到FD_ISSET(listensocket,&readset),处理别的socket
那么这个新连接就会列队等待 直到循环处理完成,返回循环开始处,才会接受连接 所以说这样性能很差,很没效率 select只适合用户数量不多,不要求效率的场合
唯一的好处就是,它只使用了一个进程.比如我们常用的adsl,www管理设置页面
用WSAAsyncSelect()这个东西是基于windows消息的,对于没有窗口控制台服务程序,硬生生的加一个窗口这样不爽。。。
WSAEventSelect()基于windows事件的模型,事实上和select是差不多的
基于事件那么要建立事件
用WSACreateEvent(),因为我们要管理多个socket
那么需要一个数组,像这样的
WSAEVENT EventArray[WSA_MAXIMUM_WAIT_EVENTS];其中WSA_MAXIMUM_WAIT_EVENTS,在winsock2.h定义,值是64.
然后
WSAEventSelect(Accept, EventArray[EventTotal - 1], FD_READ|FD_WRITE|FD_CLOSE); WSAWaitForMultipleEvents();......因为WSAWaitForMultipleEvents(),只能支持由WSA_MAXIMUM_WAIT_EVENTS对象规定的最大值,它的值64。和select没有本质的区别 使用CreateThread() #include #include #include #pragma comment(lib,"ws2_32.lib") DWORD WINAPI proc(LPVOID lpParameter); int main ( ) { WSADATA wsa; HANDLE handle=NULL;SOCKET listensocket,clisocket; SOCKADDR_IN addr_server; addr_server.sin_family=2; addr_server.sin_port=htons(8888); addr_server.sin_addr.s_addr=htonl(INADDR_ANY); WSAStartup(0x0202,&wsa); listensocket = socket(AF_INET,SOCK_STREAM,0); bind(listensocket, (sockaddr*)&addr_server, sizeof(addr_server)); listen(listensocket, 1024); while(1) { clisocket = accept(listensocket, NULL, NULL); handle = handle=CreateThread(NULL, 0, proc, (LPVOID)&clisocket, 0, NULL); CloseHandle(handle); Sleep (1000);} return 0; } DWORD WINAPI proc(LPVOID lpParameter){ SOCKET s; char buff[1024]={0}; char mesg[]="Hello world!
" s=*((SOCKET*)lpParameter); int ret; while(1) {//还可以用select,这样就支持读超时
//超过时间就关到soctet,正常返回结束线程序
ret=recv(s,buff,1024,0); if( ret == 0 || ret == SOCKET_ERROR ) { break; } printf("%s",buff); } closesocket(s); return 0;}这个方法,唯一缺点是
连接太多会不停消耗内存
每一个socket都在单独的线程下处理但是代码,简单清晰,维护也很方便
和unix下的fork()其实是差不多的
只是windows并不支持fork().
下面是http协议它是在RFC 2616定义的下面是firfox浏览器,来连接我们刚刚写的好服务器程序我们的服务器程序,现在很简陋
就是接受到数据,打引出来
下面是请求消息的数据
GET / HTTP/1.1 Host: 192.168.1.80:8888 User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.0; zh-CN; rv:1.9.0.10) Gecko/2009042316 Firefox/3.0.10 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: zh-cn Accept-Encoding: gzip,deflate Accept-Charset: gb2312,utf-8;q=0.7,*;q=0.7 Keep-Alive: 300 Connection: keep-alive 我们先不自己解析这个请求内容,也不去判断是不是符合http/1.1协议的语法格式我们的http server,只是简单的响应这个请求,然后把数据发送给我们的客户端程序 填加代码
生成http响应消息我们来个具体的列子
现在我们使用telnet程序来连接一个正常的http server,它发送的数据到底是什么样的
telnetset LOCAL_ECHO //设置回显open tmdnet.nothave.com 80 //连接到我的http server
GET / HTTP/1.1 //我发送的HTTP请求
下面是server回送的消息 HTTP/1.1 400 Bad Request //告诉我们没找不到我们要的资源Content-Type: text/html //资源的类型Date: Tue, 05 May 2009 02:32:42 GMT //服务器时间,注意这个是世界时间Connection: close //告诉我们连接被关闭了Content-Length: 39 //附加数据的长度,单位是字节,就是显示在浏览器里的内容
//一个\r\n的空行
Bad Request (Invalid Hostname)
//数一下是不是39个字节?
遗失对主机的连接。
按任意键继续... 改写我们的代码,像下面这样
DWORD WINAPI proc(LPVOID lpParameter){ SOCKET s; char buff[1024]={0}; char mesg[]="Hello world!
"; s=*((SOCKET*)lpParameter); int ret; while(1) { ret=recv(s,buff,1024,0); if( ret == 0 || ret == SOCKET_ERROR ) { break; } printf("%s",buff); sprintf(buff, "HTTP/1.1 400 Bad Request\r\n" //"Content-Type: text/html\r\n" "Connection: close\r\n" "Content-Length: %d\r\n" "\r\n" "%s\r\n", strlen(mesg), mesg); printf("%s",buff); ret=send(s, buff, strlen(buff), 0); if(ret == SOCKET_ERROR) { break; } } closesocket(s); return 0;} 使用浏览器来访问它,我们看到了hello world!我们的server程序和真正可用的www server相比还有很多没完善的地方
下面我们一步一步的去完善它
首先是socket超时间的问题首先如果我们的客户端刚刚才连接上,然后电脑死机了
而我们的代码里使用的block socket,当执行到recv会阻塞
如果没有数据,那么永远都会停到那里,直到重新启动我们的server程序这样显然是不行的,server程序一般是长时间工作的当上面的情况发生时候,会有很多这样的没用的线程仍然在运行。而它们已经没用了
占用的空间并没有释放
所以我们需要让socket有个读取数据的时间限制,当过了一定时间,仍然没有数据时关掉这个socket,结束这个线程 windows下支持读超时 通常可以使用setsockopt()来改变socket的属性
有SO_RCVTIMEO和SO_SNDTIMEO分别对应,读写超时
但是使用setsockopt()不是一个好方法,因为这个是windows socket特有的属性
其它平台不一定支持
使用select()这个几乎所有的平台都支持
下面是实现代码
DWORD WINAPI proc(LPVOID lpParameter){ SOCKET s; char buff[1024]={0}; char *mesg="Hello world!
"; s=*((SOCKET*)lpParameter); int ret; fd_set read; struct timeval t; t.tv_sec=60; t.tv_usec=0; while(1) { FD_ZERO(&read); FD_SET(s,&read); ret=select(0, &read, NULL, NULL, &t); if( ret == 0 || ret == SOCKET_ERROR ) break; if(FD_ISSET(s, &read)) { ret=recv(s,buff,1024,0); if( ret == 0 || ret == SOCKET_ERROR ) break; sprintf(buff, "HTTP/1.1 400 Bad Request\r\n" "Content-Type: text/html\r\n" "Connection: close\r\n" "Content-Length: %d\r\n" "\r\n" "%s\r\n", strlen(mesg), mesg); } ret=send(s, buff, strlen(buff), 0); if(ret == SOCKET_ERROR) break; printf("%s",buff); closesocket(s); return 0;} 好了,现在程序能够在一个客户端连接上时候,但没发送任何数据,
那么60秒以后会socket连接,并且结束这个线程序
我们可以使用telnet程序来验证 telnet 127.0.0.1 8888什么都不干
用任务管理器来观察程序的线程数
60秒后telnet程序会显示断开连接然后在用任务管理器来观察程序的线程数,是不是比原来少了1个
上面的代码,事实上还有很多问题
比如接受http协议请求时候,发送的请求数据,可能不是一次能接受完,或者接受了很长时间,还是没接受到http协议结束标志,"\r\n\r\n"那么应该断开这个连接,结束线程。
对于一次不能接受完的情况,我们动态分配内存来处理,不停的接收.直到接受到"\r\n\r\n"标记.
对于很长时间(我们这里假设为60秒,正常情况下,一个http请求都会很快发送服务器端
但是,有这样的情况,客户机死机,恶意数据,等,所以我们需要考虑到这样的情况。我们的程序需要更健壮一些
大概实现过程应该是这样的
从接受到数据开始记时,如果60秒内还没接受到"\r\n\r\n"标记,关闭socket,结束线程
注意,我们写的控制台程序,没有消息循环,一般不能使用windows下的SetTimer()
用WaitForSingleObject(),这个是可以的,但是,为了让我们的线程继续运行那么必须在另外生成一个新线程,然后在WaitForSingleObject().,这样很不好当一个客户连接时候,就会出现,一个服务线程,一个为了处理上面情况而设置的超时线程
如果连接的客户多起来的时候,那么性能可能会下降,因为多了一个线程.或者用timeSetEvent()来实现,注意,这个函数是需要连接Winmm.lib,这个库。
也就是使用Winmm.dll,这个文件,但是很有可能你运行程序的平台没有这个文件(以后解决,windows真够麻烦的)
说到底,就是windows下没有unix下signal()SIGALRM这个信号,
DWORD WINAPI proc(LPVOID lpParameter){ SOCKET s; char buff[4096]={0}; char *p =NULL; char *pp =NULL; unsigned long psize=0; unsigned long len=0; char *mesg="Hello world!
"; s=*((SOCKET*)lpParameter); int ret; fd_set read; struct timeval t; t.tv_sec=60; t.tv_usec=0; while(1) { FD_ZERO(&read); FD_SET(s,&read); //wait data read ret=select(0, &read, NULL, NULL, &t); // read timeout && socketerror if( ret == 0 || ret == SOCKET_ERROR ) { break; } // date read if(FD_ISSET(s, &read)) { ret=recv(s,buff,sizeof(buff),0); if( ret == 0 || ret == SOCKET_ERROR ) { break; } } if(psize ==0) //说明是处理一个新的请求 { psize= ret+1; //比接受到的字符数大一个 len=0; //p=(char*)malloc(psize); //分配psize大小的空间
if(p == NULL) { break; } }else //不是处理一个新的而是一个没接受完成的请求数据 {psize += ret; //原来的大小+新接受数据大小
p=(char*)realloc(p, psize); //重新分配空间 }MoveMemory(&p[len] ,buff, ret); //移动buff里的数据到我们申请的空间
len+=ret; //移动到字符串末尾 *&p[len]='\0'; //http quest date read? if (strstr(p, "\r\n\r\n") != NULL || strstr(p, "\n\n") != NULL ) {//解析p里的http请求,发送结果给到socket上
sprintf(buff, "HTTP/1.1 200 OK\r\n" "Content-Type: text/html\r\n" // "Connection: close\r\n" "Content-Length: %d\r\n" "\r\n" "%s\r\n", strlen(mesg), mesg); ret=send(s, buff, strlen(buff), 0); psize=0; free(p); p=NULL; if(ret == SOCKET_ERROR) { break; } } } closesocket(s); return 0;} 最近心烦的事实在太多,这个东西已经写了几天了.静不下心来
不知道还能不能坚持下来,希望能坚持到最后!给自己加油////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 现在我们有了一个p的指针,指向了一个完整http请求的数据
现在要做的工作就是,解析这个缓冲区,然后分析指令,根据不同的指令
生成结果,发送到socket。下面是个真实的http请求数据使用的是firefox浏览器抓包分析
C:\>windump -i 3 -vvvnXxRs 0 -c 1 dst host tmdnet.nothave.com 输出如下数据
0x0000: 4500 0232 cd0b 4000 8006 2205 c0a8 0150 E..2..@..."....P0x0010: 3b2f 0c8e 0dad 0050 15b9 4dbf c64d 71e1 ;/.....P..M..Mq.0x0020: 5018 3cc1 2b3f 0000 4745 5420 2f74 6d70 P.<.+?..GET./tmp0x0030: 2f68 6f6f 6b67 696e 612e 646c 6c20 4854 /hookgina.dll.HT0x0040: 5450 2f31 2e31 0d0a 486f 7374 3a20 746d TP/1.1..Host:.tm0x0050: 646e 6574 2e6e 6f74 6861 7665 2e63 6f6d dnet.nothave.com0x0060: 0d0a 5573 6572 2d41 6765 6e74 3a20 4d6f ..User-Agent:.Mo0x0070: 7a69 6c6c 612f 352e 3020 2857 696e 646f zilla/5.0.(Windo0x0080: 7773 3b20 553b 2057 696e 646f 7773 204e ws;.U;.Windows.N0x0090: 5420 352e 303b 207a 682d 434e 3b20 7276 T.5.0;.zh-CN;.rv0x00a0: 3a31 2e39 2e30 2e31 3029 2047 6563 6b6f :1.9.0.10).Gecko0x00b0: 2f32 3030 3930 3432 3331 3620 4669 7265 /2009042316.Fire0x00c0: 666f 782f 332e 302e 3130 0d0a 4163 6365 fox/3.0.10..Acce0x00d0: 7074 3a20 7465 7874 2f68 746d 6c2c 6170 pt:.text/html,ap0x00e0: 706c 6963 6174 696f 6e2f 7868 746d 6c2b plication/xhtml+0x00f0: 786d 6c2c 6170 706c 6963 6174 696f 6e2f xml,application/0x0100: 786d 6c3b 713d 302e 392c 2a2f 2a3b 713d xml;q=0.9,*/*;q=0x0110: 302e 380d 0a41 6363 6570 742d 4c61 6e67 0.8..Accept-Lang0x0120: 7561 6765 3a20 7a68 2d63 6e0d 0a41 6363 uage:.zh-cn..Acc0x0130: 6570 742d 456e 636f 6469 6e67 3a20 677a ept-Encoding:.gz0x0140: 6970 2c64 6566 6c61 7465 0d0a 4163 6365 ip,deflate..Acce0x0150: 7074 2d43 6861 7273 6574 3a20 6762 3233 pt-Charset:.gb230x0160: 3132 2c75 7466 2d38 3b71 3d30 2e37 2c2a 12,utf-8;q=0.7,*0x0170: 3b71 3d30 2e37 0d0a 4b65 6570 2d41 6c69 ;q=0.7..Keep-Ali0x0180: 7665 3a20 3330 300d 0a43 6f6e 6e65 6374 ve:.300..Connect0x0190: 696f 6e3a 206b 6565 702d 616c 6976 650d ion:.keep-alive.0x01a0: 0a52 6566 6572 6572 3a20 6874 7470 3a2f .Referer:.http:/0x01b0: 2f74 6d64 6e65 742e 6e6f 7468 6176 652e /tmdnet.nothave.0x01c0: 636f 6d2f 746d 702f 0d0a 436f 6f6b 6965 com/tmp/..Cookie0x01d0: 3a20 4153 5053 4553 5349 4f4e 4944 4141 :.ASPSESSIONIDAA0x01e0: 5341 4153 5252 3d47 4d4d 4644 4d47 4443 SAASRR=GMMFDMGDC0x01f0: 464b 424c 4242 4347 464b 4946 424f 4f3b FKBLBBCGFKIFBOO;0x0200: 2041 5350 5345 5353 494f 4e49 4443 4152 .ASPSESSIONIDCAR0x0210: 4341 5452 523d 4142 4e4e 4544 4b43 4448 CATRR=ABNNEDKCDH0x0220: 4f44 4341 4f4a 494a 484f 504c 4548 0d0a ODCAOJIJHOPLEH..0x0230: 0d0a ..///////////////////////////////////////////////////////////////////整理后得到
GET /tmp/hookgina.dll HTTP/1.1Host: tmdnet.nothave.comUser-Agent: Mozilla/5.0.(Windows; U; Windows.NT.5.0; zh-CN; rv:1.9.0.10) Gecko/2009042316.Firefox/3.0.10Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8Accept-Language: zh-cnAccept-Encoding: gzip,deflateAccept-Charset: gb2312,utf-8;q=0.7,*;q=0.7Keep-Alive: 300Connection: keep-aliveReferer: Cookie: ASPSESSIONIDAASAASRR=GMMFDMGDCFKBLBBCGFKIFBOO;.ASPSESSIONIDCARCATRR=ABNNEDKCDHODCAOJIJHOPLEH/////////////////////////////////////////////////////////////////// HTTP Request格式 Request = Request-Line; *((general-header; |request-header; |entity-header)CRLF); CRLF [message-body];Request-Line = Method SP Request-URL SP HTTP-Version CRLF Method= "OPTIONS"; |"GET"; |"HEAD"; |"POST"; |"PUT"; |"DELETE"; |"TRACE"; |"CONNECT";目前所有通用服务器必须支持GET和HEAD方法,其它都是可选的
get方法用于获取URI资源,是最为常用的一种方法。
post方法用于向指定URI提交内容,服务器端响应其行为,该方法也极为常用。
head方法向URI发送请求,仅仅只需要获得响应的协议头。
put方法用于向URI发送请求,若URI不存在,则要求服务器端根据请求创建资源。当URI存在时,服务器端必须接受请求内容,将其作为URI资源的修改后版本。
delete方法用于删除URI标识的指定资源。
trace方法用于激活服务器端对请求的循环反馈,反馈作为http响应的正文内容被传输回客户端。
connect方法通常被用于使用代理连接。 "OPTIONS"; //表示在由Request-URL标识的请求/响应上有关通信选项信息的请求
下面是列子: ////////////////////////////////////////////////////////////////////OPTIONS * HTTP/1.1 //请求行 *说明请求的资源为服务器本身
Host: tmdnet.nothave.com //主机头 HTTP/1.1 200 OK //响应行Allow: OPTIONS, TRACE, GET, HEAD, POST //列出由Request-URL资源所支持的方法集(我用的服务器支持OPTIONS, TRACE, GET, HEAD, POST )
Content-Length: 0 //指明数据长度(10进制)
Server: Microsoft-IIS/6.0 //服务器所使用的软件信息Public: OPTIONS, TRACE, GET, HEAD, POST //指明那些方法是可以被缓存的X-Powered-By: ASP.NET //注意,这个响应行不是HTTP标准
//X-(indicates that the header is an extended header) //Powered-By: tell the HTTP client which engine the request/response is processed by. Date: Mon, 11 May 2009 02:43:06 GMT //服务器时间,它有特定的格式 ////////////////////////////////////////////////////////////////////OPTIONS / HTTP/1.1 // / 请求/这个资源
Host: tmdnet.nothave.com HTTP/1.1 200 OKAllow: OPTIONS, TRACE, GET, HEAD //在请求"/"这个资源时候可以用下面方法(OPTIONS, TRACE, GET, HEAD),和上面相比少了POST
Content-Length: 0Server: Microsoft-IIS/6.0Public: OPTIONS, TRACE, GET, HEAD, POSTX-Powered-By: ASP.NETDate: Mon, 11 May 2009 02:47:04 GMT ////////////////////////////////////////////////////////////////////OPTIONS /var/old/?id=62 HTTP/1.1Host: tmdnet.nothave.com HTTP/1.1 200 OKAllow: OPTIONS, TRACE, GET, HEADContent-Length: 0Server: Microsoft-IIS/6.0Public: OPTIONS, TRACE, GET, HEAD, POSTX-Powered-By: ASP.NETDate: Mon, 11 May 2009 03:15:37 GMT///////////////////////////////////////////////////////////////////////////////////// "GET"; //获取Rquest-URL标识的任何实体信息。如果Request-URL引用了某个数据的处理过程
//则应该以它产生的数据做为实体在响应中返回,而不是该过程的源代码。 GET / HTTP/1.1host:tmdnet.nothave.com HTTP/1.1 200 OKContent-Length: 622Content-Type: text/htmlServer: Microsoft-IIS/6.0X-Powered-By: ASP.NETDate: Mon, 11 May 2009 05:28:22 GMT 2009骞?鏈?2鏃?>
2009骞?鏈?0鏃?
//////////////////////////////////////////////////////////////////////////////////////// "HEAD"; //和GET方法相同,唯一差别是没有消息体,即响应消息中没有[message-body],通常用来测试请求的资源是否存在。
HEAD / HTTP/1.1host: tmdnet.nothave.com HTTP/1.1 200 OKContent-Length: 622Content-Type: text/htmlServer: Microsoft-IIS/6.0X-Powered-By: ASP.NETDate: Mon, 11 May 2009 05:28:58 GMT "DElETE"DELETE /bin/readmindd.txt HTTP/1.1Host: tmdnet.nothave.com HTTP/1.1 501 Not Implemented //说明服务器不支持完成响应所需要的功能Content-Length: 0Server: Microsoft-IIS/6.0X-Powered-By: ASP.NETDate: Mon, 11 May 2009 05:44:19 GMT ///////////////////////////////////////////////////////////////////////////////////////// "POST"; //把数据做为[message-body]提交给请求资源
下面是具体列子
0x0020: 5018 40af d202 0000 504f 5354 202f 7661 P.@.....POST./va 0x0030: 722f 6f6c 642f 6164 6d69 6e2e 6173 7020 r/old/admin.asp. 0x0040: 4854 5450 2f31 2e31 0d0a 486f 7374 3a20 HTTP/1.1..Host:. 0x0050: 746d 646e 6574 2e6e 6f74 6861 7665 2e63 tmdnet.nothave.c 0x0060: 6f6d 0d0a 5573 6572 2d41 6765 6e74 3a20 om..User-Agent:. 0x0070: 4d6f 7a69 6c6c 612f 352e 3020 2857 696e Mozilla/5.0.(Win 0x0080: 646f 7773 3b20 553b 2057 696e 646f 7773 dows;.U;.Windows 0x0090: 204e 5420 352e 303b 207a 682d 434e 3b20 .NT.5.0;.zh-CN;. 0x00a0: 7276 3a31 2e39 2e30 2e31 3029 2047 6563 rv:1.9.0.10).Gec 0x00b0: 6b6f 2f32 3030 3930 3432 3331 3620 4669 ko/2009042316.Fi 0x00c0: 7265 666f 782f 332e 302e 3130 0d0a 4163 refox/3.0.10..Ac 0x00d0: 6365 7074 3a20 7465 7874 2f68 746d 6c2c cept:.text/html, 0x00e0: 6170 706c 6963 6174 696f 6e2f 7868 746d application/xhtm 0x00f0: 6c2b 786d 6c2c 6170 706c 6963 6174 696f l+xml,applicatio 0x0100: 6e2f 786d 6c3b 713d 302e 392c 2a2f 2a3b n/xml;q=0.9,*/*; 0x0110: 713d 302e 380d 0a41 6363 6570 742d 4c61 q=0.8..Accept-La 0x0120: 6e67 7561 6765 3a20 7a68 2d63 6e0d 0a41 nguage:.zh-cn..A 0x0130: 6363 6570 742d 456e 636f 6469 6e67 3a20 ccept-Encoding:. 0x0140: 677a 6970 2c64 6566 6c61 7465 0d0a 4163 gzip,deflate..Ac 0x0150: 6365 7074 2d43 6861 7273 6574 3a20 6762 cept-Charset:.gb 0x0160: 3233 3132 2c75 7466 2d38 3b71 3d30 2e37 2312,utf-8;q=0.7 0x0170: 2c2a 3b71 3d30 2e37 0d0a 4b65 6570 2d41 ,*;q=0.7..Keep-A 0x0180: 6c69 7665 3a20 3330 300d 0a43 6f6e 6e65 live:.300..Conne 0x0190: 6374 696f 6e3a 206b 6565 702d 616c 6976 ction:.keep-aliv 0x01a0: 650d 0a52 6566 6572 6572 3a20 6874 7470 e..Referer:.http 0x01b0: 3a2f 2f74 6d64 6e65 742e 6e6f 7468 6176 ://tmdnet.nothav 0x01c0: 652e 636f 6d2f 7661 722f 6f6c 642f 6164 e.com/var/old/ad 0x01d0: 6d69 6e2e 6173 700d 0a43 6f6f 6b69 653a min.asp..Cookie: 0x01e0: 2041 5350 5345 5353 494f 4e49 4441 4153 .ASPSESSIONIDAAS 0x01f0: 4141 5352 523d 474d 4d46 444d 4744 4346 AASRR=GMMFDMGDCF 0x0200: 4b42 4c42 4243 4746 4b49 4642 4f4f 3b20 KBLBBCGFKIFBOO;. 0x0210: 4153 5053 4553 5349 4f4e 4944 4341 5243 ASPSESSIONIDCARC 0x0220: 4154 5252 3d41 424e 4e45 444b 4344 484f ATRR=ABNNEDKCDHO 0x0230: 4443 414f 4a49 4a48 4f50 4c45 483b 2041 DCAOJIJHOPLEH;.A 0x0240: 5350 5345 5353 494f 4e49 4441 4353 4342 SPSESSIONIDACSCB 0x0250: 5451 523d 4741 4844 4943 4a44 464e 5044 TQR=GAHDICJDFNPD 0x0260: 4f42 464e 4842 4e41 4c43 4a4a 0d0a 436f OBFNHBNALCJJ..Co 0x0270: 6e74 656e 742d 5479 7065 3a20 6170 706c ntent-Type:.appl 0x0280: 6963 6174 696f 6e2f 782d 7777 772d 666f ication/x-www-fo 0x0290: 726d 2d75 726c 656e 636f 6465 640d 0a43 rm-urlencoded..C 0x02a0: 6f6e 7465 6e74 2d4c 656e 6774 683a 2031 ontent-Length:.1 0x02b0: 3231 0d0a 0d0a 7469 746c 653d 7364 6673 21....title=sdfs 0x02c0: 6466 7364 2661 7574 686f 723d 6466 7364 dfsd&author=dfsd 0x02d0: 6673 6426 696e 746f 3d73 6466 7364 6673 fsd&into=sdfsdfs 0x02e0: 6466 2670 6172 743d 2543 4625 4235 2543 df&part=%CF%B5%C 0x02f0: 4425 4233 2542 3925 4443 2543 3025 4544 D%B3%B9%DC%C0%ED 0x0300: 2663 6f6e 3d73 6464 6464 6464 6464 6464 &con=sdddddddddd 0x0310: 6464 6464 6464 6464 6464 6464 6464 6464 dddddddddddddddd 0x0320: 2668 6964 3d31 2672 6546 6f72 6d3d 33 &hid=1&reForm=3
chinaunix网友2010-09-21 07:54:57
很好的, 收藏了 推荐一个博客,提供很多免费软件编程电子书下载: http://free-ebooks.appspot.com