问题描述
我正在编写一个涉及epoll和多线程的小型Web服务器。对于简短的http / 1.1请求和响应,它可以按预期工作。但是,当处理大型文件下载时,它总是被我设计的计时器打断。我使用固定的超时值使计时器到期,但是我还有一条if语句来检查响应是否成功发送。
static void
_expire_timers(list_t *timers,long timeout)
{
httpconn_t *conn;
int sockfd;
node_t *timer;
long cur_time;
long stamp;
timer = list_first(timers);
if (timer) {
cur_time = mstime();
do {
stamp = list_node_stamp(timer);
conn = (httpconn_t *)list_node_data(timer);
if ((cur_time - stamp >= timeout) && httpconn_close(conn)) {
sockfd = httpconn_sockfd(conn);
DEBSI("[CONN] socket closed,server disconnected",sockfd);
close(sockfd);
list_del(timers,stamp);
}
timer = list_next(timers);
} while (timer);
}
}
我意识到在非阻塞环境中,write()函数可能在请求-响应通信期间被中断。我不知道write()可以保存多长时间,或者write()可以发送多少数据,因此我可以将代码中的timout设置缩短一周。
这是涉及write()的代码,
void
http_rep_get(int clifd,void *cache,char *path,void *req)
{
httpmsg_t *rep;
int len_msg;
char *bytes;
rep = _get_rep_msg((list_t *)cache,path,req);
bytes = msg_create_rep(rep,&len_msg);
/* send msg */
DEBSI("[REP] Sending reply msg...",clifd);
write(clifd,bytes,len_msg);
/* send body */
DEBSI("[REP] Sending body...",msg_body_start(rep),msg_body_len(rep));
free(bytes);
msg_destroy(rep,0);
}
以下是我用来处理传入请求的epoll循环,
do {
nevents = epoll_wait(epfd,events,MAXEVENTS,HTTP_KEEPALIVE_TIME);
if (nevents == -1) perror("epoll_wait()");
/* expire the timers */
_expire_timers(timers,HTTP_KEEPALIVE_TIME);
/* loop through events */
for (i = 0; i < nevents; i++) {
conn = (httpconn_t *)events[i].data.ptr;
sockfd = httpconn_sockfd(conn);
/* error case */
if ((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP) ||
(!(events[i].events & EPOLLIN))) {
perror("EPOLL ERR|HUP");
list_update(timers,conn,mstime());
break;
}
else if (sockfd == srvfd) {
_receive_conn(srvfd,epfd,cache,timers);
}
else {
/* client socket; read client data and process it */
thpool_add_task(taskpool,httpconn_task,conn);
}
}
} while (svc_running);
http_rep_get()由线程池处理程序httpconn_task()执行,HTTP_KEEPALIVE_TIME是固定超时。一旦请求到达,处理程序httpconn_task()将在计时器中添加一个计时器。由于write()是在http_rep_get()中执行的,因此我认为它可能会被计时器打断。我想我可以更改写给客户端的方式,但是我需要确保write()可以做什么。
如果您有兴趣,可以浏览我的项目来帮助我。 https://github.com/grassroot72/Maestro
干杯, 爱德华
解决方法
套接字fd是否有write()的大小限制?
这取决于您所说的极限。
正如注释所解释的,T.insert(tk.END,abc_clean)
调用可能写入的字节数少于您要求的字节数。此外,如果对套接字执行大写操作,则这是预期的行为。但是,没有可靠的方法来确定(或预测)在调用write
之前要写入多少字节。
处理此问题的正确方法是检查每次实际写入了多少个字节,并使用循环来确保所有字节均被写入(或直到失败)。