C语言实现并发http服务器项目

C语言实现并发http服务器项目

    • 设置端口号
    • 创建套接字
    • 接收客户端请求
    • 获取客户端http请求数据
    • 实现Http解析
    • 实现Http响应
    • 多线程实现并发
    • 完整代码

设置端口号

我这里设置的是1234

#define SERVER_PORT 1234

创建套接字

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
int main(){
    //创建套接字
    int serv_sock = socket(AF_INET, SOCK_STREAM, 0);
    
    //将套接字和IP、端口绑定
    struct sockaddr_in serv_addr;
    
    //每个字节都用0填充
    bzero(&serv_addr, sizeof(serv_addr));
    
    
    serv_addr.sin_family = AF_INET;  //使用IPv4地址
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);  //监听本地所有IP地址
    serv_addr.sin_port = htons(SERVER_PORT);  //端口
    
    //绑定 
    bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
    
    
    //进入监听状态,等待用户发起请求
    listen(serv_sock, 20);
    
    printf("wait client connect...\n");
    return 0;
}

接收客户端请求

int main(){
	//...
    //printf("wait client connect...\n");
    
    int done = 1;
    
    while (done){
	    //接收客户端请求
	    struct sockaddr_in clnt_addr;
	    socklen_t clnt_addr_size = sizeof(clnt_addr);
	    int clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
    	
    	char client_ip[64];
    	char buf[256];
    	
    	//打印客户端ip地址和端口号 
    	printf("client ip: %s\t port: %d\n", 
			inet_ntop(AF_INET, &clnt_addr.sin_addr.s_addr, client_ip, sizeof(client_ip)), 
			ntohs(clnt_addr.sin_port));
   
	    //关闭套接字
	    close(clnt_sock);
	}
	
	//关闭套接字
	close(serv_sock);
    return 0;
}

获取服务器的ip地址

ifconfig

C语言实现并发http服务器项目_第1张图片
编译代码并运行

gcc server.c -o server
./server

在这里插入图片描述

在浏览器上输入

http://ip:port

在这里插入图片描述
程序输出如下:
在这里插入图片描述

获取客户端http请求数据

客户端请求消息
客户端发送一个HTTP请求到服务器的请求消息包括以下格式:请求行(request line)、请求头部(header)、空行和请求数据四个部分组成,下图给出了请求报文的一般格式。
C语言实现并发http服务器项目_第2张图片
定义一个get_http_line(int clnt_sock, char * buf, int size)的函数,读取socket缓冲区的一行数据,放回-1读取失败,否则返回这一行数据的字符个数。

int get_http_line(int clnt_sock, char * buf, int size){
	int cnt = 0;  //这一行数据的字符个数
	char ch = '\0';
	int len = 0;
	
	while (cnt < size - 1){
		//读取socket缓冲区的一个字符
		len = read(clnt_sock, &ch, 1);
		if (len == 1){  //读取成功
			if (ch == '\r'){  //吸收回车符
				continue;
			}else if (ch == '\n'){
				break;
			}
			buf[cnt++] = ch;
		}else if (len == -1){  //读取失败
			fprintf(stderr, "read failed\n");
			cnt = -1;
			break;
		}else {  //客户端主动关闭
			fprintf(stderr, "client close\n");
			cnt = -1;
			break;
		}
	}
	
	if (cnt >= 0){
		buf[cnt] = '\0';
	}
	
	return cnt;
}

定义一个do_http_request(int clnt_sock)的函数,实现获取客户端http请求的数据

void do_http_request(int clnt_sock){
	int len = 0;
	char buf[256];
	//打印数据
	do{
		len = get_http_line(clnt_sock, buf, sizeof(buf));
		if (debug) printf("%s\n", buf);
	}while (len > 0);
}

在main函数这个位置调用do_http_request函数

//获取客户端http请求数据 
do_http_request(clnt_sock);

C语言实现并发http服务器项目_第3张图片
编译并运行程序,输出如下:
C语言实现并发http服务器项目_第4张图片

