Linux28 -- http协议相关和代码尝试,设计服务器与网页链接发送数据

tcp、udp的应用:
在这里插入图片描述

一、http协议存在两个报文,一个为请求报文(浏览器主动发给服务器的),一个为应答报文

在这里插入图片描述

1、请求报文

在这里插入图片描述
在这里插入图片描述

2、应答报文

在这里插入图片描述
在这里插入图片描述

二、tcp服务器 – 让浏览器作为客户端,去链接使用基于c语言的服务器,观察收到的信息并回复相关数据

DNS域名解析
在Windows上ping一下百度:
在这里插入图片描述

通讯过程

其中 14.215.177.38 就为百度的服务器,在不同地域,其地址可能不一样。

HTTP协议使用 80号 协议。HTTPs 使用的是43号端口。
在这里插入图片描述
浏览器发来请求,解析以后发回浏览器,一般会发送html文件(网页结构),css布局,js,和一些资源(.jpg等)。
可以在服务器段创建一个文件,将该文件发送给浏览器。

三、代码实现示例

服务器启动后,浏览器链接,浏览器可向服务器请求数据,服务器发送数据

1、完成链接,回复网页ok

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

//创建套接字
int socket_init();

int recv_request(int c);//接收请求
void send_response(int c);//恢复应答


int main()
{
    int sockfd = socket_init();
    if(sockfd == -1)
    {
        printf("socket err/n");
        exit(0);
    }

    while( 1 )
    {
        //接受客户端的链接
        struct sockaddr_in caddr;
        int len = sizeof(caddr);//大小
        int c = accept(sockfd,(struct sockaddr*)&caddr,&len);//会阻塞  等待客户端链接,这里客户端为浏览器,及等待浏览器连接
        if( c < 0 )
        {
            continue;
        }

        //浏览器的收和发
        recv_request(c);//接收请求
        send_response(c);//回复响应,回复应答
        close(c);

    }

}

int recv_request(int c)//接收请求
{
    char buff[1024] = {0};
    int n = recv(c,buff,1023,0);
    if( n <= 0)
    {
        return -1;
    }
    printf("recv:\n%s\n",buff);

}
void send_response(int c)//回复应答
{
    send(c,"ok",2,0);
}

int socket_init()
{
    //创建流式套接字
    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);//端口,http协议使用80号端口,小于1024的端口需要使用管理员权限
    saddr.sin_addr.s_addr = inet_addr("0.0.0.0");//自身的真实ip,这里为模拟的ip

    int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
    if( res == -1)
    {
        return -1;
    }

    if( listen(sockfd,5) == -1 )
    {
        return -1;
    }

    return sockfd;
}

运行结果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2、回复正常网页,这里回复html文件

在这里插入图片描述

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>

//创建套接字
int socket_init();

int recv_request(int c);//接收请求
void send_response(int c);//恢复应答


int main()
{
    int sockfd = socket_init();
    if(sockfd == -1)
    {
        printf("socket err/n");
        exit(0);
    }

    while( 1 )
    {
        //接受客户端的链接
        struct sockaddr_in caddr;
        int len = sizeof(caddr);//大小
        int c = accept(sockfd,(struct sockaddr*)&caddr,&len);//会阻塞  等待客户端链接,这里客户端为浏览器,及等待浏览器连接
        if( c < 0 )
        {
            continue;
        }

        //浏览器的收和发
        recv_request(c);//接收请求
        send_response(c);//回复响应,回复应答
        close(c);

    }

}

int recv_request(int c)//接收请求
{
    char buff[1024] = {0};
    int n = recv(c,buff,1023,0);
    if( n <= 0)
    {
        return -1;
    }
    printf("recv:\n%s\n",buff);

}
void send_response(int c)//回复应答
{
    //打开文件
    int fd = open("index.html",O_RDONLY);//这里写死为index.html
    if( fd == -1 )
    {
    	return;
    }
    
    //计算文件的大小
    int filesize = lseek(fd,0,SEEK_END);
    lseek(fd,0,SEEK_SET);
    
    //组装一个报头
    char http_buff[1024] = {"HTTP 1.1 200 OK\r\n"};
    strcat(http_buff,"Server:http\r\n");
    
    //将大小拼接到报文中
    sprintf(http_buff+strlen(http_buff),"Content_Length: %d\r\n",filesize);
    //再拼接一个\r\n,区分头部和数据
    strcat(http_buff,"\r\n");
    
    //发送信息
    send(c,http_buff,strlen(http_buff),0);//报头部分
    //发送数据部分
    char data[1024] = {0};
    int n = 0;
    while( n = read(fd,data,1024) >0 )
    {
    	send(c,data,n,0);
    }
    close(fd);
}

int socket_init()
{
    //创建流式套接字
    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);//端口,http协议使用80号端口,小于1024的端口需要使用管理员权限
    saddr.sin_addr.s_addr = inet_addr("0.0.0.0");//自身的真实ip,这里为模拟的ip

    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=utf8>
        <title>页面</title>
        </head>

        <body>
            <h1>你好
            </body>
    </html>

运行结果:

在这里插入图片描述
这里出现乱码,是因为解码和编码的格式不一样。
缺少代码<meta charset=utf8>
在这里插入图片描述
添加<meta charset=utf8>

在这里插入图片描述
右键查看网页源码,即为所写的index.html文件:
在这里插入图片描述

3、多线程实现连接,并返回报文

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <pthread.h>

