C语言实现http服务器(Linux)_linux c http

四、代码实现及运行结果

4.1主要功能实现

4.1.1 主函数

主函数中主要调用各个封装好的方法函数,首先调用创建套接字函数,创建套接字,然后创建消息队列。接着创建线程池,子线程同时执行loop_thread线程函数,将在 msgrcv处阻塞,等待获取消息队列中的消息。主线程调用epoll_create方法创建内核事件表,调用epoll_add函数添加描述符和事件。接着使用epoll_wait方法获取就绪描述符,一旦获取到就绪描述符便向消息队列中发送消息,便可以解除子线程中消息队列的阻塞,执行子线程中的程序,连接客户端,实现通信。

int main()
{
    signal(SIGPIPE,sig_fun);
    sockfd = socket\_init();//调用创建套接字函数
    if ( sockfd == -1 )
    {
        exit(0);
    }
    msgid = msgget((key\_t)1234,IPC_CREAT|0600);//创建消息队列
    if ( msgid == -1 )
    {
        exit(0);
    }
    pthread\_t id[4];
    for( int i = 0; i < 4; i++ )    //循环创建线程池
    {
        pthread\_create(&id[i],NULL,loop_thread,NULL);
    }    
    epfd = epoll\_create(MAXFD);//创建内核事件表
    if ( epfd == -1 )
    {
        printf("create epoll err\n");
        exit(0);
}
epoll\_add(epfd,sockfd);//调用封装的函数添加描述符和事件
  struct epoll\_event evs[MAXFD];
    while( 1 )
    {
        int n = epoll\_wait(epfd,evs,MAXFD,-1);//获取就绪描述符
        if( n == -1 )
        {
            continue;
        }
else    
        {
            struct mess m;
            m.type = 1;
            for(int i = 0; i < n; i++ )
            {
                m.c = evs[i].data.fd;
                if ( evs[i].events & EPOLLIN )
                {
                    msgsnd(msgid,&m,sizeof(int),0); //向消息队列发送消息
                }
            }
        }
    }
}

4.1.2创建套接字函数

将初始化创建套接字函数封装。

int socket\_init()
{
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    if ( sockfd == -1 )
    {
        return -1;
    }
    struct sockaddr\_in saddr;
    memset(&saddr,0,sizeof(saddr));
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(80);
    saddr.sin_addr.s_addr = inet\_addr("0.0.0.0");
int res = bind(sockfd,(struct sockaddr\*)&saddr,sizeof(saddr));
 if ( res == -1 )
    {
        printf("bind err\n");
        return -1;
    }
    res = listen(sockfd,5);
    if ( res == -1 )
    {
        return -1;
    }
    return sockfd;
}

4.1.3线程函数

线程将在 msgrcv处阻塞,等待获取消息队列中的消息,判断描述符类型,进行accept操作或recv操作。收到客户端请求数据后再调用get_filename函数获取客户端请求的资源名称,再判断发送错误或者正确应答报文。

 void\* loop\_thread(void\*  arg)
{
    while( 1 )
    {
        struct mess m;
        msgrcv(msgid,&m,sizeof(int),1,0);//从消息队列中读取消息
        int c = m.c;
        if ( c == sockfd )
        {
            struct sockaddr\_in caddr;
            int len = sizeof(caddr);
            int cli = accept(sockfd,(struct sockaddr\*)&caddr,&len);
            if ( cli < 0 )
            {
                continue;
            }
            epoll\_add(epfd,cli);
        }
        else
        {
            char buff[1024] = {0};
            int n = recv(c,buff,1023,0);
             if ( n <= 0 )
            {
                epoll\_del(epfd,c);//调用移除描述符函数
                close(c);
                printf("close\n");
                continue;
            }
            char\* filename = get\_filename(buff);//调用资源名获取函数
            if ( filename == NULL )
            {
                send\_404status(c);//调用发送错误应答报文函数
                epoll\_del(epfd,c);//调用移除描述符函数
                close(c);
                continue;
            }
            printf("filename:%s\n",filename);

            if ( send\_httpfile(c,filename) == -1 )//调用发送正确应答报文函数
            {
                printf("主动关闭连接\n");
                epoll\_del(epfd,c);
                close(c);
                continue;
            }
        }
        epoll\_mod(epfd,c);//调用重置函数
    }
}

