我在嵌入式平台上忙于自己的http服务器实现。从技术上讲,服务器是HTTP 1.0兼容的,因此它希望客户端发送标题“Connection:Keep-Alive”以保持连接打开。
实现看起来像这样。我删除了解析HTTP标头并执行请求的代码,以使帖子尽可能短:
int Service_Request(int conn) {
struct ReqInfo reqinfo;
volatile int resource = 0;
int retval = 0;
Req_Result req_result = GOT_REQ;
InitReqInfo(&reqinfo);
/* while we still have HTTP requests to process */
while (req_result == GOT_REQ)
{
/* Get HTTP request, there are 3 different return values:
* GOT_REQ: we got a valid HTTP request
* TIMEOUT_REQ we timed out waiting for a request
* ERROR_REQ there was some error receiving from the socket
* usually because the connection was closed by the peer*/
req_result = Get_Request(conn, &reqinfo);
if ( req_result == TIMEOUT_REQ)
{
/* timed out waiting for the client, exit */
retval = 0;
break;
}
else if (req_result == ERROR_REQ)
{
/* some error, exit */
retval = -1;
break;
}
/* Process the request GET, PUT and POST is supported*/
if (reqinfo.method == GET)
{
/* code to handle GET*/
}
/* PUT and POST are handled in the same way */
else if ((reqinfo.method == PUT) || (reqinfo.method == POST) )
{
/* Code to handle PUT and POST*/
}
else
{
/* not supported, code should never get here */
reqinfo.status = 501;
Return_Error_Msg(conn, &reqinfo);
}
/*Diag_Msg("Client Request: \r\n");
Diag_Msg(reqinfo.clientRequest);*/
/*
* the reqinfo.keep_alive flag will be set to 1 if the
* "Connection: Keep-Alive" header was sent by the client
*/
if(reqinfo.keep_alive == 0)
{
break;
}
reqinfo.keep_alive_max--;
if(reqinfo.keep_alive_max <= 0 )
{
/*
* the connection has been reused for the maxmum amount of times, stop
*/
break;
}
/*
* If we get here, we will clear the memory used for the client request
* and go to the beginning of the while loop to receive another request
*/
Writeline(conn,"\r\n",2);
FreeReqInfo(&reqinfo);
}
FreeReqInfo(&reqinfo);
return (retval);
}
int Service_Request(int conn) {
struct ReqInfo reqinfo;
volatile int resource = 0;
int retval = 0;
Req_Result req_result = GOT_REQ;
InitReqInfo(&reqinfo);
/* while we still have HTTP requests to process */
while (req_result == GOT_REQ)
{
/* Get HTTP request, there are 3 different return values:
* GOT_REQ: we got a valid HTTP request
* TIMEOUT_REQ we timed out waiting for a request
* ERROR_REQ there was some error receiving from the socket
* usually because the connection was closed by the peer*/
req_result = Get_Request(conn, &reqinfo);
if ( req_result == TIMEOUT_REQ)
{
/* timed out waiting for the client, exit */
retval = 0;
break;
}
else if (req_result == ERROR_REQ)
{
/* some error, exit */
retval = -1;
break;
}
/* Process the request GET, PUT and POST is supported*/
if (reqinfo.method == GET)
{
/* code to handle GET*/
}
/* PUT and POST are handled in the same way */
else if ((reqinfo.method == PUT) || (reqinfo.method == POST) )
{
/* Code to handle PUT and POST*/
}
else
{
/* not supported, code should never get here */
reqinfo.status = 501;
Return_Error_Msg(conn, &reqinfo);
}
/*Diag_Msg("Client Request: \r\n");
Diag_Msg(reqinfo.clientRequest);*/
/*
* the reqinfo.keep_alive flag will be set to 1 if the
* "Connection: Keep-Alive" header was sent by the client
*/
if(reqinfo.keep_alive == 0)
{
break;
}
reqinfo.keep_alive_max--;
if(reqinfo.keep_alive_max <= 0 )
{
/*
* the connection has been reused for the maxmum amount of times, stop
*/
break;
}
/*
* If we get here, we will clear the memory used for the client request
* and go to the beginning of the while loop to receive another request
*/
Writeline(conn,"\r\n",2);
FreeReqInfo(&reqinfo);
}
FreeReqInfo(&reqinfo);
return (retval);
}
Get_Request函数如下所示:
Req_Result Get_Request(int conn, struct ReqInfo * reqinfo) {
char buffer[MAX_REQ_LINE] = {0};
int rval;
fd_set fds;
struct timeval tv;
/* Set timeout to 5 seconds if this is the first request since the client connected, wait 5 seconds
* Otherwise, wait 5ms */
if(reqinfo->first_request == 1)
{
tv.tv_sec = 5;
tv.tv_usec = 0;
reqinfo->first_request = 0;
}
else
{
tv.tv_sec = reqinfo->keep_alive_timeout;
tv.tv_usec = 0;
}
/* Loop through request headers. If we have a simple request,
then we will loop only once. Otherwise, we will loop until
we receive a blank line which signifies the end of the headers,
or until select() times out, whichever is sooner. */
do {
/* Reset file descriptor set */
FD_ZERO(&fds);
FD_SET (conn, &fds);
/* Wait until the timeout to see if input is ready */
rval = select(conn + 1, &fds, NULL, NULL, &tv);
/* Take appropriate action based on return from select() */
if ( rval < 0 )
{
Diag_Msg("Error calling select() in get_request()");
return (ERROR_REQ);
}
else if ( rval == 0 ) {
/* input not ready after timeout */
return (TIMEOUT_REQ);
}
else {
/* We have an input line waiting, so retrieve it */
memset(buffer,0,MAX_REQ_LINE - 1);
if(Readline(conn, buffer, MAX_REQ_LINE - 1) == -1)
{
return (ERROR_REQ);
}
if(reqinfo->clientRequest == NULL)
{
reqinfo->clientRequest = calloc(MAX_REQ_LINE - 1, sizeof(char));
strncpy(reqinfo->clientRequest,buffer,MAX_REQ_LINE - 1);
}
else
{
strncat(reqinfo->clientRequest,buffer,MAX_REQ_LINE - 1);
}
Trim(buffer);
if ( buffer[0] == '\0' )
break;
if ( Parse_HTTP_Header(buffer, reqinfo) )
break;
}
} while ( reqinfo->type != SIMPLE );
return (GOT_REQ);
}
用英语描述此服务器的工作方式:服务器收到第一个请求。它解析标题,如果它找到“Connection:Keep-Alive”标题,它会设置一个标志。服务器继续处理此请求。一旦完成,它会检查保持活动标志。如果清除,服务器将关闭连接。如果设置,则服务器执行清理操作,并继续通过同一连接等待另一个请求。等等。
我用curl测试了这个:
Req_Result Get_Request(int conn, struct ReqInfo * reqinfo) {
char buffer[MAX_REQ_LINE] = {0};
int rval;
fd_set fds;
struct timeval tv;
/* Set timeout to 5 seconds if this is the first request since the client connected, wait 5 seconds
* Otherwise, wait 5ms */
if(reqinfo->first_request == 1)
{
tv.tv_sec = 5;
tv.tv_usec = 0;
reqinfo->first_request = 0;
}
else
{
tv.tv_sec = reqinfo->keep_alive_timeout;
tv.tv_usec = 0;
}
/* Loop through request headers. If we have a simple request,
then we will loop only once. Otherwise, we will loop until
we receive a blank line which signifies the end of the headers,
or until select() times out, whichever is sooner. */
do {
/* Reset file descriptor set */
FD_ZERO(&fds);
FD_SET (conn, &fds);
/* Wait until the timeout to see if input is ready */
rval = select(conn + 1, &fds, NULL, NULL, &tv);
/* Take appropriate action based on return from select() */
if ( rval < 0 )
{
Diag_Msg("Error calling select() in get_request()");
return (ERROR_REQ);
}
else if ( rval == 0 ) {
/* input not ready after timeout */
return (TIMEOUT_REQ);
}
else {
/* We have an input line waiting, so retrieve it */
memset(buffer,0,MAX_REQ_LINE - 1);
if(Readline(conn, buffer, MAX_REQ_LINE - 1) == -1)
{
return (ERROR_REQ);
}
if(reqinfo->clientRequest == NULL)
{
reqinfo->clientRequest = calloc(MAX_REQ_LINE - 1, sizeof(char));
strncpy(reqinfo->clientRequest,buffer,MAX_REQ_LINE - 1);
}
else
{
strncat(reqinfo->clientRequest,buffer,MAX_REQ_LINE - 1);
}
Trim(buffer);
if ( buffer[0] == '\0' )
break;
if ( Parse_HTTP_Header(buffer, reqinfo) )
break;
}
} while ( reqinfo->type != SIMPLE );
return (GOT_REQ);
}
正如你所看到的,curl说:连接#0似乎已经死了!在第一次请求完成后。然后它继续关闭连接并打开一个新连接。我确定我正确实现了HTTP 1.0 keep-alive功能。所以我的问题是:第一次请求完成后curl对连接的期望是什么?为什么它决定连接已经死了?
答案 0 :(得分:4)
我解决了。如果服务器回复HTTP / 1.1和“Content-Length:0”,curl将重用该连接。我的服务器回复看起来像这样
< HTTP/1.1 100 Continue
< HTTP/1.1 200 OK
< Server: DTSVU v0.1
< Content-Type: text/html
< Connection: Keep-Alive
< Keep-Alive: timeout=1, max=95
< Content-Length: 0
在第5次重用连接后。