• 模拟web服务器http请求应答


    我们在浏览器打开网页,其实是向远端服务器提出页面发送请求,远端服务器在接到请求后,就开始执行请求页面的程序文件,然后将执行结果通过html格式,发送到你的浏览器,再显示出来。以下用百度(www.baidu.com)为例:

    我们可以通过GET请求可以获得,百度的http应答格式,详情点我查看。或者通过 "$curl -l www.baidu.com"的方式获得,如果提示没有curl命令则表示你的Linux没有安装curl,安装方法也很简单,拿ubuntu为例,输入以下命令"$sudo apt-get install curl",一路回车即可安装。

     查询命令也很简单,输入"$curl -i www.baidu.com"

    得到百度的服务器的http应答头部信息后,我们可以仿照这种格式,写一个我们自己的服务端。

    百度服务器响应http应答头部信息

    HTTP/1.1 200 OK
    Accept-Ranges: bytes
    Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform
    Connection: Keep-Alive
    Content-Length: 2381
    Content-Type: text/html
    Date: Tue, 29 Oct 2019 01:11:38 GMT
    Etag: "588604c8-94d"
    Last-Modified: Mon, 23 Jan 2017 13:27:36 GMT
    Pragma: no-cache
    Server: bfe/1.0.8.18
    Set-Cookie: BDORZ=27315; max-age=86400; domain=.baidu.com; path=/

    这里我们先简单的模拟其中的部分:

    1. HTTP/1.1 200 OK
    2. Server: myhttp
    3. Content-Length: 330
    4. 发送html文件

    核心思想:将浏览器的请求信息解析,提取出相应内容作为返回信息的方法。

    服务器代码实现如下:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    #include <assert.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <fcntl.h>
    
    #define		PATH    "/home/stu/mydir/html_test"  /*这里是html文件所在的根目录*/
    
    int create_socket();
    int main()
    {
    	int sockfd = create_socket();
    	assert( sockfd != -1 );
    
    	while( 1 )
    	{
    		struct sockaddr_in caddr;
    		int len = sizeof(caddr);
    		int c = accept(sockfd,(struct sockaddr*)&caddr,&len);    /*accept()返回新的连接套接子,表示网络已经存在点对点的连接*/
    		if ( c < 0 )
    		{
    			continue;
    		}
    
    		char buff[1024] = {0};    /*buff用于接收浏览器(客户端)的请求*/
    		int res = recv(c,buff,1023,0);
    
    		char * s = strtok(buff," ");    /*分割字符串函数,用于提取http中的信息*/
    		if ( s == NULL )
    		{
    			close(c);
    			continue;
    		}
    
    		printf("方法:%s
    ",s);        /*服务端打印请求方法,如:GET*/
    		printf("read:
    %s
    ",buff);    /*服务端打印读取到信息*/
    
    		s = strtok(NULL," ");
    		if ( s == NULL )
    		{
                            send(c,"404",3,0);    //该路经下没有该文件,404错误 
    			close(c);
    			continue;
    		}
    
    		if ( strcmp(s,"/") == 0 )    /*如果客户端没有标明想访问哪一个文件,则默认访问index.html文件*/
    		{
    			s = "/index.html";
    		}
    
    		char path[256] = {PATH};    /*网页文件路径*/
    		strcat(path,s);            /*文件路径+文件*/
    
    		printf("file:%s
    ",path);    /*服务端打印文件的地址*/
    
    		int fd = open(path,O_RDONLY);    /*定义文件描述符,读取文件内容,发送回浏览器(客户端)*/
    		if ( fd == -1 )
    		{
    			close(c);
    			continue;
    		}
    
    		int size = lseek(fd,0,SEEK_END);    /*返回文件大小*/
    		lseek(fd,0,SEEK_SET);                /*文件指针回正,指向文件开头*/
    
                     /*http应答头部信息拼装与head_buff中*/
    		char head_buff[256] = {"HTTP/1.1 200 OK
    "};   
    		strcat(head_buff,"Server: myhttp
    ");
    		sprintf(head_buff+strlen(head_buff),"Content-Length: %d
    ",size);
    		strcat(head_buff,"
    ");
    
                    /*流式数据,可多次发送,先发送应答头部信息*/
    		send(c,head_buff,strlen(head_buff),0);
    
                    /*读取index.html文件,发送回客户端*/
    		char data[1024] = {0};
    		int num = 0;
    		while( ( num = read(fd,data,1024)) > 0 )    /*循环读取,直到读完整个文件*/
    		{
    			send(c,data,num,0);    /*边读取边发送*/
    		}
    
    		close(fd);    /*关闭文件描述符*/
    	       	close(c);    /*短连接,断开连接*/
    
    	}
    
            close(sockfd);
            exit(0);
    
    }
    
    //封装套接子为create_socker()方法
    int create_socket()
    {
    	int sockfd = socket(AF_INET,SOCK_STREAM,0);
    	if ( sockfd == -1 )
    	{
    		return -1;
    	}
    
    	struct sockaddr_in saddr;
    	memset(&saddr,0,sizeof(saddr));
    	saddr.sin_family = AF_INET;
    	saddr.sin_port = htons(80);//root
    	saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    
    	int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
    	if ( res == -1 )
    	{
    		return -1;
    	}
    
    	if ( listen(sockfd,5) == -1 )
    	{
    		return -1;
    	}
    
    	return sockfd;
    }

    服务器代码已经实现,现在只需一个index.html文件就可以进行测试。

    <html>
    	<head>
    		<meta charset=utf-8>
    		<title>我的html页面</title>
    	</head>
    
    	<body background="11.PNG">
    		<center>
    			<h3><测试标题> </h3>
    			<hr>
    			<br>
    			这是第一行,<br>
    			这是第二行. <br>
    			这是第三行,<br>
    			这是第四行. <br>
    			</center>
    	</body>
    </html>

    简单的写一个html文档,把文档命名为 index.html ,存放于 我们定义的路径下。

    打开浏览器,输入 localhost 或 127.0.0.1 即可看见我们的网页内容了,测试截图如下:


     

     在上图中我们可以看到,浏览器显示的是经过处理的文件内容,我们服务端发送的应答头部信息和原本的内容已经经过处理,如果我们想看到服务端发送的具体内容,我们还可以自己再写一个客户端,让客户端连接自己的服务端并打印出服务端发送的内容。

    客户端代码:

    #include <stdio.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <sys/types.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    int main(){
            int sockfd;
            int len;
            struct sockaddr_in address;
            int result;
            char *strings="GET / HTTP/1.1
    Host: 127.0.0.1
    Connection: Close
    
    ";
            char ch;
            
            sockfd = socket(AF_INET, SOCK_STREAM, 0);
            address.sin_family = AF_INET;
            address.sin_addr.s_addr = inet_addr("127.0.0.1");
            address.sin_port = htons(80);
    
            len = sizeof(address);
            result = connect(sockfd,  (struct sockaddr *)&address, len);
            if(result == -1){
                    perror("oops: client1");
                    return 1;
            }
    
            write(sockfd,strings,strlen(strings)); 
            
            while(read(sockfd,&ch, 1)){
                    printf("%c", ch);
            }
            close(sockfd);
     
            return 0;
    }

    运行服务端、在运行客户端,我们可以在客户端上看到如下内容:

    ps:实现本机访问后也可以试试局域网内互相访问,只需修改服务器端代码,把ip地址改成做服务端的主机的ip地址即可,在另一台主机上输入服务端主机的ip即可访问该网页内容。此博客在上一篇博客Linux套接子(c语言)模拟http请求、应答 的基础上做了加强,更加接近真实的http访问协议。

  • 相关阅读:
    随机ID添加
    学生ID查询
    node.js基础
    冒泡排序
    循环判断语句
    vue.js详细教程--优优优
    final注意事项
    HashMap Hashtable区别
    java中间件
    JSP错误页面
  • 原文地址:https://www.cnblogs.com/TaoR320/p/12680171.html
Copyright © 2020-2023  润新知