网络 http服务器-v2-epoll版本

epoll的基本接口与建立tcp连接的流程 查看:

网络 使用epoll 实现TCP服务器 - 初出茅庐小菜鸟 - 51CTO技术博客

http://shaungqiran.blog.51cto.com/10532904/1784410

重点:

        epoll 遵循的是多路复用的 I/O模型。其内部只实现了对 关注I/O事件的监听,而没有实现具体的操作。因此我们必须自己选择恰当的时机去 读  或者 写。而对于内核而言,对一个socket 文件描述符 的读 、写 事件的监听 是分离的(可读,未必可写)。因此 ,要想达到最高的效率。用户 做的 I/O操作相应的也应该读、写分离。

        而如果读写分离的话,对于HTTP服务器而言,server 端 提供的服务有时是需要 客户端 提交的信息的。读、写分离会造成无法有效的 做出响应。所以 我们必须自己维护一段 缓冲区,去将每个socket fd 的有效内容都报春起来。直到进行 写事件 时用到,之后 回收该内存。

        每个事件句柄中都有 一个 不长使用的 指针(void*)。正好我们可以用它来指向 我们的缓冲区、

typedef union epoll_data {

void *ptr; //我们可用的 ptr  用来存指向缓冲区

int fd;

__uint32_t u32;

__uint64_t u64;

} epoll_data_t;

//感兴趣的事件和被触发的事件

struct epoll_event {

__uint32_t events; /* Epoll events */

epoll_data_t data; /* User data variable */

};

epoll.c

#include "httpd.h"
#include <sys/epoll.h>
#include <malloc.h>
#include <netdb.h>
#include <sys/utsname.h>
#include <net/if.h>
#include <sys/ioctl.h>
#define _MAXFD_ 10 
#define ERRORIP -1

int set_non_block(int fd)
{
	int old_flag=fcntl(fd,F_GETFL);
	if(old_flag<0){
		perror("fcntl");
	//	exit(-4);
		return -1;
	}
	if(fcntl(fd,F_SETFL,old_flag|O_NONBLOCK)<0){
		perror("fcntl");
		return -1;
	}

	return 0;

}

int startup(char* ip,int port)
{
	int sock=socket(AF_INET,SOCK_STREAM,0);
	struct sockaddr_in server;
	server.sin_family=AF_INET;
	server.sin_addr.s_addr=inet_addr(ip);
	server.sin_port=htons(port);
	int flag=0;
//	printf("port  %d  %d",port,(htons(port)));
	if(bind(sock,(struct sockaddr *)&server,sizeof(server))<0){
		perror("bind");
		exit(-2);
	}
	if( setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)) == -1)  
    {  
	    perror("setsockopt");  
	    exit(1);  			    
	}  
	if(listen(sock,50)<0){
		perror("listen");
		exit(-3);
	}
	return sock;
}

void usage(char* arg)
{
	printf("usage %s [ip] [port]\n",arg);

}