实现Http解析

根据 HTTP 标准,HTTP 请求可以使用多种请求方法。

HTTP1.0 定义了三种请求方法: GET, POST 和 HEAD 方法。

HTTP1.1 新增了六种请求方法:OPTIONS、PUT、PATCH、DELETE、TRACE 和 CONNECT 方法。
C语言实现并发http服务器项目_第5张图片
这里我只完成了GET的请求方法,其他请求 暂不处理。
重写do_http_request(int clnt_sock)函数

#include 
#include 
struct stat st;
static int debug = 1;
static char parent_path[] = ".";  //存放html文件的主目录,"."为相对路径
void do_http_request(int clnt_sock){
	int len = 0;
	char buf[256];
	char method[16];  //请求方法
	char url[256];  //请求方法
	char path[260];  //请求的文件、路径
	
	//获取请求行(request line)
	len = get_http_line(clnt_sock, buf, sizeof(buf));
	if (len > 0){
		int i = 0, j = 0;
		
		//获取请求方法 
		while (buf[j] != ' ' && i < sizeof(method) - 1){
			method[i++] = buf[j++];
		}
		method[i] = '\0';
		if (debug) printf("request method: %s\n", method);
		
		if (strncasecmp(method, "GET", i) == 0){  //get请求 
			while (buf[j++] != ' ');
			
			i = 0;
			
			//获取请求url 
			while (buf[j] != ' ' && i < sizeof(url) - 1){
				url[i++] = buf[j++];
			}
			url[i] = '\0';
			if (debug) printf("request url: %s\n", url);

			//TODO:获取请求参数
			
			//去掉请求参数 
			char *last = strchr(url, '?');
			if (last) *last = '\0';
			
			if (debug) printf("real request url: %s\n", url);
			
			//获取请求文件、路径 
			sprintf(path, "%s%s", parent_path, url); 
			if (debug) printf("request path: %s\n", path);
			
			//判断文件是否存在
			if (stat(path, &st) == -1){
				fprintf(stderr, "stat %s find failed. reason: %s\n", path, strerror(errno));
			}else {
				if (S_ISDIR(st.st_mode)){  //如果是目录,获取对应目录的index.html文件
					strcat(path, "index.html");
				}
			}
		}else{  //其他请求 (暂不处理)
			fprintf(stderr, "other request [%s]\n", method);
		}
		
		//打印数据
		if (debug) printf("request body:\n%s\n", buf);
		
		do{
			len = get_http_line(clnt_sock, buf, sizeof(buf));
			if (debug) printf("%s\n", buf);
		}while (len > 0);
		
	}else{  //请求格式有问题,出错处理 
		fprintf(stderr, "request format error\n");
	} 
}

编译并运行程序,输出如下:
C语言实现并发http服务器项目_第6张图片

实现Http响应

HTTP 状态码
当浏览者访问一个网页时,浏览者的浏览器会向网页所在服务器发出请求。当浏览器接收并显示网页前,此网页所在的服务器会返回一个包含 HTTP 状态码的信息头(server header)用以响应浏览器的请求。

HTTP 状态码的英文为 HTTP Status Code。

下面是常见的 HTTP 状态码:

200 - 请求成功
301 - 资源(网页等)被永久转移到其它URL
404 - 请求的资源(网页等)不存在
500 - 内部服务器错误
HTTP 状态码分类
HTTP 状态码由三个十进制数字组成,第一个十进制数字定义了状态码的类型。响应分为五类:信息响应(100–199),成功响应(200–299),重定向(300–399),客户端错误(400–499)和服务器错误 (500–599):
C语言实现并发http服务器项目_第7张图片
HTTP响应也由四个部分组成,分别是:状态行、消息报头、空行和响应正文。
C语言实现并发http服务器项目_第8张图片
这里我只实现了200,400,404,500,501的状态码
200 OK 请求成功。一般用于GET与POST请求
400 Bad Request 客户端请求的语法错误,服务器无法理解
404 Not Found 服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置"您所请求的资源无法找到"的个性页面
500 Internal Server Error 服务器内部错误,无法完成请求
501 Not Implemented 服务器不支持请求的功能,无法完成请求

