ホームページ > バックエンド開発 > PHPチュートリアル > PHPサーバーを実装するC言語

PHPサーバーを実装するC言語

WBOY
リリース: 2016-06-23 13:46:33
オリジナル
1558 人が閲覧しました

原理介绍

原创性申明:

HTTP协议的作用原理

连接:Web浏览器与Web服务器建立连接,打开一个称为socket(套接字)的虚拟文件,此文件的建立标志着连接建立成功。
请求:Web浏览器通过socket向Web服务器提交请求。HTTP的请求一般是GET或POST命令(POST用于FORM参数的传递)。GET命令的格式为:
GET 路径/文件名 HTTP/1.0
文件名指出所访问的文件,HTTP/1.0指出Web浏览器使用的HTTP版本。

应答:Web浏览器提交请求后,通过HTTP协议传送给Web服务器。Web服务器接到后,进行事务处理,处理结果又通过HTTP传回给Web浏览器,从而在Web浏览器上显示出所请求的页面。


然后返回 网页内容到客户端,客户端更具 Conntent-Type 来解析


代码实现

其实Web服务器也就是一个一般的Socket的服务器,只不过它遵守HTTP协议来实现的罢了


//// server.c//// David J. Malan// malan@harvard.edu//// feature test macro requirements#define _GNU_SOURCE#define _XOPEN_SOURCE 700#define _XOPEN_SOURCE_EXTENDED// limits on an HTTP request's size, based on Apache's// http://httpd.apache.org/docs/2.2/mod/core.html#define LimitRequestFields 50#define LimitRequestFieldSize 4094#define LimitRequestLine 8190// number of octets for buffered reads#define OCTETS 512// header files#include <stdio.h>#include <arpa/inet.h>#include <errno.h>#include <limits.h>#include <math.h>#include <signal.h>#include <stdbool.h>#include <stdlib.h>#include <string.h>#include <strings.h>#include <unistd.h>// typestypedef char octet;// prototypesbool connected(void);bool error(unsigned short code);void handler(int signal);ssize_t load(void);const char* lookup(const char* extension);ssize_t parse(void);void reset(void);void start(short port, const char* path);void stop(void);// server's rootchar* root = NULL;// file descriptor for socketsint cfd = -1, sfd = -1;// buffer for requestoctet* request = NULL;// FILE pointer for filesFILE* file = NULL;// buffer for response-bodyoctet* body = NULL;int main(int argc, char* argv[]){	// a global variable defined in errno.h that's "set by system	// calls and some library functions [to a nonzero value]	// in the event of an error to indicate what went wrong"	errno = 0;	// default to a random port	int port = 0;	// usage	const char* usage = "Usage: server [-p port] /path/to/root";	// parse command-line arguments	int opt;	while ((opt = getopt(argc, argv, "hp:")) != -1)	{		switch (opt)		{		// -h		case 'h':			printf("%s\n", usage);			return 0;			// -p port		case 'p':			port = atoi(optarg);			break;		}	}	// ensure port is a non-negative short and path to server's root is specified	if (port < 0 || port > SHRT_MAX || argv[optind] == NULL || strlen(argv[optind]) == 0)	{		// announce usage		printf("%s\n", usage);		// return 2 just like bash's builtins		return 2;	}	// start server	start(port, argv[optind]);	// listen for SIGINT (aka control-c)	signal(SIGINT, handler);	// accept connections one at a time	while (true)	{		// reset server's state		reset();		// wait until client is connected		if (connected())		{			// parse client's HTTP request			ssize_t octets = parse();			if (octets == -1)			{				continue;			}			// extract request's request-line			// http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html			const char* haystack = request;			char* needle = strstr(haystack, "\r\n");			if (needle == NULL)			{				error(400);				continue;			}			else if (needle - haystack + 2 > LimitRequestLine)			{				error(414);				continue;			}			char line[needle - haystack + 2 + 1];			strncpy(line, haystack, needle - haystack + 2);			line[needle - haystack + 2] = '\0';			// log request-line			printf("\n%s", line);			// TODO: validate request-line			char* method = line;			char* request_target;			char* version;			char* absolute_path;			char* s1;			s1 = strstr (line," ");			if (s1 == NULL || (version = strstr(s1 + 1, " ")) == NULL || strncmp(line + sizeof(line) - 2, "\r\n", 2) == 0)			{				error(400);				continue;			}			request_target = strdup(s1 + 1);			request_target[version - s1 - 1] = '\0';			//printf("request_target is %s, version is %s", request_target, version);			if (strncmp(method,"GET",3) != 0)			{				error(405);				continue;			}			if (strncmp(request_target, "/", 1) != 0)			{				error(501);				continue;			}			if (strchr(request_target, '\"') != NULL)			{				error(400);				continue;			}			if (strncmp (version + 1,"HTTP/1.1",8) != 0)			{				error(505);				continue;			}			if (strchr(request_target, '.') == NULL)			{				error(501);				continue;			}			// TODO: extract query from request-target			char *query = strchr(request_target, '?');			if (query == NULL)				query = "";			else				query = query + 1;			//printf("query is %s\n", query);			// TODO: concatenate root and absolute-path			absolute_path = strdup(request_target);//			printf("%s. %d\n", absolute_path, query-request_target);			if (strcmp(query, "") != 0)				absolute_path[query - request_target - 1] = '\0';			//printf("absolute path is %s\n", absolute_path);			char *path = (char*)malloc(1000);			strcpy(path, root);//			strcat(path, "/");			strcat(path, absolute_path);			//printf("path is %s\n", path);			// TODO: ensure path exists			if (access(path, F_OK) == -1) {				error(404);				continue;			}			// TODO: ensure path is readable			if (access(path, R_OK) == -1) {				error(403);				continue;			}			// TODO: extract path's extension			char* extension;			if ((extension = strrchr(absolute_path, '.')) == NULL) {				error(501);				continue;			}			extension = extension + 1;			//printf("extension is %s\n", extension);			// dynamic content			if (strcasecmp("php", extension) == 0)			{				// open pipe to PHP interpreter				char* format = "QUERY_STRING=\"%s\" REDIRECT_STATUS=200 SCRIPT_FILENAME=\"%s\" php-cgi";				char command[strlen(format) + (strlen(path) - 2) + (strlen(query) - 2) + 1];				sprintf(command, format, query, path);				file = popen(command, "r");				if (file == NULL)				{					error(500);					continue;				}				// load file				ssize_t size = load();				if (size == -1)				{					error(500);					continue;				}				// subtract php-cgi's headers from body's size to get content's length				haystack = body;				needle = memmem(haystack, size, "\r\n\r\n", 4);				if (needle == NULL)				{					error(500);					continue;				}				size_t length = size - (needle - haystack + 4);				// respond to client				if (dprintf(cfd, "HTTP/1.1 200 OK\r\n") < 0)				{					continue;				}				if (dprintf(cfd, "Connection: close\r\n") < 0)				{					continue;				}				if (dprintf(cfd, "Content-Length: %i\r\n", length) < 0)				{					continue;				}				if (write(cfd, body, size) == -1)				{					continue;				}			}			// static content			else			{				// look up file's MIME type				const char* type = lookup(extension);				if (type == NULL)				{					error(501);					continue;				}				// open file				file = fopen(path, "r");				if (file == NULL)				{					error(500);					continue;				}				// load file				ssize_t length = load();				if (length == -1)				{					error(500);					continue;				}				// TODO: respond to client				// respond with Status-Line				dprintf(cfd, "HTTP/1.1 200 OK\r\n");				// respond with Connection header				dprintf(cfd, "Connection: close\r\n");				// respond with Content-Length header				dprintf(cfd, "Content-Length: %i\r\n", length);				// respond with Content-Type header				dprintf(cfd, "Content-Type: text/html\r\n");				// respond with CRLF				dprintf(cfd, "\r\n");								write(cfd, body, length);			}			// announce OK			printf("\033[32m");			printf("HTTP/1.1 200 OK");			printf("\033[39m\n");		}	}}/** * Accepts a connection from a client, blocking (i.e., waiting) until one is heard. * Upon success, returns true; upon failure, returns false. */bool connected(void){	struct sockaddr_in cli_addr;	memset(&cli_addr, 0, sizeof(cli_addr));	socklen_t cli_len = sizeof(cli_addr);	cfd = accept(sfd, (struct sockaddr*) &cli_addr, &cli_len);	if (cfd == -1)	{		return false;	}	return true;}/** * Handles client errors (4xx) and server errors (5xx). */bool error(unsigned short code){	// ensure client's socket is open	if (cfd == -1)	{		return false;	}	// ensure code is within range	if (code < 400 || code > 599)	{		return false;	}	// determine Status-Line's phrase	// http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6.1	const char* phrase = NULL;	switch (code)	{	case 400: phrase = "Bad Request"; break;	case 403: phrase = "Forbidden"; break;	case 404: phrase = "Not Found"; break;	case 405: phrase = "Method Not Allowed"; break;	case 413: phrase = "Request Entity Too Large"; break;	case 414: phrase = "Request-URI Too Long"; break;	case 418: phrase = "I'm a teapot"; break;	case 500: phrase = "Internal Server Error"; break;	case 501: phrase = "Not Implemented"; break;	case 505: phrase = "HTTP Version Not Supported"; break;	}	if (phrase == NULL)	{		return false;	}	// template	char* template = "<html><head><title>%i %s</title></head><body><h1>%i %s</h1></body></html>";	char content[strlen(template) + 2 * ((int) log10(code) + 1 - 2) + 2 * (strlen(phrase) - 2) + 1];	int length = sprintf(content, template, code, phrase, code, phrase);	// respond with Status-Line	if (dprintf(cfd, "HTTP/1.1 %i %s\r\n", code, phrase) < 0)	{		return false;	}	// respond with Connection header	if (dprintf(cfd, "Connection: close\r\n") < 0)	{		return false;	}	// respond with Content-Length header	if (dprintf(cfd, "Content-Length: %i\r\n", length) < 0)	{		return false;	}	// respond with Content-Type header	if (dprintf(cfd, "Content-Type: text/html\r\n") < 0)	{		return false;	}	// respond with CRLF	if (dprintf(cfd, "\r\n") < 0)	{		return false;	}	// respond with message-body	if (write(cfd, content, length) == -1)	{		return false;	}	// announce Response-Line	printf("\033[31m");	printf("HTTP/1.1 %i %s", code, phrase);	printf("\033[39m\n");	return true;}/** * Loads file into message-body. */ssize_t load(void){	// ensure file is open	if (file == NULL)	{		return -1;	}	// ensure body isn't already loaded	if (body != NULL)	{		return -1;	}	// buffer for octets	octet buffer[OCTETS];	// read file	ssize_t size = 0;	while (true)	{		// try to read a buffer's worth of octets		ssize_t octets = fread(buffer, sizeof(octet), OCTETS, file);		// check for error		if (ferror(file) != 0)		{			if (body != NULL)			{				free(body);				body = NULL;			}			return -1;		}		// if octets were read, append to body		if (octets > 0)		{			body = realloc(body, size + octets);			if (body == NULL)			{				return -1;			}			memcpy(body + size, buffer, octets);			size += octets;		}		// check for EOF		if (feof(file) != 0)		{			break;		}	}	return size;}/** * Handles signals. */void handler(int signal){	// control-c	if (signal == SIGINT)	{		// ensure this isn't considered an error		// (as might otherwise happen after a recent 404)		errno = 0;		// announce stop		printf("\033[33m");		printf("Stopping server\n");		printf("\033[39m");		// stop server		stop();	}}/** * Returns MIME type for supported extensions, else NULL. */const char* lookup(const char* extension){	// TODO	if (strcasecmp(extension, "css"))			return "text/css";	if (strcasecmp(extension, "html"))			return "text/html";	if (strcasecmp(extension, "gif"))			return "image/gif";	if (strcasecmp(extension, "ico"))			return "image/x-icon";	if (strcasecmp(extension, "jpg"))			return "image/jpeg";	if (strcasecmp(extension, "js"))			return "text/javascript";	if (strcasecmp(extension, "png"))			return "image/png";	return NULL;}/** * Parses an HTTP request. */ssize_t parse(void){	// ensure client's socket is open	if (cfd == -1)	{		return -1;	}	// ensure request isn't already parsed	if (request != NULL)	{		return -1;	}	// buffer for octets	octet buffer[OCTETS];	// parse request	ssize_t length = 0;	while (true)	{		// read from socket		ssize_t octets = read(cfd, buffer, sizeof(octet) * OCTETS);		if (octets == -1)		{			error(500);			return -1;		}		// if octets have been read, remember new length		if (octets > 0)		{			request = realloc(request, length + octets);			if (request == NULL)			{				return -1;			}			memcpy(request + length, buffer, octets);			length += octets;		}		// else if nothing's been read, socket's been closed		else		{			return -1;		}		// search for CRLF CRLF		int offset = (length - octets < 3) ? length - octets : 3;		char* haystack = request + length - octets - offset;		char* needle = memmem(haystack, request + length - haystack, "\r\n\r\n", 4);		if (needle != NULL)		{			// trim to one CRLF and null-terminate			length = needle - request + 2 + 1;			request = realloc(request, length);			if (request == NULL)			{				return -1;			}			request[length - 1] = '\0';			break;		}		// if buffer's full and we still haven't found CRLF CRLF,		// then request is too large		if (length - 1 >= LimitRequestLine + LimitRequestFields * LimitRequestFieldSize)		{			error(413);			return -1;		}	}	return length;}/** * Resets server's state, deallocating any resources. */void reset(void){	// free response's body	if (body != NULL)	{		free(body);		body = NULL;	}	// close file	if (file != NULL)	{		fclose(file);		file = NULL;	}	// free request	if (request != NULL)	{		free(request);		request = NULL;	}	// close client's socket	if (cfd != -1)	{		close(cfd);		cfd = -1;	}}/** * Starts server. */void start(short port, const char* path){	// path to server's root	root = realpath(path, NULL);	if (root == NULL)	{		stop();	}	// ensure root exists	if (access(root, F_OK) == -1)	{		stop();	}	// ensure root is executable	if (access(root, X_OK) == -1)	{		stop();	}	// announce root	printf("\033[33m");	printf("Using %s for server's root, size is %ld", root, sizeof(root));	printf("\033[39m\n");	// create a socket	sfd = socket(AF_INET, SOCK_STREAM, 0);	if (sfd == -1)	{		stop();	}	// allow reuse of address (to avoid "Address already in use")	int optval = 1;	setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));	// assign name to socket	struct sockaddr_in serv_addr;	memset(&serv_addr, 0, sizeof(serv_addr));	serv_addr.sin_family = AF_INET;	serv_addr.sin_port = htons(port);	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);	if (bind(sfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1)	{		stop();	}	// listen for connections	if (listen(sfd, SOMAXCONN) == -1)	{		stop();	}	// announce port in use	struct sockaddr_in addr;	socklen_t addrlen = sizeof(addr);	if (getsockname(sfd, (struct sockaddr*) &addr, &addrlen) == -1)	{		stop();	}	printf("\033[33m");	printf("Listening on port %i", ntohs(addr.sin_port));	printf("\033[39m\n");}/** * Stop server, deallocating any resources. */void stop(void){	// preserve errno across this function's library calls	int errsv = errno;	// reset server's state	reset();	// free root, which was allocated by realpath	if (root != NULL)	{		free(root);	}	// close server socket	if (sfd != -1)	{		close(sfd);	}	// terminate process	if (errsv == 0)	{		// success		exit(0);	}	else	{		// announce error		printf("\033[33m");		printf("%s", strerror(errsv));		printf("\033[39m\n");		// failure		exit(1);	}}
ログイン後にコピー


编译方法
gcc -o server server.c -lm

运行方法

./server -p 8080 public 

其中public目录是存放html和php文件的





関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート