基於epoll實現的一個簡單的web服務器

 本文使用epoll創建一個簡單的web服務器。經過epoll實現對過個端口的管理,非阻塞的數據發送。

使用到數據結構  


typedef union epoll_data {
                void *ptr;
                int fd;         /*與之關聯的處理的socket文件句柄*/
                __uint32_t u32;
                __uint64_t u64;
        } epoll_data_t;
       
        /*設置fd對應的epoll的管理事件*/
        struct epoll_event {
                __uint32_t events;      /*設置epoll能夠處理的事件。見 詳解1 ,能夠用‘|' 將多個事件連接起來*/
                epoll_data_t data;      /*與之關聯的句柄數據,見上一個結構體*/
        };

詳解1
EPOLLIN:文件描述符能夠讀
      EPOLLOUT:文件描述符能夠寫
      EPOLLPRI:文件描述符有緊急的數據可寫
      EPOLLERR:文件描述符發生錯誤
      EPOLLHUP:文件描述符被掛起
      EPOLLET: 文件描述符有事件發生

所用到的函數: html

一、int epoll_create(int size) web

該函數生成一個epoll專用的文件描述符,其中的參數是指定生成描述符的最大範圍(在咱們作網絡開發中應該是能夠管理的端口的個數。就是能夠接受連接的socket的個數)。 ubuntu


二、int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) 數組

該函數用於控制某個文件描述符上的事件,能夠註冊事件,修改事件,刪除事件。 服務器

參數:epfd:由 epoll_create 生成的epoll專用的文件描述符; 網絡

op:要進行的操做例如註冊事件,可能的取值EPOLL_CTL_ADD 註冊、EPOLL_CTL_MOD 修改、EPOLL_CTL_DEL 刪除 數據結構

fd:關聯的socket文件描述符; socket

        event:指向epoll_event的指針,根據op的值對fd管理的事件進行操做,更改的事件在event中存放; 函數

        若是調用成功返回0,不成功返回-1 網站


三、int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout)

該函數用於輪詢I/O事件的發生;

參數:

epfd:由epoll_create 生成的epoll專用的文件描述符;

epoll_event:用於回傳代處理事件的數組,即便將全部知足條件的socket文件描述對應的epoll_event中條件的epoll_event放到events數組中 ;

maxevents:每次能處理的事件數的最大值;

timeout:等待I/O事件發生的超時值;

返回發生事件數。


下面是一個例子:(本程序是在ubuntu12.04中運行成功,若是在別的系統上沒法運行請諒解)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <sys/epoll.h>
 
/*設這文件句柄sfd爲非阻塞
*
*/
static int make_socket_non_blocking (int sfd)
{
    int flags, s;
    flags = fcntl (sfd, F_GETFL, 0);
    if (flags == -1)
    {
        perror ("fcntl");
        return -1;
    }
 
    flags |= O_NONBLOCK;
    s = fcntl (sfd, F_SETFL, flags);
    if (s == -1)
    {
        perror ("fcntl");
        return -1;
    }
 
    return 0;
}
 
/*建立epoll管理最大文件句柄的個數
*parameter
* @maxfds:最大的句柄個數。也就是網站數目。端口的數目
*/
int epoll_init(int maxfds)
{
    return epoll_create(maxfds);
}
 
/*設這epoll管理每一個文件句柄的參數和方法
*parameter
* @fd:要管理socket文件句柄
* @maxfds:管理socket文件句柄的個數
*/
static struct epoll_event * epoll_event_init(int * fd, int maxfds)
{
    struct epoll_event *events;
    int i = 0;
    if(0 >= maxfds || NULL ==fd) return NULL;
    events = (struct epoll_event *)malloc( sizeof(struct epoll_event) * maxfds);
    for(; i < maxfds; i++)
    {
        events[i].data.fd = fd[i];
        events[i].events = EPOLLIN | EPOLLET;
    }
    return events;
}
 
 
int epoll_handler(int epfd, int * fd, int maxfds)
{
    struct epoll_event *events = epoll_event_init(fd, maxfds);
    struct epoll_event *ev = events;
    int i = 0;
    for(; i < maxfds; i++)
    {
        epoll_ctl(epfd, EPOLL_CTL_ADD, fd[i], ev);
        ev++;
    }
    
}
 
 
/*  
@description:開始服務端監聽
@parameter
ip:web服務器的地址
port:web服務器的端口
@result:成功返回建立socket套接字標識,錯誤返回-1
 
*/
int socket_listen( char *ip, unsigned short int port)
{
    int res_socket; //返回值
    int res, on;
    struct sockaddr_in address;
    struct in_addr in_ip;
    res = res_socket = socket(AF_INET, SOCK_STREAM, 0);
    setsockopt(res_socket, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
    memset(&address, 0, sizeof(address));
    address.sin_family = AF_INET ;
    address.sin_port =htons(port);
    address.sin_addr.s_addr = htonl(INADDR_ANY); //inet_addr("127.0.0.1");
    res = bind( res_socket, (struct sockaddr *) &address, sizeof( address ) );
    if(res) { printf( "port is used , not to repeat bind\n" ); exit(101); };
    res = listen(res_socket,5);
    if(res) { printf( "listen port is error ;\n" ); exit( 102 );  };
    return res_socket ;
}
 
 
void send_http1(int conn_socket)
{  
    char *send_buf = "HTTP/1.1 200 OK\r\nServer: Reage webserver\r\nContent-Type: text/html\r\nConnection: close\r\n\r\n<!DOCTYPE html><html><head><title>epoll learn</title></head><body><h1>Reage Test111111</h1>Hello World! </body></html>\r\n\r\n";
    write(conn_socket, send_buf, strlen(send_buf));
}

void send_http2(int conn_socket)
{  
    char *send_buf = "HTTP/1.1 200 OK\r\nServer: Reage webserver\r\nContent-Type: text/html\r\nConnection: close\r\n\r\n<!DOCTYPE html><html><head><title>epoll learn</title></head><body><h1>Reage Test22222</h1>Hello World! </body></html>\r\n\r\n";
    write(conn_socket, send_buf, strlen(send_buf));
}
 
 
 
int main(int argc, char * argv[] )
{   
    int  res_socket[2];
    struct epoll_event event[100];
    struct sockaddr_in client_addr;
    int len;
    res_socket[0] = socket_listen( "127.0.0.1", 1025) ;
    res_socket[1] = socket_listen( "127.0.0.1", 1024) ;
    make_socket_non_blocking(res_socket[0]);
    make_socket_non_blocking(res_socket[1]);
    int epfd = epoll_init(2);
    epoll_handler(epfd, res_socket, 2);
    while(1)
    {
        int count = epoll_wait(epfd, event, 2, -1);
        while(count--)
        {
            sleep(10);
            int connfd = accept(event[count].data.fd, (struct sockaddr *)&client_addr, &len);
          //針對不一樣的端口,便是不一樣的網站進行不一樣的處理
           if( event[count].data.fd == res_socket[0])            
                send_http1(connfd);
            else
                send_http2(connfd);
            close(connfd);
        }
      }  
 
}
blogs :http://blog.csdn.net/rentiansheng/article/details/8684194
相關文章
相關標籤/搜索