void epollup(int sock)
{
	int epoll_fd=epoll_create(256);
	if(epoll_fd<0){
		perror("epoll");
		return;
	}
	int timeout_num=0;
	int done=0;
	int timeout=10000;
	int i=0;
	int ret_num=-1;

	struct epoll_event ev;
	struct epoll_event event[100];
	ev.data.fd=sock;
	ev.events=EPOLLIN|EPOLLET;
//	fd_num=1;
//	printf("listen sock%d\n",sock);
	if(epoll_ctl(epoll_fd,EPOLL_CTL_ADD,sock,&ev)<0){
		perror("epoll_ctl");
		return ;
	}
	while(!done){
		switch(ret_num=epoll_wait(epoll_fd,event,256,timeout)){
		case -1:{
			perror("epoll_wait");
			break;
		}
		case 0 :{
			if(	timeout_num++>20)
				done=1;
			printf("time out...\n");
			break;
		}
		default:{
				for(i=0;i<ret_num;++i){
					if(event[i].data.fd==sock&&event[i].events&EPOLLIN){
						int new_sock=-1;
						struct sockaddr_in client;
						socklen_t len=sizeof(client);
						while((new_sock=accept(sock,(struct sockaddr*)&client,&len))){
							if(new_sock<0){
								//perror("accept");
								break;
							}
							if(set_non_block(new_sock)<0){
								echo_error(new_sock,500);
								continue;
							}
							printf(" epoll :%d\n",new_sock);
							ev.data.fd=new_sock;
							ev.events=EPOLLIN|EPOLLET;
							if(epoll_ctl(epoll_fd,EPOLL_CTL_ADD,new_sock,&ev)<0){
								perror("epoll_ctl");
								echo_error(new_sock,503);
								continue;
							}
						}
						break;
					}
					else {
						if(event[i].events&EPOLLIN){
							int fd=event[i].data.fd;
							pev_buf pev=(pev_buf)malloc(sizeof(ev_buf));
							event[i].data.ptr=pev;
							pev->fd=fd;
							if(epoll_recv_http(&event[i])<0){
								free(pev);
								if(epoll_ctl(epoll_fd,EPOLL_CTL_DEL,fd,&ev)<0)
									perror("read EPOLL_CTL_DEL");
								close(fd);
								continue;
							}
							else
								event[i].events=EPOLLOUT;
						}
						if(event[i].events&EPOLLOUT){
							int fd=((pev_buf)(event[i].data.ptr))->fd;
							epoll_echo_http(event+i);
							if(epoll_ctl(epoll_fd,EPOLL_CTL_DEL,fd,&ev)<0)
							perror("out_ctl_del");
							free((pev_buf)event[i].data.ptr);
							printf("epoll close :%d\n",fd);
							close(fd);

						}

					}
				}
			break;
		}

		}
	}
}

int main(int argc,char* argv[]){
	if(argc!=3){
		usage(argv[0]);
		exit(-1);
	}
	int port=atoi(argv[2]);
	int listen_sock;
	char* ip=NULL;
	if(strcmp(argv[1],"any")==0){
			int sfd, intr;
			struct ifreq buf[16];
		    struct ifconf ifc;
		    sfd = socket (AF_INET, SOCK_DGRAM, 0); 
		    if (sfd < 0)
				return ERRORIP;
			ifc.ifc_len = sizeof(buf);
			ifc.ifc_buf = (caddr_t)buf;
			if (ioctl(sfd, SIOCGIFCONF, (char *)&ifc))
				return ERRORIP;
			intr = ifc.ifc_len / sizeof(struct ifreq);
			while (intr-- > 0 && ioctl(sfd, SIOCGIFADDR, (char *)&buf[intr]));
				close(sfd);
			ip= inet_ntoa(((struct sockaddr_in*)(&buf[intr].ifr_addr))-> sin_addr);

		printf("%s\n",ip);
		listen_sock=startup(ip,port);
	}
	else

		 listen_sock=startup( argv[1],port);

//	printf("port %s %d",argv[2],port);

    set_non_block(listen_sock);
	epollup(listen_sock);
	close(listen_sock);
	return 0;
}

 httpd.h

#ifndef __MYHTTP__

#define __MYHTTP__
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/epoll.h>
#include <sys/sendfile.h>
#define _SIZE_ 1024

typedef struct event_buf{//自己维护的结构体
	int fd;
	char method[10];//http请求方法
	char url[1024];//请求参数
	char parameter[1024];//get方法 参数
	int cgi;//是否需要用到CGI 方法
}ev_buf,*pev_buf;//重命名

char* get_text(int fd,char* buf);//获取消息正文

int epoll_recv_http(struct epoll_event *ev);//接受消息
int epoll_echo_http(struct epoll_event * ev);//发送xiaoxi
void cgi_action(int fd,char* method,char* url,char* parameter);//CGI接口

int get_line(int sock_fd,char * buf);//从socket fd 读取一行信息