定义HTTP响应状态行全局变量

char z00[] = "HTTP/1.0 200 OK\r\n";
char f00[] = "HTTP/1.0 400 BAD REQUEST\r\n";
char f0f[] = "HTTP/1.0 404 NOT FOUND\r\n";
char b00[] = "HTTP/1.0 500 Internal Sever Error\r\n";
char b0l[] = "HTTP/1.0 501 Method Not Implemented\r\n";

定义一个do_http_response(int clnt_sock, const char *path)的函数,实现Http响应

void do_http_response(int clnt_sock, const char *path){
	FILE *resourse = NULL;
	resourse = fopen(path, "r");
	
	if (resourse == NULL){
		fprintf(stderr, "stat %s open failed. reason: %s\n", path, strerror(errno));
		not_found(clnt_sock);
		return;
	}
	
	//定义HTTP响应状态行
	char *header = z00;
	if (strncasecmp(path, "./error.html", 12) == 0){
		header = f0f;
	}else if (strncasecmp(path, "./inner.html", 12) == 0){
		header = b00;
	}else if (strncasecmp(path, "./unimplemented.html", 18) == 0){
		header = b0l;
	}else if (strncasecmp(path, "./bad_request.html", 16) == 0){
		header = f00;
	}
	
	//发送头部
	if (headers(clnt_sock, resourse, header) == 0){
		//发送内容
		tailers(clnt_sock, resourse);
	}
	
	fclose(resourse);
}

定义not_found、inner_error、unimplemented、bad_request函数来实现响应404、500、501、400状态。

void not_found(int clnt_sock){
	do_http_response(clnt_sock, "./error.html");
}

void inner_error(int clnt_sock){
	do_http_response(clnt_sock, "./inner.html");
}

void unimplemented(int clnt_sock){
	do_http_response(clnt_sock, "./unimplemented.html");
}
void bad_request(int clnt_sock){
	do_http_response(clnt_sock, "./bad_request.html");
}

在do_http_request函数这个位置调用它们
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在do_http_request函数这个位置调用do_http_response函数C语言实现并发http服务器项目_第9张图片
发送头部函数headers(int clnt_sock, FILE *resourse, const char *header),返回-1发送失败(文件已经打开但是访问文件出错),返回0发送成功

int headers(int clnt_sock, FILE *resourse, const char *header){
	struct stat st;
	int fileId = 0;
	fileId = fileno(resourse);  //获取已经打开的文件的文件描述符
	if (fstat(fileId, &st) == -1){  //文件已经打开但是访问文件出错
		fprintf(stderr, "inner error. reason: %s\n", strerror(errno));
		inner_error(clnt_sock);
		return -1;
	}
	
	char buf[1024] = {0};
	char temp[64];
	//状态行
	strcpy(buf, header);
	//消息报头
	strcat(buf, "Server: Martin Server\r\n");
	strcat(buf, "Content-Type: text/html\r\n");
	strcat(buf, "Connection: Close\r\n");
	
	int wc = st.st_size;  //正文大小
	sprintf(temp, "Content-Length: %d\r\n\r\n", wc);
	strcat(buf, temp);
	
	if (debug) printf("write:\n%s\n", buf);
	
	if(send(clnt_sock, buf, strlen(buf), 0) < 0){
		fprintf(stderr, "failed to send.reason:%s.buf:%s\n", strerror(errno), buf);
		return -1;
	}
	return 0;
}

发送内容函数tailers(int clnt_sock, FILE *resourse)

void tailers(int clnt_sock, FILE *resourse){
	char buf[1024];
	do{  //边读边写
		fgets(buf, sizeof(buf), resourse);
	 	int len = write(clnt_sock, buf, strlen(buf));
	 	
	 	if (len < 0){
	 		fprintf(stderr, "send error, reason: %s\n", strerror(errno));
	 		break;
		}
	 	
	 	if (debug) printf("%s", buf);
	}while (!feof(resourse));
	if (debug) printf("\n");
}

