分类: 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,它发送的数据到底是什么样的
telnet
set 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..@..."....P
0x0010: 3b2f 0c8e 0dad 0050 15b9 4dbf c64d 71e1 ;/.....P..M..Mq.
0x0020: 5018 3cc1 2b3f 0000 4745 5420 2f74 6d70 P.<.+?..GET./tmp
0x0030: 2f68 6f6f 6b67 696e 612e 646c 6c20 4854 /hookgina.dll.HT
0x0040: 5450 2f31 2e31 0d0a 486f 7374 3a20 746d TP/1.1..Host:.tm
0x0050: 646e 6574 2e6e 6f74 6861 7665 2e63 6f6d dnet.nothave.com
0x0060: 0d0a 5573 6572 2d41 6765 6e74 3a20 4d6f ..User-Agent:.Mo
0x0070: 7a69 6c6c 612f 352e 3020 2857 696e 646f zilla/5.0.(Windo
0x0080: 7773 3b20 553b 2057 696e 646f 7773 204e ws;.U;.Windows.N
0x0090: 5420 352e 303b 207a 682d 434e 3b20 7276 T.5.0;.zh-CN;.rv
0x00a0: 3a31 2e39 2e30 2e31 3029 2047 6563 6b6f :1.9.0.10).Gecko
0x00b0: 2f32 3030 3930 3432 3331 3620 4669 7265 /2009042316.Fire
0x00c0: 666f 782f 332e 302e 3130 0d0a 4163 6365 fox/3.0.10..Acce
0x00d0: 7074 3a20 7465 7874 2f68 746d 6c2c 6170 pt:.text/html,ap
0x00e0: 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-Lang
0x0120: 7561 6765 3a20 7a68 2d63 6e0d 0a41 6363 uage:.zh-cn..Acc
0x0130: 6570 742d 456e 636f 6469 6e67 3a20 677a ept-Encoding:.gz
0x0140: 6970 2c64 6566 6c61 7465 0d0a 4163 6365 ip,deflate..Acce
0x0150: 7074 2d43 6861 7273 6574 3a20 6762 3233 pt-Charset:.gb23
0x0160: 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-Ali
0x0180: 7665 3a20 3330 300d 0a43 6f6e 6e65 6374 ve:.300..Connect
0x0190: 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/..Cookie
0x01d0: 3a20 4153 5053 4553 5349 4f4e 4944 4141 :.ASPSESSIONIDAA
0x01e0: 5341 4153 5252 3d47 4d4d 4644 4d47 4443 SAASRR=GMMFDMGDC
0x01f0: 464b 424c 4242 4347 464b 4946 424f 4f3b FKBLBBCGFKIFBOO;
0x0200: 2041 5350 5345 5353 494f 4e49 4443 4152 .ASPSESSIONIDCAR
0x0210: 4341 5452 523d 4142 4e4e 4544 4b43 4448 CATRR=ABNNEDKCDH
0x0220: 4f44 4341 4f4a 494a 484f 504c 4548 0d0a ODCAOJIJHOPLEH..
0x0230: 0d0a ..
///////////////////////////////////////////////////////////////////
整理后得到
GET /tmp/hookgina.dll HTTP/1.1
Host: tmdnet.nothave.com
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
Referer:
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 OK
Allow: OPTIONS, TRACE, GET, HEAD //在请求"/"这个资源时候可以用下面方法(OPTIONS, TRACE, GET, HEAD),和上面相比少了POST
Content-Length: 0
Server: Microsoft-IIS/6.0
Public: OPTIONS, TRACE, GET, HEAD, POST
X-Powered-By: ASP.NET
Date: Mon, 11 May 2009 02:47:04 GMT
////////////////////////////////////////////////////////////////////
OPTIONS /var/old/?id=62 HTTP/1.1
Host: tmdnet.nothave.com
HTTP/1.1 200 OK
Allow: OPTIONS, TRACE, GET, HEAD
Content-Length: 0
Server: Microsoft-IIS/6.0
Public: OPTIONS, TRACE, GET, HEAD, POST
X-Powered-By: ASP.NET
Date: Mon, 11 May 2009 03:15:37 GMT
/////////////////////////////////////////////////////////////////////////////////////
"GET"; //获取Rquest-URL标识的任何实体信息。如果Request-URL引用了某个数据的处理过程
//则应该以它产生的数据做为实体在响应中返回,而不是该过程的源代码。
GET / HTTP/1.1
host:tmdnet.nothave.com
HTTP/1.1 200 OK
Content-Length: 622
Content-Type: text/html
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
Date: Mon, 11 May 2009 05:28:22 GMT
2009
骞?鏈?2鏃?
>
2009骞?鏈?0鏃?
////////////////////////////////////////////////////////////////////////////////////////
"HEAD"; //和GET方法相同,唯一差别是没有消息体,即响应消息中没有[message-body],通常用来测试请求的资源是否存在。
HEAD / HTTP/1.1
host: tmdnet.nothave.com
HTTP/1.1 200 OK
Content-Length: 622
Content-Type: text/html
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
Date: Mon, 11 May 2009 05:28:58 GMT
"DElETE"
DELETE /bin/readmindd.txt HTTP/1.1
Host: tmdnet.nothave.com
HTTP/1.1 501 Not Implemented //说明服务器不支持完成响应所需要的功能
Content-Length: 0
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
Date: 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