c语言编写http服务器(Linux下运行)

发布时间:2023年12月20日

参考文章:https://blog.csdn.net/baixingyubxy/article/details/125964986?spm=1001.2014.3001.5506

上面是详细讲解,我这篇是总结了他的代码,因为他没给整体代码?

所有代码:


#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <signal.h>

#define MAXFD 1024
#define PATH "/home/hadoop/qimo/"

struct mess {
    int type;
    int c;
};

// 声明get_filename函数
char* get_filename(char buff[]);
int sockfd, msgid, epfd;

void sig_fun(int signo)
{
    printf("got a signal %d\n", signo);
}

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(8080);
    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;
}

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);//调用重置函数
    }
}

//添加描述符函数
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");
    }
}

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;
}

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_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;
}

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;
}

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); //向消息队列发送消息
                }
            }
        }
    }
}

?编译命令

我的源代码名字是 test1.c

?gcc -pthread test1.c -o test1

?修改内容

文件夹所有内容如下”

添加index.html

<html>
     <head>
         <meta charset=utf8>
          <title>baixingyu</title>
          </head>
      <body background="R-C.jpg">
             <center>
                 <h2>nazhanpeng--hhhhh</h2>
                 </center>

                <input style="width:300px;height:150px;text-align-center;font-size:30px"  type="text" placeholder="请输入用户名">
                <input  style="width:300px;height:150px;text-align-center;font-size:30px"  type="password" placeholder="请输入密码">
                <a href="test.html">下一页</a>

                </body>
      </html>

添加test.html

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

添加:err404.html?

 <html>
     <head>
          <meta charset=utf8>
          <title>访问失败</title>
          </head>
          <body background="1.jpg">
              <center>
                  <h2>页面走丢了</h2>
                  </center>
              </body>
</html>

?修改访问路径

改成你的存放源文件的目录

命令? pwd

?修改端口号

?查看端口号是否占用,如果有返回值就是占用,没有显示就是没有占用,被占用了自行修改就可以

sudo netstat -tuln | grep :8080

当前目录上传两张图片

1张改名为1.jpg

1张改名为R-C.jpg

mv? 原来的图片.jpg? ? 要改的名字.jpg

访问演示:

index.html

test.html

小bug:访问不存在的页面,没有出现err404.html

可以自行修改,

文章来源:https://blog.csdn.net/qq_62736318/article/details/135088115
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。