TCP/IP网络编程 (End):制作HTTP服务器端

HTTP概要

理解Web服务器端

HTTP是Hypertext Transfer Protocol的缩写, Hypertext(超文本)是可以根据客户端请求而跳转的结构化信息。

 

HTTP协议:HTTP是以超文本传输为目的而设计的应用层协议,这种协议同样属于基于TCP/IP实现的协议,因此,可以直接实现HTTP。从结果上看,实现该协议相当于实现Web服务器端。浏览器属于基于套接字的客户端,因此连接到任意Web服务器端时,浏览器内部会创建套接字。只不过浏览器多了项功能,它将服务器端传输的HTML格式的超文本解析为可读性较强的试图。 总之,Web服务器端是以HTTP协议为基础传输超文本的服务器端。

 

HTTP

无状态的Stateless协议

HTTP协议的请求及响应方式设计:

       

服务器端响应客户端请求后立即断开连接,服务器端不会维持客户端状态。即使同一客户端再次发送请求,服务器端也无法辨认出是原先那个,而会以相同方式处理新请求。因此HTTP又称为:"无状态的Stateless协议".

 

为了弥补HTTP无法保持连接的缺点,Web编程中通常会使用CokkieSession技术。

 

请求消息(Request Message)的结构

介绍客户端向服务器端发送的请求消息的结构。Web服务器端需要解析并响应客户端请求,客户端和服务器端之间的数据请求方式标准如图:                                            

                                                         

 

请求行含有请求方式信息。典型的请求方式有GET和POST,GET主要用于请求数据,POST主要用于传输数据。

 

我们只实现响应GET请求的Web服务器端。

请求行信息 : GET /index.html HTTP/1.1 含义如下:

请求(GET)index.html文件,希望以1.1版本的HTTP协议进行通信。

 

消息头文件包含发送请求的浏览器信息,用户认证信息等关于HTTP信息的附加消息。

消息体中装有客户端向服务器端传输的数据,为了装入数据,需要以POST方式发送请求。

 

 

响应消息(Response Message)的结构

介绍Web服务器端向客户端传递的响应消息的结构。

                                                

状态行中含有关于请求的状态消息。这是与请求消息相比最显著的区别。

状态行中含有关于客户端请求的处理结果:

例如客户端请求index.html文件时,表示index.html文件是否存在,服务器端是否发生问题而无法响应等不同情况的信息将写入状态行。

 

上图中“HTTP/1.1 200 OK"含义:

我想用HTTP1.1版本进行响应,你的请求已正确处理(200 OK);

 

表示客户端请求的执行结果的数字称为状态码,典型有以下几种:

 

消息头中含有传输的数据类型和长度等信息。

上图的消息头含有如下信息:

服务器端名为SimpleWebserver,传输的数据类型为text/html(html格式u的文本数据)。数据长度不超过2048字节。

 

插入一个空行后,通过消息体发送客户端请求的文件数据。

 

实现简单的Web服务器端

现在开始在HTTP协议的基础上编写Web服务器端。

 

实现基于LInix的多线程Web服务器端

此示例主要用于复习之前的知识,无实际用处:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<pthread.h>
#define BUF_SIZE 1024
#define SMALL_BUF 100

void* request_handler(void *arg);
void send_data(FILE* fp, char* ct, char* file_name);
char* content_type(char* file);
void send_error(FILE* fp);
void error_handling(char *message);

