免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2995 | 回复: 8
打印 上一主题 下一主题

[C] epoll ET模式?求助 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2013-03-16 15:17 |只看该作者 |倒序浏览
本帖最后由 bio_tt 于 2013-03-16 15:53 编辑

各位咨询一下。 epoll ET模式,用来写web server,当用户A来请求时,得到A请求的网页A,这个时候如果A对应的socket可写,那么就往里面写,但是网页A很大,所以可能buffer满了,所以不能写了。要等下次socketA可写时,
再接着读取文件A,然后往socketA里面写。关键是文件A,以及已经发送的长度,是记录在哪的?

也就是说下一次socketA可写时,我怎么知道socketA对应的请求文件是A,而且已经发送了多少字节了?

我最近玩epoll,用的是非阻塞模式。。所以一个客户的socket不可写,就切换到可写的那一个进行操作,这样IO异步效率高。。但是是怎么记录请求文件,已经发送长度,和socket句柄的关系的?

论坛徽章:
4
水瓶座
日期:2013-09-06 12:27:30摩羯座
日期:2013-09-28 14:07:46处女座
日期:2013-10-24 14:25:01酉鸡
日期:2014-04-07 11:54:15
2 [报告]
发表于 2013-03-16 15:37 |只看该作者
本帖最后由 linux_c_py_php 于 2013-03-16 15:38 编辑

... ... 你还真是玩epoll...

1, 解析请求
2, 注册写事件
3, 触发写事件
4, 先判断buffer是否剩余, 是则send发送buffer.
5, 否则读文件, 写socket,写不出去的存buffer.

对于web server, 发送静态文件只是插件之一, 在应答http header阶段需要填写content-type/length, 在应答http body阶段则是读文件.

一个连接里要存储若干插件相关的信息, webserver框架层要在不同的阶段回调各个插件做事情.

论坛徽章:
0
3 [报告]
发表于 2013-03-16 15:49 |只看该作者
回复 2# linux_c_py_php


    大神,是不是可以把用户请求的文件名,和已经发送的长度,记录在events.data.ptr所指向的结构体里面?这样就关联起来了?

论坛徽章:
4
水瓶座
日期:2013-09-06 12:27:30摩羯座
日期:2013-09-28 14:07:46处女座
日期:2013-10-24 14:25:01酉鸡
日期:2014-04-07 11:54:15
4 [报告]
发表于 2013-03-16 15:53 |只看该作者
本帖最后由 linux_c_py_php 于 2013-03-16 15:57 编辑
bio_tt 发表于 2013-03-16 15:49
回复 2# linux_c_py_php


你每个连接起码有一个connection结构体来记录连接信息吧? 连接信息里再存文件信息.

这种读文件返回请求的流式慢处理服务, 必须依靠状态机+插件结构来设计才能写出来, 否则是很混乱的.


简单的讲, 你必须知道当前连接处于什么状态, 等请求还是等应答?

等请求就注册读事件, 等应答就取消读事件, 注册写事件.

这里你要做的就是解析到请求后进入等应答状态, 每次写事件触发, 你就读文件, 写一些东西出去(注意你的outbuf是否已有数据, 那么追加outbuf就可以了), 直到你完整的读完了文件, 那么你可以根据outbuf是否仍有待写数据决定是否取消写事件, 然后注册读事件, 重新进入等应答状态.

论坛徽章:
4
水瓶座
日期:2013-09-06 12:27:30摩羯座
日期:2013-09-28 14:07:46处女座
日期:2013-10-24 14:25:01酉鸡
日期:2014-04-07 11:54:15
5 [报告]
发表于 2013-03-16 15:58 |只看该作者
另外, ET别用了, 都是用LT的, ET不适用于状态机编程.

论坛徽章:
0
6 [报告]
发表于 2013-03-16 16:08 |只看该作者
本帖最后由 bio_tt 于 2013-03-16 16:10 编辑

回复 5# linux_c_py_php

其实看了很多帖子都麻木了。 学生物的学写代码太吃力了。 其实我只想实现这样一个简单的模式:


1)监听。。。
2)监听加入epoll
3)来新的连接,读取请求的内容,比如/index.html,然后搞一个结构体,记录用户请求的文件
struct user_data{
int fd;
int length;
char filename[1000];
}
同时将events.data.ptr指向这个结构体,那么下次就可以通过events.events,拿到这个文件名以及发送长度
4) 判断可写不,如果可写,就往客户写index.html内容,如果buffer满了,不可写,那么就就把结构体中的length修改一下,改为已经发送的长度。下次再可读时,就将文件指针lseek到length所对应的位置