4.1.4封装epoll函数
//添加描述符函数
void epoll\_add(int epfd,int fd)
{
    struct epoll\_event ev;
    ev.data.fd = fd;
    ev.events = EPOLLIN | EPOLLONESHOT;

    if ( epoll\_ctl(epfd,EPOLL_CTL_ADD,fd,&ev) == -1)
    {
        printf("epoll add err\n");
    }
}
//移除描述符函数
void epoll\_del(int epfd, int fd )
{
    if ( epoll\_ctl(epfd,EPOLL_CTL_DEL,fd,NULL) == -1 )
    {
        printf("epoll del err\n");
    }
}
//重置描述符函数
void epoll\_mod(int epfd, int fd)
{
    struct epoll\_event ev;
    ev.data.fd = fd;
    ev.events = EPOLLIN | EPOLLONESHOT;
    if ( epoll\_ctl(epfd,EPOLL_CTL_MOD,fd,&ev) == -1 )
    {
        printf("epoll mod err\n");
    }
}

4.1.5获取资源名函数

通过这个函数来解析客户端请求报文,获取资源名称。

char\* get\_filename(char buff[])
{   char\* ptr = NULL;
    char \* s = strtok\_r(buff," ",&ptr);
    if ( s == NULL )
    {
        printf("请求报文错误\n");
        return NULL;
    }
    printf("请求方法:%s\n",s);
    s = strtok\_r(NULL," ",&ptr);
    if ( s == NULL )
    {
        printf("请求报文 无资源名字\n");
        return NULL;
    }
    if ( strcmp(s,"/") == 0 )
    {
        return "/index.html";
    }
    return s;
}

4.1.6发送正确应答报文函数

如果客户请求资源可以正常访问,则调用该函数发送应答报文。

int  send\_httpfile(int c, char\* filename)
{
    if ( filename == NULL || c < 0 )
    {
        send(c,"err",3,0);
        return -1 ;
    }

    char path[128] = {PATH};
    strcat(path,filename);// /home/ubuntu/ligong/day12/index.hmtl
    int fd = open(path,O_RDONLY);
    if ( fd == -1 )
    {
        //send(c,"404",3,0);
        send\_404status(c);
        return -1;
    }

    int size = lseek(fd,0,SEEK\_END);
    lseek(fd,0,SEEK\_SET);
    char head_buff[512] = {"HTTP/1.1 200 OK\r\n"};
    strcat(head_buff,"Server: myhttp\r\n");
    sprintf(head_buff+strlen(head_buff),"Content-Length: %d\r\n",size);
    strcat(head_buff,"\r\n");//分隔报头和数据 空行
    send(c,head_buff,strlen(head_buff),0);
    printf("send file:\n%s\n",head_buff);

    int num = 0;
    char data[1024] = {0};
    while( ( num = read(fd,data,1024)) > 0 )
    {
        send(c,data,num,0);
    }
    close(fd);

    return 0;
}

4.1.7发送错误应答报文函数

如果客户端访问的在服务器端资源不存在,则调用该函数发送应答
报文。

int  send\_404status(int c)
{
    int fd = open("err404.html",O_RDONLY);
    if ( fd == -1 )
    {
        send(c,"404",3,0);
        return 0;
    }

    int size = lseek(fd,0,SEEK\_END);
    lseek(fd,0,SEEK\_SET);
    char head_buff[512] = {"HTTP/1.1 404 Not Found\r\n"};
    strcat(head_buff,"Server: myhttp\r\n");
    sprintf(head_buff+strlen(head_buff),"Content-Length: %d\r\n",size);
    strcat(head_buff,"\r\n");//分隔报头和数据 空行
    send(c,head_buff,strlen(head_buff),0);

    char data[1024] = {0};
    int num = 0;
    while( ( num = read(fd,data,1024)) > 0 )
    {
        send(c,data,num,0);
    }
    close(fd);
    return 0;
}

4.1.8 index.htlm
<html>
     <head>
         <meta charset=utf8>
          <title>baixingyu</title>
          </head>
      <body background="R-C.jpg">
             <center>
                 <h2>bxy</h2>
                 </center>
                <a href="test.html">下一页</a>
                </body>
      </html>

4.1.9 test.html
 <html>
      <head>
         <meta charset=utf8>
         <title>测试</title>
          </head>
         <body>
             <center>
                 <h2>小狗小狗
                 </center>
                   <a href="index.html">返回</a>
              </body>
 </html>

4.1.10 404err.html
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值