int main(int argc,char *argv[])
{
	int serv_sock,clnt_sock;
	struct sockaddr_in serv_adr,clnt_adr;
	int clnt_adr_sz;
	char buf[BUF_SIZE];
	pthread_t t_id;
	if (argc != 2) {
		printf("Usage: %s <port> \n",argv[0]);
		exit(1);
	}

	serv_sock = socket(PF_INET,SOCK_STREAM,0);
	memset(&serv_adr,0,sizeof(serv_adr));
	serv_adr.sin_family = AF_INET;
	serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
	serv_adr.sin_port = htons(atoi(argv[1]));
	if (bind(serv_sock,(struct sockaddr*)&serv_adr,sizeof(serv_adr)) == -1)
		error_handling("bind() error!");
	if(listen(serv_sock,20) == -1)
		error_handling("listen() error");

	while(1)
	{
		clnt_adr_sz = sizeof(clnt_adr);
		clnt_sock = accept(serv_sock,(struct sockaddr*)&clnt_adr,&clnt_adr_sz);
		printf("Connection Request : %s:%d \n",
				inet_ntoa(clnt_adr.sin_addr),ntohs(clnt_adr.sin_port));
		pthread_create(&t_id,NULL,request_handler,&clnt_sock);
		pthread_detach(t_id);
	}
	close(serv_sock);
	return 0;
}


void* request_handler(void *arg)
{
	int clnt_sock = *((int*)arg);
	char req_line[SMALL_BUF];
	FILE* clnt_read;
	FILE* clnt_write;

	char method[10];
	char ct[15];
	char file_name[30];

	clnt_read  = fdopen(clnt_sock,"r");
	clnt_write = fdopen(dup(clnt_sock),"w");
	fgets(req_line,SMALL_BUF,clnt_read);
	if(strstr(req_line,"HTTP/") == NULL)
	{
		send_error(clnt_write);
		fclose(clnt_read);
		fclose(clnt_write);
		return;
	}
	strcpy(method,strtok(req_line,"/"));
	strcpy(file_name,strtok(NULL,"/"));
	strcpy(ct,content_type(file_name));
	if (strcmp(method, "GET") != 0)
	{
		send_error(clnt_write);
		fclose(clnt_read);
		fclose(clnt_write);
		return ;
	}

	fclose(clnt_read);
	send_data(clnt_write,ct,file_name);
}

void send_data(FILE* fp, char* ct, char* file_name)
{
	char protocol[] = "HTTP/1.0 200 OK\r\n";
	char server[] = "Server:Linux Web Server \r\n";
	char cnt_len[] = "Content-length:2048\r\n";
	char cnt_type[SMALL_BUF];
	char buf[BUF_SIZE];
	FILE* send_file;

	sprintf(cnt_type,"Content-type:%s \r\n\r\n",ct);
	send_file = fopen(file_name,"r");
	if (send_file == NULL)
	{
		send_error(fp);
		return ;
	}

	/* 传输头信息 */
	fputs(protocol,fp);
	fputs(server,fp);
	fputs(cnt_len,fp);
	fputs(cnt_type,fp);

	/* 传输请求数据 */
	while(fgets(buf,BUF_SIZE,send_file) != NULL)
	{
		fputs(buf,fp);
		fflush(fp);
	}
	fflush(fp);
	fclose(fp);
}

char* content_type(char* file)
{
	char extension[SMALL_BUF];
	char file_name[SMALL_BUF];
	strcpy(file_name,file);
	strtok(file_name,".");
	strcpy(extension,strtok(NULL,"."));

	if(!strcmp(extension,"html") || !strcmp(extension,"html"))
		return "text/html";
	else 
		return "text/plain";
}

void send_error(FILE* fp)
{
	char protocol[] = "HTTP/1.0 400 Bad Request\r\n";
	char server[] = "Server:Linux Web Server \r\n";
	char cnt_len[] = "Content-length:2048\r\n";
	char cnt_type[] = "Content-type:text/html\r\n\r\n";
	char content[] = "<html><head><title>NETWORK</title></head>"
		"<body><font size=+5><br>发生错误! 查看请求文件名和请求方式!"
		"</font></body></html>";

	fputs(protocol,fp);
	fputs(server,fp);
	fputs(cnt_len,fp);
	fputs(cnt_type,fp);
	fflush(fp);
}

void error_handling(char *message)
{
	fputs(message,stderr);
	fputc('\n',stderr);
	exit(1);
}

猜你喜欢

转载自blog.csdn.net/amoscykl/article/details/80330706