HTTP概要
理解Web服务器端
HTTP是Hypertext Transfer Protocol的缩写, Hypertext(超文本)是可以根据客户端请求而跳转的结构化信息。
HTTP协议:HTTP是以超文本传输为目的而设计的应用层协议,这种协议同样属于基于TCP/IP实现的协议,因此,可以直接实现HTTP。从结果上看,实现该协议相当于实现Web服务器端。浏览器属于基于套接字的客户端,因此连接到任意Web服务器端时,浏览器内部会创建套接字。只不过浏览器多了项功能,它将服务器端传输的HTML格式的超文本解析为可读性较强的试图。 总之,Web服务器端是以HTTP协议为基础传输超文本的服务器端。
HTTP
无状态的Stateless协议
HTTP协议的请求及响应方式设计:
服务器端响应客户端请求后立即断开连接,服务器端不会维持客户端状态。即使同一客户端再次发送请求,服务器端也无法辨认出是原先那个,而会以相同方式处理新请求。因此HTTP又称为:"无状态的Stateless协议".
为了弥补HTTP无法保持连接的缺点,Web编程中通常会使用Cokkie和Session技术。
请求消息(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); }