项目--mimi_http服务器

这里的html的页面可以自己去搜索一个,但是需要注意的是在linux系统上的文件路径和代码上需要一致,不然会报错!!!

#include<stdio.h>
#include<errno.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<string.h>
#include<ctype.h>
#include<arpa/inet.h>
#include<string.h>
#include <sys/stat.h>
#include<pthread.h>



#define SERVER_PORT 80
//#define IP "1.1.1.1"

static int debug = 1;

void * do_http_request(void *client_Sock);

int get_line(int client_Sock,char *buf,int buf_size);

void do_http_response(int client_Sock,const char *path);

void not_found(int client_Sock);//404

void unimplemented(int client_Sock);//501

void bad_request(int client_Sock);//400

int headers(int client_Sock,FILE*resource);

void cat(int client_Sock,FILE*resource);

void innter_error(int client_Sock);

//出错报错函数
void perror_fun(const char*des){
	
	fprintf(stderr,"%s error, reason: %s!!!\n",des,strerror(errno));
	exit(1);
	
}

int main(void){
	
	int sock;//信箱
	int ret=0;
	struct sockaddr_in server_addr;
	
	pthread_t id;
	int *pclient_sock=NULL;
	
	//创建信箱
	sock=socket(AF_INET,SOCK_STREAM,0);
	if(sock==-1){
		
		perror_fun("sock");
	}
	
	//选择协议IPV4
	server_addr.sin_family=AF_INET;
	
	//inet_pton(AF_INET,IP,&server_addr.sin_addr.s_addr);
	
	//监听本地所有IP地址
	server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
	
	//绑定端口
	server_addr.sin_port=htons(SERVER_PORT);
	
	//把标签绑定到收信的信箱上
	ret = bind(sock,(struct sockaddr*)&server_addr,sizeof(server_addr));
	if(ret==-1){
		
		perror_fun("bind");
	}
	
	//把信箱挂到传达室,这样就可以接收信件了
	ret = listen(sock,0);
	if(ret==-1){
		
		perror_fun("listen");
	}
	
	//等待来信
	printf("等待客户端的连接!!!!\n");
	
	while(1){
		
		struct sockaddr_in client;
		
		int client_Sock=0;
		
		int buf_len=0;
		
		char client_ip[64]={0};
		
		char buf[256]={0};
		
		socklen_t client_addr_len;
		
		client_addr_len=sizeof(client);
		
		client_Sock = accept(sock,(struct sockaddr*)&client,&client_addr_len);
		
		//打印客户端的IP地址和端口
		printf("客户端已连接: ip:%s\t port:%d\n",
				inet_ntop(AF_INET,&client.sin_addr.s_addr,client_ip,sizeof(client_ip)),
				ntohs(client.sin_port));
				
		//处理http请求,读取客户端发送的数据
		//do_http_request(client_Sock);
		
		//启动线程处理http请求
		pclient_sock = (int*)malloc(sizeof(int));
		
		*pclient_sock = client_Sock;
		
		pthread_create(&id,NULL,do_http_request,(void*)pclient_sock);
		
		//close(client_Sock);
		
	}
	
	close(sock);
	
	return 0;
	//在Windows下 使用telnet + ip + 端口  测试
}

void * do_http_request(void * pclient_Sock){
	
	int len=0;
	
	char buf[256]={0};
	
	char method[64]={0};
	
	char url[256]={0};
	
	char path[512]={0};
	
	struct stat st;
	
	int client_Sock=*(int*)pclient_Sock;
	
	//读取客户端发送的http请求
	
	//1.读取请求行
	len = get_line(client_Sock,buf,sizeof(buf));
	
	if(len>0){//读到请求行
	
		int i=0,j=0;
		
		while(!isspace(buf[j])&&i<sizeof(method)-1){//isspace(表示白空格)
			
			method[i]=buf[j];
			i++;
			j++;
		}
		
		method[i]='\0';//在行尾添加结束符
		
			if(debug){
				printf("request method: %s\n",method);
			}
		
		if(strncasecmp(method,"GET",strlen(method))==0){//只处理get请求
			if(debug){
				printf("method = GET\n");
			}
		
		//获取URL
		while(isspace(buf[j++]));//跳过白空格
			i=0;
		
		while(!isspace(buf[j])&&i<sizeof(url)-1){//isspace(表示白空格)
			
			url[i]=buf[j];
			i++;
			j++;
		}
		
		url[i]='\0';
		
			if(debug){
				printf("URL: %s\n",url);
			}
			
		//继续读取http头部
		do{
			
			len = get_line(client_Sock,buf,sizeof(buf));
			
			if(debug){
				printf("read : %s\n",buf);
			}
			
		}while(len>0);
		
		/**定位服务器本地的html文件**/
		
		//处理url中的?号
		{
			char *pos = strchr(url,'?');//判断是否为'?'
			
			if(pos){//如果是
				
				*pos='\0';
				
				printf("real url: %s\n",url);
			}
		}
		
		sprintf(path,"./demo_http/%s",url);
		
		if(debug){
			printf("path :%s\n",path);
		}
		
		//执行http响应
		//判断文件是否存在,如果存在就响应200 OK 同时发送相应的html文件,不存在就响应404 NOT FOUND
		
		if(stat(path,&st)==-1){//文件不存在或是出错
			
			fprintf(stderr,"stat %s failed, reason:%s\n",path,strerror(errno));
			
			not_found(client_Sock);
			
		}else{//文件存在
		
			if(S_ISDIR(st.st_mode)){
				
				strcat(path,"/index.html");
			}
		
			do_http_response(client_Sock,path);
		}
		
		}else{//非get请求,读取http头部,并响应客户端 501 method not implemented
			//继续读取http头部
		do{
			
			fprintf(stderr,"other request: %s\n",method);
			
			len = get_line(client_Sock,buf,sizeof(buf));
			
			if(debug){
				printf("read : %s\n",buf);
			}
			
		}while(len>0);
		
			unimplemented(client_Sock); //请求未实现
		}
		
	}else{//请求格式有问题,出错处理
		bad_request(client_Sock);  //在响应时再实现
	}	
	
	close(client_Sock);
	
	if(pclient_Sock){
		
		free(pclient_Sock);
	}

	return NULL;
}

