网页小型服务初步

网页小型服务初步

函数入口

1、设置端口

2、进程执行目录

3、start()

start()

利用epoll多路I/O复用

epoll深度理解

一般而言,由硬件产生的信号需要 CPU 立马做出回应,不然数据可能就丢失了,所以它的优先级很高。

CPU 理应中断掉正在执行的程序,去做出响应;当 CPU 完成对硬件的响应后,再重新执行用户程序。

它和函数调用差不多,只不过函数调用是事先定好位置,而中断的位置由“信号”决定。

中断程序调用

以键盘为例,当用户按下键盘某个按键时,键盘会给 CPU 的中断引脚发出一个高电平,CPU 能够捕获这个信号,然后执行键盘中断程序。

进程阻塞为什么不占用 CPU 资源?

  • 操作系统如何知道网络数据对应于哪个 Socket?

  • 如何同时监视多个 Socket 的数据?

    所谓唤起进程,就是将进程从所有的等待队列中移除,加入到工作队列里面

跑题了:

通过一个轮询处理i/o事件

void *epoll_run(int port)
{
	int i = 0;
    struct epoll_event all_events[MAXSIZE];

    // 创建一个epoll监听树根
    int epfd = epoll_create(MAXSIZE);
    if (epfd == -1) { 
        perror("epoll_create error");
        exit(1);
    }
    
    // 创建lfd,并添加至监听树
    int lfd = init_listen_fd(port, epfd);
   
    while (1) {
    	// 监听节点对应事件
        int ret = epoll_wait(epfd, all_events, MAXSIZE, -1);
        if (ret == -1) {      
            perror("epoll_wait error");
            exit(1);
        }

        for (i=0; i<ret; ++i) {    
            // 只处理读事件, 其他事件默认不处理
            struct epoll_event *pev = &all_events[i];
            
            // 不是读事件
            if (!(pev->events & EPOLLIN)) {                     
                continue;
            }
            if (pev->data.fd == lfd) {   	// 接受连接请求   
                
                do_accept(lfd, epfd);
                
            } else {						// 读数据
                
                do_read(pev->data.fd, epfd);
            }
        }
    }
}

定义读事件初步理解http协议

void do_read(int cfd, int epfd)
{
	// 读 http 请求协议,获取一行,得文件名。
	char line[1024] = {0};
	int ret, n;
	ret = get_line(cfd, line, sizeof(line));
	if (ret == 0) {
		printf("检测到客户端关闭...\n");
		disconnect(cfd, epfd);	
	} else {		// 读到 http 请求协议第一行
		// 循环 读取剩余的 http 请求协议数据。
		while (1) {
			char buf[1024] = {0};
			n = get_line(cfd, buf, sizeof(buf));
			if (n == -1)
				break;
			if (buf[0]=='\n')
				break;
		}
	}
	// 判断http协议头,拆分 方法、文件名、协议号 -- "GET /hello.c HTTP/1.1"
	if (strncasecmp("GET", line, 3) == 0) {
		// 拆分, 获取文件名
		printf("-----%s\n", line);
		char method[16] = {0}, path[256] = {0}, protocol[16]={0};
		 
		sscanf(line, "%[^ ] %[^ ] %[^ ]", method, path, protocol);
		
		// 处理 浏览器请求文件
		http_request(cfd, path+1);
	}
}

处理http请求

void http_request(int cfd, const char *file)
{
	// 判断文件是否存在。
	struct stat sbuf;
	int ret = stat(file, &sbuf);
	if (ret == -1) {
		// 回发 给 浏览器 404 错误页面	
		perror("stat error");
		exit(1);
	} 
	
	// 判断文件类型 --- 普通文件。
	if (S_ISREG(sbuf.st_mode)) {
		
		// 组织http应答协议,发送。 错误号、错误描述、错误类型、文件大小
		//send_http_respond(cfd, 200, "ok", "text/plain; charset=iso-8859-1", sbuf.st_size);
		send_http_respond(cfd, 200, "ok", "text/html; charset=iso-8859-1", sbuf.st_size);
		//send_http_respond(cfd, 200, "ok", "image/jpeg", sbuf.st_size);
		
		// 打开文件发送文件内容。
		send_file(cfd, file);
	}	
}

回发

void send_http_respond(int cfd, int num, const char*desp, const char *type, long size)
{
	char buf[4096] = {0};
	sprintf(buf, "%s %d %s\r\n", "HTTP/1.1", num, desp);	
	sprintf(buf+strlen(buf), "%s%s\r\n", "Content-Type:", type);
	sprintf(buf+strlen(buf), "%s%ld\r\n", "Content-Length:", size);
	sprintf(buf+strlen(buf), "\r\n");
	// printf("写回的http应答:|\n%s|\n", buf);
	
	// 将组织好的 http 应答头,发送给浏览器
	int ret = send(cfd, buf, strlen(buf), 0);
	if (ret == -1) {
		perror("send_http_respond send error");
		exit(1);
	}
}

发送文件内容

void send_file(int cfd, const char*file)
{
	int n = 0;
	char buf[4096] = {0};
	int fd = open(file, O_RDONLY);
	if (fd == -1) {
		perror("open error");	
		exit(1);
	}
	// 循环从,本地文件读取数据,写入套接字给浏览器
	while ((n = read(fd, buf, sizeof(buf))) > 0) {
		send(cfd, buf, n, 0);
	}
	close(fd);	
}

处理连接请求

void do_accept(int lfd, int epfd)
{
	struct sockaddr_in clt_addr;
    socklen_t clt_addr_len = sizeof(clt_addr);
    
    int cfd = accept(lfd, (struct sockaddr*)&clt_addr, &clt_addr_len);
    if (cfd == -1) {   
        perror("accept error");
        exit(1);
    }

    // 打印客户端IP+port
    char client_ip[64] = {0};
    printf("New Client IP: %s, Port: %d, cfd = %d\n",
           inet_ntop(AF_INET, &clt_addr.sin_addr.s_addr, client_ip, sizeof(client_ip)),
           ntohs(clt_addr.sin_port), cfd);

    // 设置 cfd 非阻塞
    int flag = fcntl(cfd, F_GETFL);
    flag |= O_NONBLOCK;
    fcntl(cfd, F_SETFL, flag);

    // 将新节点cfd 挂到 epoll 监听树上
    struct epoll_event ev;
    ev.data.fd = cfd;
    
    // 边沿非阻塞模式
    ev.events = EPOLLIN | EPOLLET;
    int ret = epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &ev);
    if (ret == -1)  {
        perror("epoll_ctl add cfd error");
        exit(1);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值