打造简易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