void* http_action(void* client_sock);//

void echo_error(int fd,int _errno);//回显错误信息
void error_all(int fd,int err,char* reason);//所有错误信息
void echo_html(int fd,const char* url,int size );//回显请求网页

#endif

httpd.c

#include "httpd.h"

#define DEFAULT "/default.html"
#define IMG "src_html"
#define CGI "src_cgi"

int times=0;

void echo_error(int fd,int _errno)
{
printf("join err\n");
switch(_errno){
case 400://Bad Request  //客户端请求有语法错误,不能被服务器所理解
break;
case 404:////请求资源不存在,eg:输入了错误的URL
//printf("*******404\n");
error_all(fd,404,"NOT_FIND!!");
break;
case 401://请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用 
break;
case 403://服务器收到请求,但是拒绝提供服务 
break;
case 500:// Internal Server Error //服务器发生不可预期的错误
break;
case 503://Server Unavailable  //服务器当前不能处理客户端的请求,一段时间后可能恢复正常
break;
default:
break;
}
}

void error_all(int fd,int err,char* reason)
{
char buf[_SIZE_]="";
char error[_SIZE_]="";
sprintf(buf,"HTTP/1.0 %d %s\r\n\r\n",err,reason);
sprintf(error," %d %s",err,reason);
printf("err buf:%s\n error:%s",buf,error);
write(fd,buf,strlen(buf));
write(fd,"<html>\n",strlen("<html>\n"));
write(fd,"<head>",strlen("<head>"));
write(fd,"<h1> HELLO PPH!!!</h1>\n",strlen("<h1> HELLO PPH!!!</h1>\n"));
write(fd,"<h2>",strlen("<h2>"));
write(fd,error,strlen(error));
write(fd,"</h2>\n",strlen("</h2>\n"));
write(fd,"</head>\n",strlen("</head>\n"));
write(fd,"</html>",strlen("</html>"));
//	echo_html(fd,"src_html/1.html",102400000);
}
void echo_html(int fd,const char* url,int fd_size ){
char buf[_SIZE_]="HTTP/1.1 200 OK\r\n\r\n";

int rfd=-1;
off_t set=0;
ssize_t size=1;
printf("url:%s\n",url);
if((rfd=open(url, O_RDONLY))<0){
echo_error(fd,500);
printf("e-html %s",strerror(errno));
}
printf("open success\n");
if(write(fd,buf,strlen(buf))<0){
perror(" echo_html_write");
close(rfd);
return;
}
printf("write head success\n");
int i=0;
while(set!=fd_size){
size=sendfile(fd,rfd,&set,fd_size);
if(errno!=EAGAIN&&size<0){
printf("sendfile error %s %d\n",strerror(errno),errno);
break;
}
//	printf("\nsend: size %d all size  %d time %d \n",set,fd_size,++i);
}
close(rfd);
return;
}

int get_line(int sock_fd,char * line){
int index=0;
ssize_t size=1;
char ch=0;
printf("getline start\n");
while(ch!=‘\n‘){
if((size=read(sock_fd,&ch,1))<0){
perror("getline__");
if(errno==EAGAIN)
continue;
else
return -1;
}
if(ch==‘\r‘){
char tmp=0;
if(recv(sock_fd,&tmp,1,MSG_PEEK)>0){
if(tmp==‘\n‘){
line[index++]=tmp;
read(sock_fd,&ch,1);
continue;
}
}
}
//printf("index %d\n",index);
if(index==1024){
printf("httpd line full exit\n");
line[1023]=0;
return -2;
}
line[index++]=ch;
}
line[index]=0;
if(strcmp(line,"\n")==0){
return 0;
}
printf("getline  success\n");
return 1;
}

//获取post正文 参数