void do_http_response(int client_Sock,const char *path){
	int ret = 0;
	FILE *resource = NULL;
	
	resource = fopen(path,"r");
	
	if(resource==NULL){
		
		not_found(client_Sock);
		
		return;
	}
	
	//1.发送http头部
	ret = headers(client_Sock,resource);
	
	//2.发送http body.
	if(!ret){
		
		cat(client_Sock,resource);
	
	}
	
	fclose(resource);
}

int headers(int client_Sock,FILE*resource){
	
	struct stat st;
	
	int file_id=0;
	
	char tmp[64]={0};
	
	char buf[1024]={0};
	
	//http头部
	strcpy(buf,"HTTP/1.0 200 OK\r\n");
	strcat(buf,"server: liangheng serverver\r\n");
	strcat(buf,"content-type: text/html\r\n");
	strcat(buf,"connection: close\r\n");
	
	file_id = fileno(resource);
	
	if(fstat(file_id,&st)==-1){
		
		innter_error(client_Sock);
		exit(-1);
	}
	
	snprintf(tmp,64,"Content-Length: %ld\r\n\r\n",st.st_size);
	
	strcat(buf,tmp);
	
	if(debug){
		
		fprintf(stdout,"header:%s\n",buf);
	}
	
	if(send(client_Sock,buf,strlen(buf),0)<0){
		
		fprintf(stderr,"send failed data:%s\n reason :%s\n",buf,strerror(errno));
		return -1;
	}
	
	return 0;
}

//实现html文件的内容按行读取并送给客户端
void cat(int client_Sock,FILE*resource){
	
	char buf[1024]={0};
	
	fgets(buf,sizeof(buf),resource);
	
	while(!feof(resource)){
		
		int len = write(client_Sock,buf,strlen(buf));
		
		if(len<0){//发送body的过程中出现问题,怎么办? 1.重试
			
			fprintf(stderr,"send body error, reason:%s\n",strerror(errno));
			break;
		}
		
		if(debug){
			
			fprintf(stdout,"%s\n",buf);
		}
		
		fgets(buf,sizeof(buf),resource);
	}
}

/*
void do_http_response(int client_Sock){
	
	const char *main_header="HTTP/1.0 200 OK\r\n server: liangheng serverver\r\ncontent-type: text/html\r\n connection: close";
	
	//字符串内,文本再包含字符串需要使用\ \转义符
	const char *http_val="\
<html lang=\"zh-CN\">\n\
<head>\n\
<meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\">\n\
<title>This is a test</title>\n\
</head>\n\
<body>\n\
<div align=center height=\"500px\" >\n\
<br/><br/><br/>\n\
<h2>游戏王: 决斗链接!!</h2><br/><br/>\n\
<form action=\"commit\" method=\"post\">\n\
账号: <input type=\"text\" name=\"name\" />\n\
<br/>密码: <input type=\"password\" name=\"age\" />\n\
<br/><br/><br/><input type=\"submit\" value=\"确定\" />\n\
<input type=\"reset\" value=\"取消\" />\n\
</form>\n\
</div>\n\
</body>\n\
</html>";
	
	//1.送main_header
	int len = write(client_Sock,main_header,strlen(main_header));
	
	if(debug){
		
		fprintf(stderr,"...do_http_response...\n");
	}
	
	if(debug){
		
		fprintf(stderr,"write[%d]: %s\n",len,main_header);
	}
	
	//2.生成content-length 行并发送
	char send_buf[64]={0};
	
	int http_len = strlen(http_val);
	
	len = snprintf(send_buf,64,"content-length: %d\r\n\r\n",http_len);
	
	len = write(client_Sock,send_buf,len);
	
	if(debug){
		
		fprintf(stdout,"write[%d]:%s",len,send_buf);
	}
	
	len = write(client_Sock,http_val,http_len);
	
	if(debug){
		
		fprintf(stdout,"write[%d]:%s",len,http_val);
	}
	
	//3.发送html 文件内容
}

*/