5)当最终发送完成时,关掉句柄,free结构体。

这样的思路能用吗?
   

论坛徽章:
4
水瓶座
日期:2013-09-06 12:27:30摩羯座
日期:2013-09-28 14:07:46处女座
日期:2013-10-24 14:25:01酉鸡
日期:2014-04-07 11:54:15
7 [报告]
发表于 2013-03-16 16:11 |只看该作者
简单的东西不简单啊, 哥们, 你这个情况已经到了nginx的设计级别了, 只不过功能单一而已.

论坛徽章:
0
8 [报告]
发表于 2013-03-16 16:16 |只看该作者
本帖最后由 bio_tt 于 2013-03-16 16:17 编辑

回复 7# linux_c_py_php


    我记得上次还向你请教过epoll的问题呢。。但是有些专业术语,听不懂啊。。,所以一直在查资料,就是想知道怎么把发送的文件和长度记录下来和句柄关联起来。。一直没有找到啊。。所以一直没法往下写啊。。希望你能多说说。

也看了你发上来的几个server。感觉很强大啊。

PS:求大神联系方式,以后好多请教啊。感谢。

论坛徽章:
0
9 [报告]
发表于 2013-03-16 21:16 |只看该作者
回复 7# linux_c_py_php


    大神,我简单写了一个,发现效率不够高啊。。帮我看看结构有问题不。。谢谢了。

  代码如下:

#include "stdio.h"
#include "string.h"
#include "stdlib.h"
#include "sys/socket.h"
#include "netinet/in.h"
#include "sys/epoll.h"
#include "fcntl.h"
#include "sys/ioctl.h"
#include "errno.h"
#include "pthread.h"
#include "sys/stat.h"
#include <sys/types.h>
#include <dirent.h>
#include "signal.h"

struct user_data{
int fd;
int head;
long length;
char filename[5000];
};