char* get_length(int fd,char* content_length)
{
int size=1;
int tag=0;
int index=0;
while(size!=0){//通过持续读取一行 直到读到空行结束
size=get_line(fd,content_length);
if(size==-2)
continue;
if(strncasecmp(content_length,"content-length: ",16)==0){
printf(" length success\n");
break;
}
if(size==-1){
printf("get line出错\n");
return NULL;
}

}
content_length[strlen(content_length)-1]=0;
strcpy(content_length,content_length+16);
printf("con end: %s\n",content_length);
return content_length;
}

void cgi_action(int fd,char* method,char* url ,char* parameter)
{
char env[20]="METHOD=";
char par[_SIZE_]="PARAMETER=";
int pwrite[2];
if((pipe(pwrite)<0)){
perror("pipe");
return;
}
strcat(env,method);
strcat(par,parameter);
printf(" act url:%s\n",url);
printf("parameter:%s\n",par);
if(putenv(env)<0){
perror("putenv");
return;
}
if(putenv(par)<0){
perror("putenv par");
return;
}
//	printf("fork qian\n");
pid_t id=fork();
if(id<0){
perror("fork");
return;
}
else if(id==0){//子进程
close(pwrite[0]);
//printf("child\n");
if(dup2(pwrite[1],1)<0){
perror("dup2.1");
return;
}
if(dup2(fd,0)<0){
perror("dup2.2");
return;
}
if(execl(url,NULL)<0){
perror("execl");
printf("exit url:\n",url);
exit(-2);
}
 
}
else{//父进程
close(pwrite[1]);
char buf[_SIZE_]="";
int count=0;
int i=0;
ssize_t size=1;
while(size>0){
size=read(pwrite[0],buf,_SIZE_);
if(size<0){
echo_error(fd,500);
break;
}
if(size==0)
break;
write(fd,buf,strlen(buf));

}
waitpid(-1,NULL,0);
close(pwrite[0]);
}
}

int epoll_echo_http(struct epoll_event * ev)
{

char* method=((pev_buf)ev->data.ptr)->method;
char* url=((pev_buf)ev->data.ptr)->url;
char* parameter=((pev_buf)ev->data.ptr)->parameter;//get方法 参数
int fd=((pev_buf)ev->data.ptr)->fd;

int cgi=((pev_buf)ev->data.ptr)->cgi;
struct stat stat_buf; 
if(cgi==0)
if(stat(url, &stat_buf)<0){
printf("stat <0 \n");
echo_error(fd,404);
return 0;
}

if(strcasecmp("POST",method)==0){

//printf("already cgi\n");
cgi_action(fd,method,url,parameter);
}
else if(strcasecmp("GET",method)==0){
if(cgi==1){
//cgi
//	printf("rev_http: parameter:%s\n",parameter);
cgi_action(fd,method,url,parameter);
printf("ret cgi\n");
}
else{
echo_html(fd,url,stat_buf.st_size);
}

}
if(strcasecmp(method,"POST")==0)
clear_buf(fd);
return 0;
}

int epoll_recv_http(struct epoll_event *ev)
{
if(ev==NULL){
printf("ev error\n");
return -1;
}
int fd=((pev_buf)ev->data.ptr)->fd;

char real_url[128]="src_html";
char line[_SIZE_];
char* method=NULL;
char* version=NULL;
char* url=NULL;
char parameter[_SIZE_]="";//get方法 参数
char  content_length[_SIZE_]="";
if(get_line(fd,line)==-2){
printf("it‘s a cache request! so can‘t process!\n");
return 0;
}
int index=strlen(line)-1;
//GET / HTTP/1.1
while(index>0){//提取method url
if(line[index]==‘ ‘&&version==NULL){
version=((char*)line)+index+1;
line[index]=0;
}
if(line[index]==‘ ‘&&url==NULL){
url=line+index+1;
line[index]=0;
}
--index;
}
method=line;

((pev_buf)ev->data.ptr)->cgi=0;
if(strcasecmp("GET",method)==0){
index=0;
while(url[index]){
if(url[index]==‘?‘){
((pev_buf)ev->data.ptr)->cgi=1;
strcpy(parameter,url+index+1);
url[index]=0;
((pev_buf)ev->data.ptr)->cgi=1;
break;
}
++index;
}

}
else if(strcasecmp("POST",method)==0){
((pev_buf)ev->data.ptr)->cgi=1;
if(get_length(fd,content_length)==NULL){
echo_error(fd,503);
printf("get len err\n");
clear_buf(fd);
return -1;
}
strcpy(parameter,content_length);
}
if(strcmp(url,"/")==0){
strcat(real_url,DEFAULT);
}
else{
if(((pev_buf)ev->data.ptr)->cgi==1){
strcpy(real_url,CGI);
}
strcat(real_url,url);
}
printf("real_url :%s\n",real_url);

strcpy(((pev_buf)ev->data.ptr)->method,method);
strcpy(((pev_buf)ev->data.ptr)->url,real_url);
strcpy(((pev_buf)ev->data.ptr)->parameter,parameter);
printf(" get connect :%d times !\n",++times);
if(strcasecmp(method,"get")==0)
clear_buf(fd);

return 1;
}