创建html文件,并放在存放html文件的主目录里面
在这里插入图片描述
index.html

DOCTYPE html>
<html style="font-size: 16px;">
  <head>
<meta charset="UTF-8">
    <title>indextitle>
  head>
  <body>
        <h1><b>Hello worldb>h1>
        <p>Hi worldp>
  body>
html>

error.html

DOCTYPE html>
<html style="font-size: 16px;">
  <head>
<meta charset="UTF-8">
    <title>404title>
  head>
  <body>
        <h1><b>404 Not Foundb>h1>
        <p>未找到该页面p>
  body>
html>

inner.html

DOCTYPE html>
<html style="font-size: 16px;">
  <head>
<meta charset="UTF-8">
    <title>500title>
  head>
  <body>
        <h1><b>500 Inner Errorb>h1>
        <p>服务器内部错误p>
  body>
html>

unimplemented.html

DOCTYPE html>
<html style="font-size: 16px;">
  <head>
<meta charset="UTF-8">
    <title>501title>
  head>
  <body>
        <h1><b>501 Method Not Implementedb>h1>
        <p>服务器不支持的请求方法p>
  body>
html>

bad_request.html

DOCTYPE html>
<html style="font-size: 16px;">
  <head>
<meta charset="UTF-8">
    <title>400title>
  head>
  <body>
        <h1><b>400 Bad Requestb>h1>
        <p>错误的请求p>
  body>
html>

编译并运行程序,输出如下:

  • 默认请求./index.html
    C语言实现并发http服务器项目_第10张图片
    C语言实现并发http服务器项目_第11张图片

  • 请求a.html
    C语言实现并发http服务器项目_第12张图片
    C语言实现并发http服务器项目_第13张图片

  • 请求空行

    在终端输入:

    telnet ip port
    

    再按一次回车键
    C语言实现并发http服务器项目_第14张图片
    C语言实现并发http服务器项目_第15张图片

  • POST请求
    在终端输入:

    telnet ip port
    POST index.html http/1.0
    

    C语言实现并发http服务器项目_第16张图片
    C语言实现并发http服务器项目_第17张图片

多线程实现并发

串行缺点:客户端排成一队,一个客户端完成请求后服务器才能接受下一个客户端的请求,客户端等待时间长。
在终端输入:

telnet ip port

然后在浏览器输入

http://ip:port

C语言实现并发http服务器项目_第18张图片
就会发现浏览器一直在等待服务器的响应,因为在浏览器前面服务器在处理telnet的请求,然而telnet不发送请求,就会造成死锁的情况。
在这里插入图片描述

解决死锁情况方法:

  1. 设定一个limit_time时间,服务器与客户端http连接后如果超过这个limit_time时间就断开连接。
  2. 使用多线程,并行执行http请求。

我用多线程来实现。
pthread_create函数
创建一个新线程,并行的执行任务。
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
返回值:成功:0; 失败:错误号。
参数:
pthread_t:当前Linux中可理解为:typedef unsigned long int pthread_t;
参数1:传出参数,保存系统为我们分配好的线程ID
参数2:通常传NULL,表示使用线程默认属性。若想使用具体属性也可以修改该参数。
参数3:函数指针,指向线程主函数(线程体),该函数运行结束,则线程结束。
参数4:线程主函数执行期间所使用的参数。
在一个线程中调用pthread_create()创建新的线程后,当前线程从pthread_create()返回继续往下执行,而新的线程所执行的代码由我们传给pthread_create的函数指针start_routine决定。start_routine函数接收一个参数,是通过pthread_create的arg参数传递给它的,该参数的类型为void *,这个指针按什么类型解释由调用者自己定义。start_routine的返回值类型也是void *,这个指针的含义同样由调用者自己定义。start_routine返回时,这个线程就退出了,其它线程可以调用pthread_join得到start_routine的返回值,以后再详细介绍pthread_join。
pthread_create成功返回后,新创建的线程的id被填写到thread参数所指向的内存单元。
attr参数表示线程属性,本节不深入讨论线程属性,所有代码例子都传NULL给attr参数,表示线程属性取缺省值。

包含头文件

#include 

将原来的void do_http_request(int clnt_sock)修改成void * do_http_request(void * pclnt_sock),因为pthread_create传的是void *的函数指针和void *的参数。

void * do_http_request(void * pclnt_sock){
	int len = 0;
	char buf[256];
	char method[16];
	char url[256];
	char path[260];
	int clnt_sock = *(int *)pclnt_sock;  //新增
	len = get_http_line(clnt_sock, buf, sizeof(buf));
	if (len > 0){
		int i = 0, j = 0;
		
		//获取请求方法 
		while (buf[j] != ' ' && i < sizeof(method) - 1){
			method[i++] = buf[j++];
		}
		method[i] = '\0';
		if (debug) printf("request method: %s\n", method);
		
		if (strncasecmp(method, "GET", i) == 0){  //get请求 
			while (buf[j++] != ' ');
			
			i = 0;
			
			//获取请求url 
			while (buf[j] != ' ' && i < sizeof(url) - 1){
				url[i++] = buf[j++];
			}
			url[i] = '\0';
			if (debug) printf("request url: %s\n", url);
			
			//去掉请求参数 
			char *last = strchr(url, '?');
			if (last) *last = '\0';
			
			if (debug) printf("real request url: %s\n", url);
			
			//获取请求文件、路径 
			sprintf(path, "%s%s", parent_path, url); 
			if (debug) printf("request path: %s\n", path);
			
			//判断文件是否存在
			if (stat(path, &st) == -1){
				fprintf(stderr, "stat %s find failed. reason: %s\n", path, strerror(errno));
				not_found(clnt_sock);
			}else {
				if (S_ISDIR(st.st_mode)){
					strcat(path, "index.html");
				}
				do_http_response(clnt_sock, path);
			}
		}else{  //其他请求 (暂不处理)
			fprintf(stderr, "other request [%s]\n", method);
			unimplemented(clnt_sock);
		}
		
		if (debug) printf("request body:\n%s\n", buf);
		
		do{
			len = get_http_line(clnt_sock, buf, sizeof(buf));
			if (debug) printf("%s\n", buf);
		}while (len > 0);
		
	}else{  //请求格式有问题,出错处理 
		fprintf(stderr, "request format error\n");
		bad_request(clnt_sock);
	} 
	
	//关闭套接字
	close(clnt_sock);
	//释放指针 
	if (pclnt_sock) free(pclnt_sock);
	pclnt_sock = 0;
	return NULL;
}

在main函数增加如下代码:

pthread_t p_id; 
int * pclnt_sock = NULL;
pclnt_sock = (int *)malloc (sizeof(int));
*pclnt_sock = clnt_sock;
//开启线程
pthread_create(&p_id, NULL, do_http_request, (void *)pclnt_sock);

C语言实现并发http服务器项目_第19张图片
编译代码并运行

gcc server.c -o server -lpthread
./server

C语言实现并发http服务器项目_第20张图片

完整代码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include  

#define SERVER_PORT 1234

struct stat st;
static int debug = 1;
static char parent_path[] = ".";
char z00[] = "HTTP/1.0 200 OK\r\n";
char f00[] = "HTTP/1.0 400 BAD REQUEST\r\n";
char f0f[] = "HTTP/1.0 404 NOT FOUND\r\n";
char b00[] = "HTTP/1.0 500 Internal Sever Error\r\n";
char b0l[] = "HTTP/1.0 501 Method Not Implemented\r\n";


int get_http_line(int clnt_sock, char * buf, int size);
int headers(int clnt_sock, FILE *resourse, const char *header);
void tailers(int clnt_sock, FILE *resourse); 
void not_found(int clnt_sock);
void inner_error(int clnt_sock);
void unimplemented(int clnt_sock);
void bad_request(int clnt_sock);
void do_http_response(int clnt_sock, const char *path);
//void do_http_request(int clnt_sock);
void * do_http_request(void * pclnt_sock); 

int main(){
    //创建套接字
    int serv_sock = socket(AF_INET, SOCK_STREAM, 0);
    
    //将套接字和IP、端口绑定
    struct sockaddr_in serv_addr;
    
    //每个字节都用0填充
    bzero(&serv_addr, sizeof(serv_addr));
    
    
    serv_addr.sin_family = AF_INET;  //使用IPv4地址
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);  //具体的IP地址
    serv_addr.sin_port = htons(SERVER_PORT);  //端口
    
    
    bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
    
    
    //进入监听状态,等待用户发起请求
    listen(serv_sock, 20);
    
    printf("wait client connect...\n");
    
    int done = 1;
    
    while (done){
	    //接收客户端请求
	    struct sockaddr_in clnt_addr;
	    socklen_t clnt_addr_size = sizeof(clnt_addr);
	    int clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
    	
    	char client_ip[64];
    	char buf[256];
    	
    	pthread_t p_id; 
    	int * pclnt_sock = NULL;
    	pclnt_sock = (int *)malloc (sizeof(int));
    	*pclnt_sock = clnt_sock;
    	
    	//打印客户端ip地址和端口号 
    	printf("client ip: %s\t port: %d\n", 
			inet_ntop(AF_INET, &clnt_addr.sin_addr.s_addr, client_ip, sizeof(client_ip)), 
			ntohs(clnt_addr.sin_port));
    	
    	//获取客户端http请求数据 
//    	do_http_request(clnt_sock);
   
	    //关闭套接字
//	    close(clnt_sock);

		//开启线程
		pthread_create(&p_id, NULL, do_http_request, (void *)pclnt_sock);
	}
	
	//关闭套接字
	close(serv_sock);
    return 0;
}

//void do_http_request(int clnt_sock){
void * do_http_request(void * pclnt_sock){
	int len = 0;
	char buf[256];
	char method[16];
	char url[256];
	char path[260];
	int clnt_sock = *(int *)pclnt_sock;
	len = get_http_line(clnt_sock, buf, sizeof(buf));
	if (len > 0){
		int i = 0, j = 0;
		
		//获取请求方法 
		while (buf[j] != ' ' && i < sizeof(method) - 1){
			method[i++] = buf[j++];
		}
		method[i] = '\0';
		if (debug) printf("request method: %s\n", method);
		
		if (strncasecmp(method, "GET", i) == 0){  //get请求 
			while (buf[j++] != ' ');
			
			i = 0;
			
			//获取请求url 
			while (buf[j] != ' ' && i < sizeof(url) - 1){
				url[i++] = buf[j++];
			}
			url[i] = '\0';
			if (debug) printf("request url: %s\n", url);
			
			//去掉请求参数 
			char *last = strchr(url, '?');
			if (last) *last = '\0';
			
			if (debug) printf("real request url: %s\n", url);
			
			//获取请求文件、路径 
			sprintf(path, "%s%s", parent_path, url); 
			if (debug) printf("request path: %s\n", path);
			
			//判断文件是否存在
			if (stat(path, &st) == -1){
				fprintf(stderr, "stat %s find failed. reason: %s\n", path, strerror(errno));
				not_found(clnt_sock);
			}else {
				if (S_ISDIR(st.st_mode)){
					strcat(path, "index.html");
				}
				do_http_response(clnt_sock, path);
			}
		}else{  //其他请求 (暂不处理)
			fprintf(stderr, "other request [%s]\n", method);
			unimplemented(clnt_sock);
		}
		
		if (debug) printf("request body:\n%s\n", buf);
		
		do{
			len = get_http_line(clnt_sock, buf, sizeof(buf));
			if (debug) printf("%s\n", buf);
		}while (len > 0);
		
	}else{  //请求格式有问题,出错处理 
		fprintf(stderr, "request format error\n");
		bad_request(clnt_sock);
	} 
	
	//关闭套接字
	close(clnt_sock);
	//释放指针 
	if (pclnt_sock) free(pclnt_sock);
	pclnt_sock = 0;
	return NULL;
}


void do_http_response(int clnt_sock, const char *path){
	FILE *resourse = NULL;
	resourse = fopen(path, "r");
	
	if (resourse == NULL){
		fprintf(stderr, "stat %s open failed. reason: %s\n", path, strerror(errno));
		not_found(clnt_sock);
		return;
	}
	
	//定义HTTP响应状态行
	char *header = z00;
	if (strncasecmp(path, "./error.html", 12) == 0){
		header = f0f;
	}else if (strncasecmp(path, "./inner.html", 12) == 0){
		header = b00;
	}else if (strncasecmp(path, "./unimplemented.html", 18) == 0){
		header = b0l;
	}else if (strncasecmp(path, "./bad_request.html", 16) == 0){
		header = f00;
	}
	
	//发送头部
	if (headers(clnt_sock, resourse, header) == 0){
		//发送内容
		tailers(clnt_sock, resourse);
	}
	
	fclose(resourse);
}

int get_http_line(int clnt_sock, char * buf, int size){
	int cnt = 0;
	char ch = '\0';
	int len = 0;
	
	while (cnt < size - 1 && ch != '\n'){
		len = read(clnt_sock, &ch, 1);
		if (len == 1){
			if (ch == '\r'){
				continue;
			}else if (ch == '\n'){
				break;
			}
		}else if (len == -1){
			fprintf(stderr, "read failed\n");
			cnt = -1;
			break;
		}else {
			fprintf(stderr, "client close\n");
			cnt = -1;
			break;
		}
		buf[cnt++] = ch;
	}
	
	if (cnt >= 0){
		buf[cnt] = '\0';
	}
	
	return cnt;
}

void not_found(int clnt_sock){
	do_http_response(clnt_sock, "./error.html");
}

void inner_error(int clnt_sock){
	do_http_response(clnt_sock, "./inner.html");
}

void unimplemented(int clnt_sock){
	do_http_response(clnt_sock, "./unimplemented.html");
}
void bad_request(int clnt_sock){
	do_http_response(clnt_sock, "./bad_request.html");
}

int headers(int clnt_sock, FILE *resourse, const char *header){
	struct stat st;
	int fileId = 0;
	fileId = fileno(resourse);  //获取已经打开的文件的文件描述符
	if (fstat(fileId, &st) == -1){  //文件已经打开但是访问文件出错
		fprintf(stderr, "inner error. reason: %s\n", strerror(errno));
		inner_error(clnt_sock);
		return -1;
	}
	
	char buf[1024] = {0};
	char temp[64];
	//状态行
	strcpy(buf, header);
	//消息报头
	strcat(buf, "Server: Martin Server\r\n");
	strcat(buf, "Content-Type: text/html\r\n");
	strcat(buf, "Connection: Close\r\n");
	
	int wc = st.st_size;  //正文大小
	sprintf(temp, "Content-Length: %d\r\n\r\n", wc);
	strcat(buf, temp);
	
	if (debug) printf("write:\n%s\n", buf);
	
	if(send(clnt_sock, buf, strlen(buf), 0) < 0){
		fprintf(stderr, "failed to send.reason:%s.buf:%s\n", strerror(errno), buf);
		return -1;
	}
	return 0;
}

void tailers(int clnt_sock, FILE *resourse){
	char buf[1024];
	do{
		fgets(buf, sizeof(buf), resourse);
	 	int len = write(clnt_sock, buf, strlen(buf));
	 	
	 	if (len < 0){
	 		fprintf(stderr, "send error, reason: %s\n", strerror(errno));
	 		break;
		}
	 	
	 	if (debug) printf("%s", buf);
	}while (!feof(resourse));
	if (debug) printf("\n");
}

你可能感兴趣的:(C++服务器,服务器,c语言,http)