打造简易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("等待客户端的连接...\n");
	
	/*
	有些浏览器在用户输入完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\t port:%d\n",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='\0';
	int len=0;
	while(count<size-1)
	{
		len=read(sock,&c,1);
		if(len==1)
		{
			if(c=='\r')
				continue;
			else if(c=='\n')
				break;
			buff[count++]=c;
		}
		else if(len<0)
		{
			perror("read error\n");
			count=-1;
			break;
		}
		else if(len==0)//客户端断开连接
		{
			fprintf(stderr,"client quit\n");
			count=-1;
			break;
		}
	}
	if(count>=0)
		buff[count]='\0';
	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]='\0';
		j=0;
		if(strncasecmp(method,"GET",strlen(method))==0)//不区分大小写,处理get请求
		{
			if(debug)printf("method:%s\n",method);
			while(isspace(buff[i++]));
			while(!isspace(buff[i]))
				url[j++]=buff[i++];
			url[j]='\0';
			if(debug)printf("url:%s\n",url);
			do//请求头部
			{
				len=getLine(sock,buff,sizeof(buff));
				if(debug)printf("%s\n",buff);
			}while(len>0);
			
			char*pos=strchr(url,'?');
			if(pos)*pos='\0';//把url中的'?'及后面的输入忽略掉,只要访问的实际网页
			sprintf(path,"./htmlDocs/%s",url);//根据自己需要设置相应路径
			if(debug)printf("path:%s\n",path);
			if(stat(path,&st)==-1)
			{
				fprintf(stderr,"path wrong,reason:%s\n",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\n",method);
			do//请求头部
			{
				len=getLine(sock,buff,sizeof(buff));
				if(debug)printf("%s\n",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\r\n\
Content-Type: text/html\r\n\
\r\n\
<HTML lang=\"zh-CN\">\n\
<meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\">\n\
<HEAD>\n\
<TITLE>404</TITLE>\n\
</HEAD>\n\
<BODY>\n\
<P>404,文件不存在!\n\
</BODY>\n\
</HTML>\n";
	int len=write(sock,reply,strlen(reply));
	if(debug)fprintf(stdout,reply);
	if(len<=0)fprintf(stderr,"send not_found msg wrong,reason:%s\n",strerror(errno));
}

int sendHeader(int sock,FILE *file)
{
	struct stat st;
	char buff[1024];
	char len[64];
	strcpy(buff,"HTTP/1.0 200 OK\r\n");
	strcat(buff, "Server: soul Server\r\n");
	strcat(buff, "Content-Type: text/html\r\n");
	strcat(buff, "Connection: Close\r\n");
	int fd=fileno(file);
	if(fstat(fd,&st)==-1)
	{
		innerError(sock);
		return -1;
	}
	snprintf(len,64,"content-length:%ld\r\n\r\n",st.st_size);
	strcat(buff,len);
	if(send(sock,buff,strlen(buff),0)<0)
	{
		fprintf(stderr,"send header fail,reason:%s\n",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\n",strerror(errno));
			return;
		}
		if(debug)fprintf(stdout,buff);
	}
}

void innerError(int sock)
{
	const char * reply = "\
HTTP/1.0 500 innerError\r\n\
Content-Type: text/html\r\n\
\r\n\
<HTML lang=\"zh-CN\">\n\
<meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\">\n\
<HEAD>\n\
<TITLE>500</TITLE>\n\
</HEAD>\n\
<BODY>\n\
<P>500,服务器内部错误!\n\
</BODY>\n\
</HTML>\n";
	int len=write(sock,reply,strlen(reply));
	if(debug)fprintf(stdout,reply);
	if(len<=0)fprintf(stderr,"send innerError msg wrong,reason:%s\n",strerror(errno));
}

void notImplemented(int sock)
{
	const char * reply = "\
HTTP/1.0 501 notImplemented\r\n\
Content-Type: text/html\r\n\
\r\n\
<HTML lang=\"zh-CN\">\n\
<meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\">\n\
<HEAD>\n\
<TITLE>501</TITLE>\n\
</HEAD>\n\
<BODY>\n\
<P>501,请求方法未实现!\n\
</BODY>\n\
</HTML>\n";
	int len=write(sock,reply,strlen(reply));
	if(debug)fprintf(stdout,reply);
	if(len<=0)fprintf(stderr,"send notImplemented msg wrong,reason:%s\n",strerror(errno));
}

void badRequest(int sock)
{
	const char * reply = "\
HTTP/1.0 400 badRequest\r\n\
Content-Type: text/html\r\n\
\r\n\
<HTML lang=\"zh-CN\">\n\
<meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\">\n\
<HEAD>\n\
<TITLE>400</TITLE>\n\
</HEAD>\n\
<BODY>\n\
<P>400,请求格式错误!\n\
</BODY>\n\
</HTML>\n";
	int len=write(sock,reply,strlen(reply));
	if(debug)fprintf(stdout,reply);
	if(len<=0)fprintf(stderr,"send badRequest msg wrong,reason:%s\n",strerror(errno));
}

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

gcc xxx.c -pthread -o xxx.exe
posted @ 2021-07-15 18:46  youlj  阅读(77)  评论(0编辑  收藏  举报