int setnonblocking(int sock)
{

  int flag;
  fcntl(sock,F_GETFL,&flag);
  flag|=O_NONBLOCK;
  if(fcntl(sock,F_SETFL,&flag)== -1)
   {
     int on=1;
         if(ioctl(sock,FIONBIO,&on)==-1)
          {
             printf("SETFL ioctl error\n";
                 return 1;
          }
   }
   return 0;
}


void main(int argc,char *argv[])
{
  struct sigaction sa;
  sa.sa_handler=SIG_IGN;
  sigaction(SIGPIPE,&sa,0);   
  int len,nfds,opts,client_sockfd,listenfd;
  struct sockaddr_in server_addr,client_addr;
  listenfd=socket(AF_INET,SOCK_STREAM,0);
  setnonblocking(listenfd);
  bzero(&server_addr,sizeof(server_addr));
  server_addr.sin_family=AF_INET;
  server_addr.sin_port=htons(atoi(argv[1]));
  server_addr.sin_addr.s_addr=inet_addr("192.168.3.165";
  setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,1,sizeof(int));
  int nRecvBuf=32*1024;
  setsockopt(listenfd,SOL_SOCKET,SO_RCVBUF,(const char*)&nRecvBuf,sizeof(int));
  bind(listenfd,(struct sockaddr *)&server_addr,sizeof(server_addr));
  listen(listenfd,50);
  fork();
  fork();
  int epfd=epoll_create(500);
  struct epoll_event events[200];
  struct epoll_event ev1;
  ev1.data.fd=listenfd;
  ev1.events=EPOLLIN|EPOLLET;
  epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev1);
  int i;
  for(;
   {
     nfds=epoll_wait(epfd,events,20,500);
     for(i=0;i<nfds;i++)
       {
         if(events.data.fd== listenfd )
           {
              client_sockfd=accept(listenfd,NULL,NULL);
              int re=setnonblocking(client_sockfd);
              if(re == 1){close(client_sockfd);continue;}
              ev1.data.fd=client_sockfd;
              ev1.events=EPOLLIN|EPOLLET;
              epoll_ctl(epfd,EPOLL_CTL_ADD,client_sockfd,&ev1);
           }
         else if(events.events&EPOLLIN)
           {
               client_sockfd=events.data.fd;
                char last[5000]="",buf1[100];
                int len=0;
                int n,read_num;
                ioctl(client_sockfd,FIONREAD,&read_num);
                while(len < read_num)
                   {
                     if((n=read(client_sockfd,buf1,99))<0)
                        {
                           if(errno == ECONNRESET)
                              {
                               close(client_sockfd);
                                  break;
                              }
                           else if(errno == EAGAIN){
                                  break;
                              }
                        }
                     else if(n == 0)
                        {
                          break;
                        }
                      buf1[n]='\0';
                      len=len+n;
                      strcat(last,buf1);
                    }
               char cmd[200],arg[2048];
               strcpy(arg,"/tmp/taat";
               sscanf(last,"%s%s",cmd,arg+9);
               if(strcmp(arg,"/tmp/taat/"== 0)
               strcpy(arg,"/tmp/taat/index.html";
               if(strcmp(arg,"/tmp/taat"==0)
                           strcpy(arg,"/tmp/taat/index.html";
               struct user_data *new=(struct user_data *)malloc(sizeof(struct user_data));
               new->fd=client_sockfd;
               strcpy(new->filename,arg);
               new->length=0;
               new->head=0;
               ev1.data.fd=client_sockfd;
               ev1.data.ptr=new;   
               ev1.events=EPOLLOUT|EPOLLET;
               epoll_ctl(epfd,EPOLL_CTL_MOD,client_sockfd,&ev1);           
           }
         else if(events.events&EPOLLOUT)
           {
              struct user_data *old=(struct user_data *)events.data.ptr;
              int fd=old->fd;
              int len=old->length;
              char filename[1000];
              strcpy(filename,old->filename);
              //-----------------------------------            
              FILE *file;
              file=fopen(filename,"rb";
              if(file == NULL)
                 {
                  printf("file %s open failed\n",filename);
                  ev1.data.fd=fd;
                  epoll_ctl(epfd,EPOLL_CTL_DEL,fd,&ev1);close(fd);free(old);old=NULL;continue;
                 }
             if(old->head == 0)
              {
                            char type[100]="Content-Type: text/html";
              char *ta;
              ta=strrchr(filename,'.');
              if(ta == NULL)
               {
                              fclose(file);
                  printf("prefix %s get failed\n",filename);
                  ev1.data.fd=fd;
                  epoll_ctl(epfd,EPOLL_CTL_DEL,fd,&ev1);close(fd);free(old);old=NULL;continue;
               }
              ta=ta+1;
              if(strcmp(ta,"html" == 0)
              strcpy(type,"Content-Type: text/html");
              if(strcmp(ta,"txt") == 0)
              strcpy(type,"Content-Type: text/plain");
              if(strcmp(ta,"gif") == 0)
              strcpy(type,"Content-Type: text/gif");
              if(strcmp(ta,"jpg") == 0)
              strcpy(type,"Content-Type: text/jpg");
              if(strcmp(ta,"png") == 0)
              strcpy(type,"Content-Type: text/png");
                char content[200];
                sprintf(content,"HTTP/1.1 200 OK\r\n%s\r\n\r\n",type);
                if(write(fd,content,sizeof(char)*strlen(content))<0)
                  {
                                    fclose(file);
                    printf("write content error\n");
                    ev1.data.fd=fd;epoll_ctl(epfd,EPOLL_CTL_DEL,fd,&ev1);close(fd);free(old);old=NULL;continue;
                  }
                old->head=1;
              }
            fseek(file,len,SEEK_SET);
            int num;
             char buf[5000];
             while((num=fread(buf,sizeof(char),4999,file))>0)
               {
                buf[num]='\0';
                int num1;
                num1=write(fd,buf,num);
                if (num1 < num)
                   {
                      if (num1 == -1 && errno != EAGAIN)
                        { perror("write error"); }
                      break;
                   }
               }
             long curpos=ftell(file);
             fseek(file, 0L, SEEK_END);
             long all_len = ftell(file);
             if(curpos == all_len)
              {
                 printf("file %s read and send over\n",filename);
                 fclose(file);
                 ev1.data.fd=fd;epoll_ctl(epfd,EPOLL_CTL_DEL,fd,&ev1);close(fd);free(old);old=NULL;continue;
              }
             else{
                printf("file %s read and send not over\n",filename);
                  old->length=curpos;
                                  fclose(file);
              }
             //-------------------------------------
          }
       }

   }

}

而且我用ab测试的时候fcntl有很多失败呢。。求指导效率改进。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP