最近要用c语言写一个简单的http服务器,实现html的deliver和404处理
由于是第一次写服务器,花了点精力,终于实现了,在这里分享一下
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<netinet/in.h>
#include<pthread.h>
#include<sys/socket.h>
#include<netdb.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<ctype.h>
#define SERVER_STRING "Server: httpT/0.1.0\r\n"
#define MAX_WAIT 5//服务器最大等待数
int startup(u_short *port);//生成套接字socket(),套接字和客户端地址绑定,分配端口,开始监听请求listen()
void accept_request(int client_sock);//处理客户端请求
int get_line(int sock, char *buf, int size);//从套接字socket中读取一行放在buf缓冲区中、返回buf中存储的数据长度(不包括\0)
void serve_file(int client_sock,const char* filename);//把filename这个文件加上http头后发给客户端
void header(int client_sock, const char *filename);//给客户端发送http头
void cat(int client_sock,FILE* fp);//给客户端发送请求的文件的内容
void not_found(int client_sock);//请求的文件404 not found 返回给客户端404错误信息
void error_die(const char* msg);//错误,终止进程
int main()
{
int server_sock = -1;//服务器端socket
int client_sock = -1;//客户端socket
u_short port = 44444;//端口号
struct sockaddr_in client_name;//客户端地址数据结构
int client_name_len = sizeof(client_name);
pthread_t thread;//线程变量
server_sock = startup(&port);
printf("http server running on port:%d\n",port);
while(1)//无限循环等待请求
{
client_sock = accept(server_sock,(struct sockaddr*)&client_name,&client_name_len);//接受客户端请求
if(client_sock == -1)
error_die("accept() error");
if(pthread_create(&thread , NULL, accept_request, client_sock) != 0)
error_die("pthread_create() error");
}
close(server_sock);//关闭服务器socket,结束程序
return 0;
}
int startup(u_short *port)
{
int server_sock = -1;//客户端socket
struct sockaddr_in name;
server_sock = socket(PF_INET, SOCK_STREAM, 0);//根据指定的地址族、数据类型和协议来分配一个套接口的描述字及其所用的资源
if(server_sock == -1)
error_die("socket() error");
memset(&name, 0, sizeof(name));
name.sin_family = AF_INET;
name.sin_port = htons(*port);
name.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(server_sock,(struct sockaddr*)&name,sizeof(name)) < 0)
error_die("bind() error");
if(*port == 0)
{
int namelen = sizeof(name);
if(getsockname(server_sock,(struct sockaddr*)&name,&namelen) < 0)//如果端口是0,把随机分配给server_sock的端口取出来赋值给port
error_die("getsockname() error");
*port = ntohs(name.sin_port);
}
//开始监听
if(listen(server_sock,MAX_WAIT) < 0)
error_die("listen() error");
return server_sock;
}
int get_line(int client_sock,char* buf,int size)
{
//用recv()逐个字符从client_sock中接受一行http请求
char c = '\0';
int i = 0;
int n;
while((i < size - 1) && (c != '\n'))
{
n = recv(client_sock,&c,1,0);
if(n > 0)
{
if(c == '\r')
{
n = recv(client_sock,&c,1,MSG_PEEK);
if((n > 0) && (c == '\n'))
{
recv(client_sock,&c,1,0);
}
else
{
c = '\n';
}
}
buf[i] = c;
i++;
}
else
{
c = '\n';
}
}
buf[i] = '\0';
return i;
}
void accept_request(int client_sock)
{
char buf[256];
char file_path[256];
char url[256];
char method[50];
int numchars = 0;
struct stat st;//文件
int i = 0;
char* ptr = 0;//用来遍历缓存buf
get_line(client_sock,buf,256);
puts(buf);
ptr = buf;
while(!isspace(*ptr))//取出请求method
{
method[i] = *ptr;
i++;
ptr++;
}
method[i] = '\0';
i=0;
ptr++;
while(!isspace(*ptr))//取出请求的文件路径
{
file_path[i] = *ptr;
i++;
ptr++;
}
file_path[i] = '\0';
sprintf(url,"htdocs%s",file_path);
if(url[strlen(url) - 1] == '/')
{
//如果这是一个目录,默认访问目录下的index.html
strcat(url,"index.html");
}
//如果不是以/结尾的依然可能是一个目录文件夹
if(stat(url,&st) < 0)
{
//把所有 headers 的信息都丢弃,这一步是必须的,就是在这里走了许多弯路!
numchars = get_line(client_sock, buf, sizeof(buf));
while ((numchars > 0) && strcmp("\n", buf)) // read & discard headers
{
numchars = get_line(client_sock, buf, sizeof(buf));
}
not_found(client_sock);
}
else
{
if ((st.st_mode & S_IFMT) == S_IFDIR) //S_IFMT为文件类型的位遮罩,进行按位与运算后的结果与S_IFDIR比较,判断是否为文件夹
strcat(url, "/index.html");
//下面可以deliver html文件了
serve_file(client_sock,url);
}
close(client_sock);//断开与客户端的连接,http的特性
}
void serve_file(int client_sock,const char* filename)
{
FILE* fp = NULL;
int numchars = 1;
char buf[1024];
//读取并丢弃 header
buf[0] = 'A'; buf[1] = '\0';
while ((numchars > 0) && strcmp("\n", buf)) // read & discard headers
numchars = get_line(client_sock, buf, sizeof(buf));
if((fp = fopen(filename,"r")) == NULL)
not_found(client_sock);
else
{
header(client_sock,filename);//给客户端发送http头
cat(client_sock,fp);//发送文件内容
}
fclose(fp);
}
void header(int client_sock, const char *filename)
{
char buf[256];
(void)filename; /* could use filename to determine file type */
sprintf(buf,"HTTP/1.1 200 OK\r\n");
puts(buf);
send(client_sock,buf,strlen(buf),0);
sprintf(buf,SERVER_STRING);
send(client_sock,buf,strlen(buf),0);
sprintf(buf,"Content-Type: text/html;charset=utf-8\r\n");
send(client_sock,buf,strlen(buf),0);
sprintf(buf,"\r\n");
send(client_sock,buf,strlen(buf),0);
return;
}
void cat(int client_sock,FILE* fp)
{
char buf[1024];
fgets(buf,sizeof(buf),fp);
while(!feof(fp))
{
send(client_sock,buf,strlen(buf),0);
fgets(buf,sizeof(buf),fp);
}
}
void not_found(int client_sock)
{
char buf[256];
strcpy(buf,"HTTP/1.1 404 NOT FOUND\r\n");
send(client_sock,buf,strlen(buf),0);
strcpy(buf,SERVER_STRING);
send(client_sock,buf,strlen(buf),0);
strcpy(buf,"Content-Type: text/html;charset=utf-8;\r\n");
send(client_sock,buf,strlen(buf),0);
strcpy(buf,"\r\n\r\n");
send(client_sock,buf,strlen(buf),0);
strcpy(buf,"<html><head><title>not found</title><head><body>\r\n");
send(client_sock,buf,strlen(buf),0);
strcpy(buf,"<h1 style=\"text-align:center;\">ERROR: 404 NOT FOUND</h1></body></html>\r\n");
send(client_sock,buf,strlen(buf),0);
return;
}
void error_die(const char* msg)
{
perror(msg);
exit(1);
}