//返回值: -1表示读取出错, 0表示读到一个空行, 大于0表示成功读取一行 
int get_line(int client_Sock,char *buf,int buf_size){
	
	int count=0;
	
	char ch='\0';
	
	int len=0;
	
	while((count<buf_size-1)&&ch!='\n'){
		
		len=read(client_Sock,&ch,1);
		
		if(len==1){
			
			if(ch=='\r'){//'\r'表示回车
			
				continue;
				
			}else if(ch=='\n'){
				
				//buf[count]='\0';
				
				break;
			}
			
			//处理一般的字符
			buf[count]=ch;
			
			count++;
			
		}else if(len==-1){//读取出错
			
			perror("read failed");
			count=-1;
			break;
			
		}else{//返回0; 客户端关闭sock链接
			
			fprintf(stderr,"clien clsoe ...\n");
			count=-1;
			break;
		}
	}
	
	if(count>=0){
		
		buf[count]='\0';
	}
	
	return count;
}

void not_found(int client_Sock){
	
	const char*reply = "HTTP/1.0 404 NOT FOUND\r\n\
\r\n\
<HTML>\
<HEAD>\
<TITLE>NOT FOUND</TITLE>\
</HEAD>\
<BODY>\
<html lang=\"zh-CN\">\n\
<head>\n\
<meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\">\n\
<title>This is a test</title>\n\
</head>\n\
<body>\n\
<div align=center height=\"500px\" >\n\
<br/><br/><br/>\n\
<h2>文件不存在!!</h2><br/><br/>\n\
<P>The server could not fulfill your request because the resource specified is unavailable or nonexistent...\
</BODY>\
</HTML>";
	
	int len = write(client_Sock,reply,strlen(reply));
	
	if(debug){
		
		fprintf(stderr,"%s\n",reply);
	}
	
	if(len<=0){
		
		fprintf(stderr,"send reply failed reason: %s\n",strerror(errno));
	}
}

void unimplemented(int client_Sock){
	
	const char*reply = "HTTP/1.0 501 method NOT Implemented\r\n\
\r\n\
<HTML>\
<HEAD>\
<TITLE>NOT FOUND</TITLE>\
</HEAD>\
<BODY>\
<html lang=\"zh-CN\">\n\
<head>\n\
<meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\">\n\
<title>This is a test</title>\n\
</head>\n\
<body>\n\
<div align=center height=\"500px\" >\n\
<br/><br/><br/>\n\
<h2>找不到响应的文件!!</h2><br/><br/>\n\
<P>The server could not fulfill your request because the resource specified is unavailable or nonexistent...\
</BODY>\
</HTML>";	

	int len = write(client_Sock,reply,strlen(reply));
	
	if(debug){
		
		fprintf(stderr,"%s\n",reply);
	}
	
	if(len<=0){
		
		fprintf(stderr,"send reply failed reason: %s\n",strerror(errno));
	}
}

void bad_request(int client_Sock){
	
	const char*reply = "HTTP/1.0 400 bad request\r\n\
\r\n\
<HTML>\
<HEAD>\
<TITLE>NOT FOUND</TITLE>\
</HEAD>\
<BODY>\
<html lang=\"zh-CN\">\n\
<head>\n\
<meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\">\n\
<title>This is a test</title>\n\
</head>\n\
<body>\n\
<div align=center height=\"500px\" >\n\
<br/><br/><br/>\n\
<h2>文件不存在或是出错!!</h2><br/><br/>\n\
<P>The server could not fulfill your request because the resource specified is unavailable or nonexistent...\
</BODY>\
</HTML>";	

	int len = write(client_Sock,reply,strlen(reply));
	
	if(debug){
		
		fprintf(stderr,"%s\n",reply);
	}
	
	if(len<=0){
		
		fprintf(stderr,"send reply failed reason: %s\n",strerror(errno));
	}
}

void innter_error(int client_Sock){
	
	const char*reply = "HTTP/1.0 501 Method Not Implemented\r\n\
Content-Type: text/html\r\n\
\r\n\
<HTML>\r\n\
<HEAD>\r\n\
<TITLE>Inner Error</TITLE>\r\n\
</HEAD>\r\n\
<BODY>\
    <P>服务器内部出错...\r\n\
</BODY>\r\n\
</HTML>";
	
	int len = write(client_Sock,reply,strlen(reply));
	
	if(debug){
		
		fprintf(stderr,"%s\n",reply);
	}
	
	if(len<=0){
		
		fprintf(stderr,"send reply failed reason: %s\n",strerror(errno));
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值