Thttpd源程序解析20 请求回送过程详解

此文章之前讲的请求处理过程说明请求处理过程校验请求格式、分析请求内容、处理请求将数据存储在缓存数组中并将此连接的文件描述符的状态设置为发送状态添加在管理数组中,当程序循环执行到检测此文件的描述符时检测到此文件的状态为发送状态将会执行handle_send函数,此函数的作用是确定发送数据的数据量并发送给客户端,根据发送的状态做出相应的处理。

handle_send函数

(1)根据是否设置限制最大发送数据的数据量进行设置最大发送数据的数据量,没有设置发送数据限制设置最大发送数据字节数为1000000000字节,反之设置最大发送字节数为250000000字节。

(2)根据hc->responselen的值是否为0判断是只发送响应头文件还是发送响应头文件和内容文件。

(3)根据写数据的结果进行处理如果写数据失败但是错误值为EINTR退出函数,如果写数据失败但是错误值为EWOULDBLOCK或者是EAGAIN在文件数组中删除此文件描述符,然后调用定时函数进行重新处理发送函数退出函数。如果为其他错误关闭连接退出函数。

(4)对于hc->responselen的值不为0的,但是发送的数据小于hc->responselen的值的更新发送数据的长度和下一次发送数据的位置。

(5)更新需要发送数据的开始位置和截止位置。

(6)对于数据完全被发送完的关闭连接退出函数。

(7)对于数据没有发送完的如果延迟时间大于最小延迟时间的设置延迟时间为延迟时间减去最小延迟时间。

(8)如果限制最大发送数据但是每秒发送的数据大于设置的最大发送限制的进行发送数据的网络速度限制。

流程图

源程序

static void handle_send( connecttab* c, struct timeval* tvP ) { size_t max_bytes; int sz, coast; ClientData client_data; time_t elapsed; httpd_conn* hc = c->hc; int tind; /**对于没有限制的处理*/ if ( c->max_limit == THROTTLE_NOLIMIT ) { max_bytes = 1000000000L; } /**对于有限制的处理*/ else { max_bytes = c->max_limit / 4; /* send at most 1/4 seconds worth */ } /* Do we need to write the headers first? */ /**对于hc->responselen的值为0的处理,即没有数据的处理*/ if ( hc->responselen == 0 ) { /* No, just write the file. */ sz = write(hc->conn_fd, &(hc->file_address[c->next_byte_index]),MIN( c->end_byte_index - c->next_byte_index, max_bytes ) ); } /**对于hc->responselen的值不为0的处理*/ else { /* Yes. We'll combine headers and file into a single writev(), ** hoping that this generates a single packet. */ /**一次函数对象*/ struct iovec iv[2]; iv[0].iov_base = hc->response; iv[0].iov_len = hc->responselen; iv[1].iov_base = &(hc->file_address[c->next_byte_index]); iv[1].iov_len = MIN( c->end_byte_index - c->next_byte_index, max_bytes ); /**一次写两个数据块的数据*/ sz = writev( hc->conn_fd, iv, 2 ); } /**对于写数据错误的处理但是错误的值为eintr的处理*/ if ( sz < 0 && errno == EINTR ) { return; } /***/ if ( sz == 0 ||( sz < 0 && ( errno == EWOULDBLOCK || errno == EAGAIN ) ) ) { /* This shouldn't happen, but some kernels, e.g. ** SunOS 4.1.x, are broken and select() says that ** O_NDELAY sockets are always writable even when ** they're actually not. ** ** Current workaround is to block sending on this ** socket for a brief adaptively-tuned period. ** Fortunately we already have all the necessary ** blocking code, for use with throttling. */ c->wouldblock_delay += MIN_WOULDBLOCK_DELAY; c->conn_state = CNST_PAUSING; fdwatch_del_fd( hc->conn_fd ); client_data.p = c; if ( c->wakeup_timer != (Timer*) 0 ) { syslog( LOG_ERR, "replacing non-null wakeup_timer!" ); } c->wakeup_timer = tmr_create(tvP, wakeup_connection, client_data, c->wouldblock_delay, 0 ); if ( c->wakeup_timer == (Timer*) 0 ) { syslog( LOG_CRIT, "tmr_create(wakeup_connection) failed" ); exit( 1 ); } return; } /**对于写失败的处理*/ if ( sz < 0 ) { /* Something went wrong, close this connection. ** ** If it's just an EPIPE, don't bother logging, that ** just means the client hung up on us. ** ** On some systems, write() occasionally gives an EINVAL. ** Dunno why, something to do with the socket going ** bad. Anyway, we don't log those either. ** ** And ECONNRESET isn't interesting either. */ if ( errno != EPIPE && errno != EINVAL && errno != ECONNRESET ) { syslog( LOG_ERR, "write - %m sending %.80s", hc->encodedurl ); } clear_connection( c, tvP ); return; } /* Ok, we wrote something. */ c->active_at = tvP->tv_sec; /* Was this a headers + file writev()? */ /**对于调用写的方式不同的处理*/ if ( hc->responselen > 0 ) { /* Yes; did we write only part of the headers? */ if ( sz < hc->responselen ) { /* Yes; move the unwritten part to the front of the buffer. */ int newlen = hc->responselen - sz; (void) memmove( hc->response, &(hc->response[sz]), newlen ); hc->responselen = newlen; sz = 0; } else { /* Nope, we wrote the full headers, so adjust accordingly. */ sz -= hc->responselen; hc->responselen = 0; } } /* And update how much of the file we wrote. */ /**更新当前已经写的数据的数量*/ c->next_byte_index += sz; c->hc->bytes_sent += sz; for ( tind = 0; tind < c->numtnums; ++tind ) { throttles[c->tnums[tind]].bytes_since_avg += sz; } /* Are we done? */ /**对于已经写完数据的处理*/ if ( c->next_byte_index >= c->end_byte_index ) { /* This connection is finished! */ finish_connection( c, tvP ); return; } /* Tune the (blockheaded) wouldblock delay. */ /**对于没有写完数据的处理*/ if ( c->wouldblock_delay > MIN_WOULDBLOCK_DELAY ) { c->wouldblock_delay -= MIN_WOULDBLOCK_DELAY; } /* If we're throttling, check if we're sending too fast. */ if ( c->max_limit != THROTTLE_NOLIMIT ) { elapsed = tvP->tv_sec - c->started_at; if ( elapsed == 0 ) { elapsed = 1; /* count at least one second */ } if ( c->hc->bytes_sent / elapsed > c->max_limit ) { c->conn_state = CNST_PAUSING; fdwatch_del_fd( hc->conn_fd ); /* How long should we wait to get back on schedule? If less ** than a second (integer math rounding), use 1/2 second. */ coast = c->hc->bytes_sent / c->max_limit - elapsed; client_data.p = c; if ( c->wakeup_timer != (Timer*) 0 ) { syslog( LOG_ERR, "replacing non-null wakeup_timer!" ); } c->wakeup_timer = tmr_create(tvP, wakeup_connection, client_data,coast > 0 ? ( coast * 1000L ) : 500L, 0 ); if ( c->wakeup_timer == (Timer*) 0 ) { syslog( LOG_CRIT, "tmr_create(wakeup_connection) failed" ); exit( 1 ); } } } /* (No check on min_limit here, that only controls connection startups.) */ }