int  clear_buf(int fd)
{
char buf[_SIZE_]="";
ssize_t size=0;
size=read(fd,buf,_SIZE_);
if(size<0){
perror("clear_buf_read");
return -1;
}
buf[size]=0;
return 0;
}
时间: 2024-10-20 19:30:22

网络 http服务器-v2-epoll版本的相关文章

网络 http服务器-v1-多线程版本

http服务器-v1-多线程版本 一.http协议分析         来源:http://www.cnblogs.com/li0803/archive/2008/11/03/1324746.html        引言    HTTP是一个属于应用层的面向对象的协议,由于其简捷.快速的方式,适用于分布式超媒体信息系统.它于1990年提出,经过几年(http/0.9 http/1.0)的使用与发展,得到不断地完善和扩展.目前在WWW中HTTP/1.1已经广泛使用,而且HTTP-NG(Next G

RDIFramework.NET V2.8版本 ━ 开发实例之产品管理(WinForm)

RDIFramework.NET V2.8版本 ━ 开发实例之产品管理(WinForm) 现在,我们使用.NET快速开发整合框架(RDIFramework.NET)来开发一个应用,此应用皆在说明如何使用框架简单快速的进行开发,整合.在这里我们要开发一个产品管理的功能模块,在开发前,我们需要进行数据库的设计,在这儿为了简单说明,我们只涉及到了一张产品表.CASE_PRODUCTINFO(产品信息表) 序号 列 名 数据 类型 长 度 小数 位 标 识 主 键 允许 空 默认值 说 明 1 ID i

WIN系统必需品:NTP网络时间服务器

安徽京准电子科技——为您的系统保驾护航! 一.NTP网络时间服务器产品介绍: NTP网络时间服务器是针对计算机.自动化装置等进行校时而研发的高科技设备,该产品可从GPS卫星(北斗卫星.B码接口.PTP)上获取标准的时间信号,将这些信号通过各种接口(NTP/SNTP.串口.B码.PTP.脉冲)传输给自动化系统中需要时间信息的设备(计算机.保护装置.故障录波器.事件顺序记录装置.安全自动装置.远动RTU),这样系统中就有了一个标准的时间源,从而达到整个系统的时间一致. HR系列NTP网络时间服务器内

RDIFramework.NET ━ .NET快速信息化系统开发框架 V2.8 版本发布

(新年巨献) RDIFramework.NET ━ .NET快速信息化系统开发框架 V2.8 版本发布 历时数月,RDIFramework.NET V2.8版本发布了,感谢大家的支持. RDIFramework.NET,基于.NET的快速信息化系统开发.整合框架,为企业或个人在.NET环境下快速开发系统提供了强大的支持,开发人员不需要开发系统的基础功能和公共模块,框架自身提供了强大的函数库和开发包,开发人员只须集中精力专注于业务部分的开发,因此大大提高开发效率和节约开发成本.框架采用目前最主流的

