• 打造简易http服务器


    (linux下面的C代码)

    点击查看代码
    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<netinet/in.h>
    #include<sys/socket.h>
    #include<sys/types.h>
    #include<ctype.h>
    #include<string.h>
    #include<arpa/inet.h>
    #include<errno.h>
    #include<sys/stat.h>
    #include<pthread.h>
    
    static int debug=1;
    int getLine(int sock,char*buff,int size);//逐行获取请求行和请求头部
    void doHttpRequest(void*sock);//获取并解析请求
    void doHttpResponse(int sock,const char*path);//响应请求
    void notFound(int sock);//404文件不存在
    void innerError(int sock);//500服务器内部错误
    void notImplemented(int sock);//501请求方法未实现
    void badRequest(int sock);//400请求格式错误
    int sendHeader(int sock,FILE *file);//发送状态行和消息头部
    void sendBody(int sock,FILE *file);//发送响应正文
    
    int main(void)
    {
    	int sock;
    	sock=socket(AF_INET,SOCK_STREAM,0);
    	struct sockaddr_in server;
    	bzero(&server,sizeof(server));
    	server.sin_family=AF_INET;
    	server.sin_addr.s_addr=htonl(INADDR_ANY);
    	server.sin_port=htons(80);//浏览器http请求默认端口80,如果被占用,请更换其他端口
    	bind(sock,(struct sockaddr*)&server,sizeof(server));
    	listen(sock,32);
    	printf("等待客户端的连接...
    ");
    	
    	/*
    	有些浏览器在用户输入完url访问的时候还会发送其他请求,所以会多次执行while循环
    	造成页面可能无法正常显示,尚不清楚具体原因,知道的劳烦评论回复
    	*/
    	while(1)
    	{
    		int client_sock;
    		pthread_t id;
    		struct sockaddr_in client;
    		socklen_t client_addr_len=sizeof(client);
    		client_sock=accept(sock,(struct sockaddr*)&client,&client_addr_len);
    		char client_ip[64];
    		printf("client ip:%s	 port:%d
    ",inet_ntop(AF_INET,&client.sin_addr.s_addr,
    		client_ip,sizeof(client_ip)),ntohs(client.sin_port));
    		pthread_create(&id,NULL,doHttpRequest,(void*)&client_sock);//支持并发访问
    	}
    	
    	close(sock);
    	return 0;
    }
    
    int getLine(int sock,char*buff,int size)
    {
    	int count=0;
    	char c='';
    	int len=0;
    	while(count<size-1)
    	{
    		len=read(sock,&c,1);
    		if(len==1)
    		{
    			if(c=='
    ')
    				continue;
    			else if(c=='
    ')
    				break;
    			buff[count++]=c;
    		}
    		else if(len<0)
    		{
    			perror("read error
    ");
    			count=-1;
    			break;
    		}
    		else if(len==0)//客户端断开连接
    		{
    			fprintf(stderr,"client quit
    ");
    			count=-1;
    			break;
    		}
    	}
    	if(count>=0)
    		buff[count]='';
    	return count;
    }
    
    void doHttpRequest(void*pSock)
    {
    	char buff[256];
    	int len=0;
    	char method[64];
    	char url[256];
    	char path[512];
    	int i=0,j=0;
    	struct stat st;
    	int sock=*(int*)pSock;
    	if(getLine(sock,buff,sizeof(buff))>0)//请求行
    	{
    		while(!isspace(buff[i]))
    			method[j++]=buff[i++];
    		method[j]='';
    		j=0;
    		if(strncasecmp(method,"GET",strlen(method))==0)//不区分大小写,处理get请求
    		{
    			if(debug)printf("method:%s
    ",method);
    			while(isspace(buff[i++]));
    			while(!isspace(buff[i]))
    				url[j++]=buff[i++];
    			url[j]='';
    			if(debug)printf("url:%s
    ",url);
    			do//请求头部
    			{
    				len=getLine(sock,buff,sizeof(buff));
    				if(debug)printf("%s
    ",buff);
    			}while(len>0);
    			
    			char*pos=strchr(url,'?');
    			if(pos)*pos='';//把url中的'?'及后面的输入忽略掉,只要访问的实际网页
    			sprintf(path,"./htmlDocs/%s",url);//根据自己需要设置相应路径
    			if(debug)printf("path:%s
    ",path);
    			if(stat(path,&st)==-1)
    			{
    				fprintf(stderr,"path wrong,reason:%s
    ",strerror(errno));
    				notFound(sock);
    			}
    			else
    			{
    				if(S_ISDIR(st.st_mode))
    				{
    					strcat(path,"/index.html");
    				}
    				doHttpResponse(sock,path);
    			}
    		}
    		else//非get请求
    		{
    			if(debug)printf("method:%s
    ",method);
    			do//请求头部
    			{
    				len=getLine(sock,buff,sizeof(buff));
    				if(debug)printf("%s
    ",buff);
    			}while(len>0);
    			notImplemented(sock);
    		}
    	}
    	else//请求格式有误
    	{
    		badRequest(sock);
    	}
    	
    	close(sock);
    }
    
    void doHttpResponse(int sock,const char*path)
    {
    	FILE *file=NULL;
    	file=fopen(path,"r");
    	if(!file)
    	{
    		notFound(sock);
    		return;
    	}
    	int ret=-1;
    	ret=sendHeader(sock,file);
    	if(!ret)sendBody(sock,file);
    	fclose(file);
    }
    
    void notFound(int sock)
    {
    	const char * reply = "
    HTTP/1.0 404 NOT FOUND
    
    Content-Type: text/html
    
    
    
    <HTML lang="zh-CN">
    
    <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
    
    <HEAD>
    
    <TITLE>404</TITLE>
    
    </HEAD>
    
    <BODY>
    
    <P>404,文件不存在!
    
    </BODY>
    
    </HTML>
    ";
    	int len=write(sock,reply,strlen(reply));
    	if(debug)fprintf(stdout,reply);
    	if(len<=0)fprintf(stderr,"send not_found msg wrong,reason:%s
    ",strerror(errno));
    }
    
    int sendHeader(int sock,FILE *file)
    {
    	struct stat st;
    	char buff[1024];
    	char len[64];
    	strcpy(buff,"HTTP/1.0 200 OK
    ");
    	strcat(buff, "Server: soul Server
    ");
    	strcat(buff, "Content-Type: text/html
    ");
    	strcat(buff, "Connection: Close
    ");
    	int fd=fileno(file);
    	if(fstat(fd,&st)==-1)
    	{
    		innerError(sock);
    		return -1;
    	}
    	snprintf(len,64,"content-length:%ld
    
    ",st.st_size);
    	strcat(buff,len);
    	if(send(sock,buff,strlen(buff),0)<0)
    	{
    		fprintf(stderr,"send header fail,reason:%s
    ",strerror(errno));
    		return -1;
    	}
    	if(debug)fprintf(stdout,buff);
    	return 0;
    }
    
    void sendBody(int sock,FILE *file)
    {
    	struct stat st;
    	char buff[1024];
    	int len=0;
    	time_t start;
    	time_t end;
    	int fd=fileno(file);
    	if(fstat(fd,&st)==-1)
    	{
    		innerError(sock);
    		return;
    	}
    	while(!feof(file))
    	{
    		fgets(buff,sizeof(buff),file);//获取一行
    		len=write(sock,buff,strlen(buff));
    		time(&start);
    		while(len<0)
    		{
    			len=write(sock,buff,strlen(buff));
    			time(&end);
    			if(end-start>3)break;//重试超过3秒如果还没有写入成功则不再写入
    		}
    		if(len<0)
    		{
    			fprintf(stderr,"send body fail,reason:%s
    ",strerror(errno));
    			return;
    		}
    		if(debug)fprintf(stdout,buff);
    	}
    }
    
    void innerError(int sock)
    {
    	const char * reply = "
    HTTP/1.0 500 innerError
    
    Content-Type: text/html
    
    
    
    <HTML lang="zh-CN">
    
    <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
    
    <HEAD>
    
    <TITLE>500</TITLE>
    
    </HEAD>
    
    <BODY>
    
    <P>500,服务器内部错误!
    
    </BODY>
    
    </HTML>
    ";
    	int len=write(sock,reply,strlen(reply));
    	if(debug)fprintf(stdout,reply);
    	if(len<=0)fprintf(stderr,"send innerError msg wrong,reason:%s
    ",strerror(errno));
    }
    
    void notImplemented(int sock)
    {
    	const char * reply = "
    HTTP/1.0 501 notImplemented
    
    Content-Type: text/html
    
    
    
    <HTML lang="zh-CN">
    
    <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
    
    <HEAD>
    
    <TITLE>501</TITLE>
    
    </HEAD>
    
    <BODY>
    
    <P>501,请求方法未实现!
    
    </BODY>
    
    </HTML>
    ";
    	int len=write(sock,reply,strlen(reply));
    	if(debug)fprintf(stdout,reply);
    	if(len<=0)fprintf(stderr,"send notImplemented msg wrong,reason:%s
    ",strerror(errno));
    }
    
    void badRequest(int sock)
    {
    	const char * reply = "
    HTTP/1.0 400 badRequest
    
    Content-Type: text/html
    
    
    
    <HTML lang="zh-CN">
    
    <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
    
    <HEAD>
    
    <TITLE>400</TITLE>
    
    </HEAD>
    
    <BODY>
    
    <P>400,请求格式错误!
    
    </BODY>
    
    </HTML>
    ";
    	int len=write(sock,reply,strlen(reply));
    	if(debug)fprintf(stdout,reply);
    	if(len<=0)fprintf(stderr,"send badRequest msg wrong,reason:%s
    ",strerror(errno));
    }

    编译的时候还需要pthread库,所以在编译的时候需加上编译选项"-pthread"

    gcc xxx.c -pthread -o xxx.exe
  • 相关阅读:
    [分治算法]众数问题
    C++代码注入
    003 ansible部署ceph集群
    002 ceph的deploy部署
    001 Ceph简介
    vmware安装ubuntu的简单配置
    博客之行启程
    Java实现 LeetCode 813 最大平均值和的分组 (DFS+DP记忆化搜索)
    Java实现 LeetCode 813 最大平均值和的分组 (DFS+DP记忆化搜索)
    海伦公式和鞋带公式求三角形的面积
  • 原文地址:https://www.cnblogs.com/YLJ666/p/15017019.html
Copyright © 2020-2023  润新知