#define  BUFF_SIZE   4096
#define  PATH        "/home/stu/桌面/daima/http"//存放资源文件的位置
#define  ER404      "404 页面找不到" 
#define  HEAD_SIZE  1024
struct ArgNode
{
    pthread_t id;
    int c;
};

int socket_init();
int http_reponse(int c, char* filename)
{
    if ( filename == NULL )
    {
        return -1;
    }

    char path[256] = {PATH};//将资源文件路径存放
    strcat(path,filename);
    printf("path:%s\n",path);//打印出全路径
    int fd = open(path,O_RDONLY);
    if ( fd == -1 )//打开失败或无资源文件
    {
        send(c,ER404,strlen(ER404),0);
        return -1;
    }

    int filesize = lseek(fd,0,SEEK_END);//空文件或者错误
    if ( filesize <= 0 )//=0文件没有内容,为空文件,<0出错
    {
        return -1;
        close(fd);
    }
    //将偏移量重新移动到起始位置
    lseek(fd,0,SEEK_SET);

    //组装报头
    char http_head[HEAD_SIZE] = {0};
    strcat(http_head,"HTTP/1.1 200 OK\r\n");
    strcat(http_head,"Server: myhttp\r\n");
    sprintf(http_head+strlen(http_head),"Content-Length: %d\r\n",filesize);
    strcat(http_head,"\r\n");//http报头组装完成

    send(c,http_head,strlen(http_head),0);//发送http报头
    //这里的写法较为简单粗暴,可能会出错
    printf("send:\n%s\n",http_head);
    
    //发送数据
    int num = 0;
    char data[BUFF_SIZE];
    while( (num = read(fd,data,BUFF_SIZE)) > 0 )
    {
        send(c,data,num,0);
    }
    
    close(fd);

    return 0;
}
char* get_filename(char buff[])
{
    if ( buff == NULL )
    {
        return NULL;
    }
    char* ptr = NULL;
    char* s = strtok_r(buff," ",&ptr);
    if ( s == NULL )
    {
        return NULL;
    }
    printf("请求方法:%s\n",s);

    s = strtok_r(NULL," ",&ptr);
    if ( s == NULL )
    {
        return NULL;
    }

    if ( strcmp(s,"/") == 0 )
    {
        return "/index.html";
    }

    return s;
}
void* thread_fun(void* arg)
{
    struct ArgNode * p = (struct ArgNode*)arg;
    if (p == NULL )
    {
        pthread_exit(NULL);//退出线程,进程其他线程可以继续执行
    }

    int c = p->c;

    while( 1 )
    {
        char buff[BUFF_SIZE] = {0};
        int n = recv(c,buff,BUFF_SIZE-1,0);//接收浏览器的http请求报文
        if ( n <= 0 )
        {
            printf("连接关闭,读取错误er:%d\n",n);
            break;//浏览器关闭连接
        }

        printf("%s\n",buff);
        char* filename = get_filename(buff);
        if ( filename == NULL )
        {
            continue;
        }
        printf("请求资源名:%s\n",filename);//filename为文件名
        if( http_reponse(c,filename) == -1 )//生成应答报文
        {
            break;
        }

    }

    close(c);
    free(p);//释放堆区空间
    pthread_exit(NULL);
}
int main()
{
    int sockfd = socket_init();
    if ( sockfd == -1 )
    {
        printf("socket err\n");
        exit(0);
    }

    while( 1 )
    {
        struct sockaddr_in caddr;
        int len = sizeof(caddr);
        int c = accept(sockfd,(struct sockaddr*)&caddr,&len);//阻塞  -- 浏览器连接 ,返回
        if ( c < 0 )
        {
            continue;
        }

        struct ArgNode * p = (struct ArgNode*)malloc(sizeof(struct ArgNode));
        if ( p == NULL )
        {
            close(c);
            continue;
        }

        p->c = c;//
        if ( pthread_create(&(p->id),NULL,thread_fun,p) != 0 )
        {
            free(p);
            close(c);
        }

    }

}

int socket_init()
{
    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);//http 80,root 
    saddr.sin_addr.s_addr = inet_addr("0.0.0.0");

    int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
    if ( res == -1 )
    {
        return -1;
    }

    if ( listen(sockfd,64) == -1 )
    {
        return -1;
    }

    return sockfd;
}


运行结果:

运行服务器:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
手动输入 地址加 index.html

在这里插入图片描述
结果为:

在这里插入图片描述
在目录地下添加图片和图标ico
在这里插入图片描述
进行访问

在这里插入图片描述
就能得到图片

在这里插入图片描述
改进index.html 文件

<html>
    <head>
        <meta charset=utf8>
        <title>百度一下,你就知道</title>
        </head>

        <body>
            <h1>你好<h1>
            </body>
</html>

在这里插入图片描述

<html>
    <head>
        <meta charset=utf8>
        <title>百度一下,你就知道</title>
        </head>

        <body background="src1.webp">
            <center>
            <h1>你好秋天~<h1>
                    </center>
            </body>
</html>

在这里插入图片描述
输入没有的文件,这里回复404,但由于未处理,这里回复为乱码。
在这里插入图片描述

三、如何在Linux内查看自己的IP地址:

在 linux 下可以通过两个命令来查看本机的 IP 地址(注意要进入管理员模式):

1.支持包括 Linux 在内的所有 Unix 系统。
#/sbin/ifconfig
2. 对于Linux 而言,也可以使用 ip 命令查看,提示:没有ifconfig命令时可以用此命令查看
#ipaddrshow

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值