RDIFramework.NET ━ .NET快速信息化系统开发框架 V2.8 版本━新增岗位管理-Web部分

RDIFramework.NET ━ .NET快速信息化系统开发框架 V2.8 版本 新增岗位管理-Web部分 岗位(职位)管理模块主要是针对组织机构的岗位(职位)进行管理,包括:增加.修改.删除.移动.对岗位设置用户,设置岗位的权限等.岗位管理在企业应用中是一个普遍应用的模块,也属于其他业务应用的基础,特别是在业务流程的流转过程中经常会用到.合理的组织与规划组织机构下的岗位,对后期的管理可以起到事半功倍的效果. 登录系统后,选择“岗位管理”可以进入岗位管理模块. 岗位(职位)管理模块主界面如下

八百里流媒体服务器系统单机版本和集群版本的对比

苏州八百里网络科技有限公司作为专业的流媒体音视频技术解决方案的提供商,针对用户并发,功能和应用需求不同,开发了两个版本的流媒体服务器系统,提供局域网或互联网的高清标清网络直播和点播. 流媒体服务器系统提供: 嵌入网页的HTML代码和视频播放地址. 支持PC和手机端(安卓和苹果)的网页观看,应用了HLS协议和H5的技术. 接收标准的RTMP推送网络直播流并按照各种协议进行网络数据分发. 具有先进的Flash P2P技术,可以节省大量的带宽成本. 视频分片存储并加密同时提供防盗链,让独有的视频内容得

公司网络web服务器负载均衡解决方案

公司网络web服务器负载均衡解决方案 随着公司产品业务的推广发展壮大,对服务器的硬件性能.相应速度.服务稳定性.数据可靠性的要求越来越高.今后服务器的负载将难以承受所有的访问.从公司的实际情况,运营成本网络安全性考虑,排除使用价格昂贵的大型服务器.以及部署价格高昂的专用负载均衡设备. DNS轮询负载均衡解决方案虽然成本低廉但是安全性能不是很好,加上公司产品的特殊性需要用户验证的体系,在会话保持方面是一大缺陷,会话保持,如果是需要身份验证的网站,在不修改软件构架的情况下,这点是比较致命的,因为DN

RDIFramework.NET ━ .NET快速信息化系统开发框架 V2.8 版本━新增企业通(内部简易聊天工具)

RDIFramework.NET ━ .NET快速信息化系统开发框架 V2.8 版本 新增企业通(内部简易聊天工具) RDIFramework.NET,基于.NET的快速信息化系统开发.整合框架,给用户和开发者最佳的.Net框架部署方案. "企业通"是RDIFramework.NET框架提供的一个即时通讯.内部聊天沟通的工具.虽不能与商业化专业的沟通工具相比,但对于框架内部进行消息的沟通还是比较方便的.成功登录框架后,点击框架主菜单"辅助工具"→"企业通&

Linux网络编程——多路复用之epoll

目录 Linux网络编程--多路复用之epoll 基础API 实例一.epoll实现在线聊天 实例二.epoll实现在客户端断开后服务端能一直运行,客户端可以多次重连 Linux网络编程--多路复用之epoll ? epoll是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率,因为它会复用文件描述符集合来传递结果而不用迫使开发者每次等待事件之前都必须重新准备要被侦听的文件描述符集合,另一点原因就是获取事件的时候,

iNeuOS工业互联平台,开放设备驱动管理、服务驱动管理、云组态自定义画布等,促进平台开放、赋能和落地。发布:v2.3版本。

目       录 1.      概述... 2 2.      iNeuOS平台演示... 2 3.      设备驱动管理... 2 4.      服务驱动管理... 3 5.      云组态自定义画布... 4 6.      快速切换组态页面菜单... 5 7.      自定义右键菜单项,显示组态子页面... 5 8.      工况关联数据点... 6 9.      修改用户密码... 6 1.   概述 现在iNeuOS正式版本的演示平台已经有350多个注册用户,一部分注册