• 网络编程与HTTP协议


    目录

    一、计算机网络基础

    1. 网络

    • 网络:由若干结点和连接这些结点的链路组成,网络中的结点可以是计算机,交换机、路由器等设备。
      网络设备有:交换机、路由器、集线器
      传输介质有:双绞线、同轴电缆、光纤

    • 互联网:把多个网络连接起来就构成了互联网。目前最大的互联网就是我们常说的因特网。

    • IP地址
      IP 地址就是给因特网上的每一个主机(或路由器)的每一个接口分配的一个在全世界范围内唯一的标识符。IP 地址因其特殊的结构使我们可以在因特网上很方便地进行寻址。
      IP 地址有分 IPV4 和 IPV6 两种类别格式,IPV4 是类似”A.B.C.D”的格式,它是 32 位的,用“.”分成四个段,每个段是 8 个位(值为 0-255),用 10 进制表示。IPV6 地址是 128位。
      在这里插入图片描述

    • MAC地址
      在局域网中,硬件地址又称为物理地址或者 MAC 地址,长度 48 位,是固化在计算机适配器的 ROM 中的地址。

      MAC和IP都可唯一标识一台主机,为什么有了MAC还需要IP呢?
      (1)IP便于寻址;
      (2)IP可表示在网络中地变化,而MAC不能。比如当我们把一个笔记本从一个城市带到另一个城市时,虽然地理位置改变了,但是电脑在局域网中的“地址”仍然不变。由此可见,局域网上某个主机的“地址”根本不能告诉我们这台主机位于什么地方。所以在网络上方便寻找某个主机,还得需要IP地址来完成。

    • 端口号
      在一台主机上用来唯一标识一个应用程序(进程)。
      在这里插入图片描述

    2. 网络分层模型

    在这里插入图片描述
    网络分层的好处:
    在这里插入图片描述

    二、Socket网络编程

    将数据发送到网络时规定整形数据使用大端字节序,所以也把大端字节序成为网络字节序列

    1. TCP编程流程

    在这里插入图片描述
    listen的作用:
    在这里插入图片描述
    四次挥手也可以演化成三次(二三步和一起)

    TCP四次挥手里,第二次和第三次挥手之间,是有可能有数据传输的。第三次挥手的目的是为了告诉主动方,“被动方没有数据要发了”。所以,在第一次挥手之后,如果被动方没有数据要发给主动方。第二和第三次挥手是有可能合并传输的。这样就出现了三次挥手。

    如果有数据要发,就不能是三次挥手了吗?上面提到的是没有数据要发的情况,如果第二、第三次挥手之间有数据要发,就不可能变成三次挥手了吗?并不是。TCP中还有个特性叫延迟确认。可以简单理解为:接收方收到数据以后不需要立刻马上回复ACK确认包。在此基础上,不是每一次发送数据包都能对应收到一个 ACK 确认包,因为接收方可以合并确认。 而这个合并确认,放在四次挥手里,可以把第二次挥手、第三次挥手,以及他们之间的数据传输都合并在一起发送。因此也就出现了三次挥手。
    在这里插入图片描述

    TCP服务器端代码ser.c

    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    
    int main()
    {
      int sockfd=socket(AF_INET,SOCK_STREAM,0);//创建监听套接字,使用ipv4和tcp协议
      assert(sockfd!=-1);
      struct sockaddr_in saddr,caddr;//套接字地址。服务器端Ip,port;客户端ip,port
      memset(&saddr,0,sizeof(saddr));
      saddr.sin_family=AF_INET;
      saddr.sin_port=htons(6000);//1024以内的为知名端口,4096以内的为保留端口,大于4096的为临时端口
      saddr.sin_addr.s_addr=inet_addr("127.0.0.1");//指定ip地址,这里用回环序列进行测试,inet_addr将字符串转成无符号整形
      int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));//地址绑定,为套接字指定一个ip+port,即地址
      assert(res!=-1);
      res=listen(sockfd,5);//监听端口,5在linux系统上指的是监听已完成三次握手的监听队列的大小
      while(1)//服务器循环接收客户端连接
      {
        int len=sizeof(caddr);
        int c=accept(sockfd,(struct sockaddr*)&caddr,&len);//c是连接套接字,接受连接,可能阻塞
        if(c<0)
        {
          perror("accept error");
          continue;
        }
        printf("c=%d
    ",c);
        while(1)
        {
          char buff[128]={0};
          int n=recv(c,buff,127,0);//recv返回值为0,说明对端关闭了
          if(n<=0)
          {
            break;
          }
          printf("buff(%d)=%s",n,buff);
          send(c,"0k",2,0);
        }
        printf("one client over!
    ");
        close(c);//四次挥手
      }
      return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    TCP客户端代码cli.c

    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    
    int main()
    {
      int sockfd=socket(AF_INET,SOCK_STREAM,0);
      assert(sockfd!=-1);
      //bind可绑定,但一般不绑定
    
      struct sockaddr_in saddr;
      memset(&saddr,0,sizeof(saddr));
      saddr.sin_family=AF_INET;
      saddr.sin_port=htons(6000);
      saddr.sin_addr.s_addr=inet_addr("127.0.0.1");
    
      int res=connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));//三次握手,建立连接
      assert(res!=-1);
    
      while(1)
      {
        char buff[128]={0};
        printf("input:
    ");
        fgets(buff,128,stdin);
        if(strncmp(buff,"end",3)==0)
        {
          break;
        }
        send(sockfd,buff,strlen(buff),0);
        memset(buff,0,sizeof(buff));
        recv(sockfd,buff,127,0);
        printf("buff=%s
    ",buff);
      }
      close(sockfd);//四次挥手
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    在这里插入图片描述

    2. TCP协议特点

    在这里插入图片描述
    在这里插入图片描述tcpdump抓包命令
    使用tcpdump 可以抓包观察 TCP 连接的建立与关闭。该命令需要管理员权限,格式如下(假设两个测试用的主机 IP 地址为 192.168.43.214 和 192.168.43.160 ) :
    在这里插入图片描述

    • TCP和UDP的区别:
      TCP:面向连接、可靠的、字节流服务。使用 TCP 协议通信的双发必须先建立连接,然后才能开始数据的读写。双方都必须为该连接分配必要的内核资源,以管理连接的状态和连接上数据的传输。TCP 连接是全双工的,双方的数据可以通过一个连接进行读写。完成数据交换之后,通信双方都必须断开连接以释放系统资源。
      UDP:无连接、不可靠、数据报服务。发送端应用程序每执行一次写操作,UDP 模块就将其封装成一个 UDP 数据报发送。接收端必须及时针对每一个 UDP 数据报执行读操作,否则就会丢包。并且,如果用户没有指定足够的应用程序缓冲区来读取 UDP 数据,则 UDP 数据将被截断

    • TCP字节流服务
      在这里插入图片描述

    • 什么是粘包?怎么解决?
      粘包可能在服务端产生也可能在客户端产生。提交数据给TCP发送时,TCP并不立刻发送此段数据,而是等待一小段时间,看看在等待期间是否还有要发送的数据,若有则会一次把这两段数据发送出去,造成粘包;另一端在接收到数据库后,放到缓冲区中,如果消息没有被及时从缓存区取走,下次在取数据的时候可能就会出现一次取出多个数据包的情况,造成粘包现象。
      在这里插入图片描述
      解决方法
      法一:发一次收一次
      在这里插入图片描述
      法二:每次先发数据的长度,再开始发数据,保证一次把这次数据收完

    • 什么是TCP断包
      使用TCP传送数据时,有可能数据过大,使得发送方缓冲区无法一次发送,造成另一端只收到的数据不完整,所以要等待数据完全接收到再解析数据。

    • TCP的状态转移图

      为什么四次挥手都完成了还要停留到TIME_WAIT状态,持续大约两个报文的时间?(TIME_WAIT状态存在的意义?)
      TIME_WAIT存在于主动关闭的一方。
      (1)可靠地终止与TCP连接
      (2)保证让迟来的TCP报文段有足够的时间被识别丢弃。

    • 拥塞控制方法
      慢启动和拥塞避免
      快速重传和快速恢复
      在这里插入图片描述

    • 为什么是三次握手,可不可以是两次为什么?
      不可以,需要三次握手才能确认双方的接收与发送能力是否正常。如客户端发出连接请求,但因连接请求报文丢失而未收到确认,于是客户端再重传一次连接请求。后来收到了确认,建立了连接。数据传输完毕后,就释放了连接,客户端共发出了两个连接请求报文段,其中第一个丢失,第二个到达了服务端,但是第一个丢失的报文段只是在某些网络结点长时间滞留了,延误到连接释放以后的某个时间才到达服务端,此时服务端误认为客户端又发出一次新的连接请求,于是就向客户端发出确认报文段,同意建立连接,不采用三次握手,只要服务端发出确认,就建立新的连接了,此时客户端忽略服务端发来的确认,也不发送数据,则服务端一致等待客户端发送数据,浪费资源。

    • 三次握手时可能出现什么攻击?
      SYN洪泛攻击:服务器端的资源分配是在二次握手时分配的,而客户端的资源是在完成三次握手时分配的,所以服务器容易受到SYN洪泛攻击。SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server则回复确认包,并等待Client确认,由于源地址不存在,因此Server需要不断重发直至超时,这些伪造的SYN包将长时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络拥塞甚至系统瘫痪。SYN 攻击是一种典型的 DoS/DDoS 攻击。
      解决SYN攻击的方法:缩短超时(SYN Timeout)时间、增加最大半连接数、过滤网关防护、SYN cookies技术

    • 挥手时,可能受到什么样的攻击?

    • 同一个端口可不可以被一个 TCP 和一个 UDP 的应用程序同时使用?
      可以,端口由端口号和协议名称组合而成。

    • 同一个应用程序可以创建多个套接字吗?
      一个程序可以创建多个Socket,但多个Socket是不能共用端口的。

    2. 多进程、多线程处理并发

    在这里插入图片描述

    ser.c

    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    
    int main()
    {
      signal(SIGCHLD,SIG_IGN);
      int sockfd=socket(AF_INET,SOCK_STREAM,0);//创建监听套接字
      assert(sockfd!=-1);
      struct sockaddr_in saddr;//套接字地址。服务器端Ip,port;客户端ip,port
      memset(&saddr,0,sizeof(saddr));
      saddr.sin_family=AF_INET;
      saddr.sin_port=htons(6000);//1024以内的为知名端口,4096以内的为保留端口,大于4096的为临时端口
      saddr.sin_addr.s_addr=inet_addr("127.0.0.1");//指定ip地址,这里用回环序列进行测试,inet_addr将字符串转成无符号整形
      int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));//地址绑定,为套接字指定一个ip+port,即地址
      assert(res!=-1);
      res=listen(sockfd,5);//监听端口,5在linux系统上指的是监听已完成三次握手的监听队列的大小
      while(1)//服务器循环接收客户端连接
      {
        struct sockaddr_in caddr;//存放客户端地址
        int len=sizeof(caddr);
        int c=accept(sockfd,(struct sockaddr*)&caddr,&len);//c是连接套接字,接受连接,可能阻塞
        if(c<0)
        {
          perror("accept error");
          continue;
        }
        pid_t pid=fork();
        if(pid==-1)
        {
          close(c);
          continue;
        }
        if(pid==0)
        {
          while(1)
          {
            char buff[128]={0};
            int n=recv(c,buff,127,0);//recv返回值为0,说明对端关闭了
            if(n<=0)
            {
              break;
            }
            printf("buff(%d)=%s",n,buff);
            send(c,"0k",2,0);
          }
          close(c);//子进程关闭
          printf("client over!
    ");
          exit(0);
        }
        close(c);//父进程关闭
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    3. UDP编程流程

    UDP 提供的是无连接、不可靠的、数据报服务.
    在这里插入图片描述
    udpser.c

    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    
    int main()
    {
        int sockfd=socket(AF_INET,SOCK_DGRAM,0);//数据报服务的套接字
        assert(sockfd!=-1);
    
        struct sockaddr_in saddr;
        memset(&saddr,0,sizeof(saddr));
        saddr.sin_family=AF_INET;
        saddr.sin_port=htons(6000);
        saddr.sin_addr.s_addr=inet_addr("127.0.0.1");
    
        int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
        assert(res!=-1);
    
        while(1)
        {
            struct sockaddr_in caddr;
            int len=sizeof(caddr);
            char buff[128]={0};
    
            int n=recvfrom(sockfd,buff,127,0,(struct sockaddr*)&caddr,&len);
            printf("recv(%d):%s
    ",n,buff);
    
            sendto(sockfd,"ok",2,0,(struct sockaddr*)&caddr,sizeof(caddr));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    udpcli.c,客户端的地址系统随机分配

    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    
    int main()
    {
        int sockfd=socket(AF_INET,SOCK_DGRAM,0);
        assert(sockfd!=-1);
    
        struct sockaddr_in saddr;//代表服务端的地址:IP port
        memset(&saddr,0,sizeof(saddr));
    
        saddr.sin_family=AF_INET;
        saddr.sin_port=htons(6000);//指定服务器的端口
        saddr.sin_addr.s_addr=inet_addr("127.0.0.1");//指定服务器的ip
        while(1)
        {
            char buff[128]={0};
            printf("input:
    ");
            fgets(buff,128,stdin);
    
            if(strncmp(buff,"end",3)==0)
            {
                break;
            }
            sendto(sockfd,buff,strlen(buff),0,(struct sockaddr*)&saddr,sizeof(saddr));
            memset(buff,0,128);
            int len=sizeof(saddr);
            recvfrom(sockfd,buff,127,0,(struct sockaddr*)&saddr,&len);
            printf("buff=%s
    ",buff);
        }
        close(sockfd);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    在这里插入图片描述
    UDP数据报服务,接收时要一次性接收完
    在这里插入图片描述

    三、HTTP协议与Web服务器

    1. 浏览器与服务器通信过程

    在浏览器中输入网址都发生了哪些事情?

    1. 先通过域名找到ip地址
    2. connect连接服务器端
    3. 发HTTP请求
    4. HTTP回应
      在这里插入图片描述

    2. HTTP请求报文头

    在这里插入图片描述

    3. HTTP应答报文头

    在这里插入图片描述

    4. HTTP的应答状态

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

    5. Web服务器的C语言实现

    myhttp.c

    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    
    int socket_init();
    
    char* get_filename(char buff[])
    {
        if(buff==NULL)
        {
            return NULL;
        }
        char* s=strtok(buff," ");
        if(s==NULL)
        {
            return NULL;
        }
        printf("请求的方法是:%s
    ",s);
        s=strtok(NULL," ");
        if(s==NULL)
        {
            return NULL;
        }
        return s;
    }
    int main()
    {
        int sockfd=socket_init();
        assert(sockfd!=-1);
        while(1)
        {
            struct sockaddr_in caddr;
            int len=sizeof(caddr);
            int c=accept(sockfd,(struct sockaddr*)&caddr,&len);
            if(c<0)
            {
                continue;
            }
            char recv_buff[512]={0};
            recv(c,recv_buff,511,0);//接收浏览器发送过来的数据
            printf("read:
    %s
    ",recv_buff);
            
            char* filename=get_filename(recv_buff);//获取浏览器访问的文件名字
            if(filename==NULL)
            {
                send(c,"err",3,0);
                close(c);
                continue;
            }
    
            char path[128]={"/home/chenfan/code"};//拼接路径
            if(strcmp(filename,"/")==0)
            {
                strcat(path,"index.html");
            }
            else
            {
                strcat(path,filename);
            }
            int fd=open(path,O_RDONLY);//打开文件
            if(fd==-1)
            {
                send(c,"404",3,0);
                close(c);
                continue;
            }
            int filesize=lseek(fd,0,SEEK_END);//文件大小
            lseek(fd,0,SEEK_SET);
    
            char head_buff[512]={"HTTP/1.0 200 OK
    "};
            strcat(head_buff,"Server: myhttp
    ");
            sprintf(head_buff+strlen(head_buff),"Content-Length:%d
    ",filesize);
            
            strcat(head_buff,"
    ");
    
            send(c,head_buff,strlen(head_buff),0);//发送头部
            printf("send:%s
    ",head_buff);
    
            char data_buff[1024]={0};
            int num=0;
            while((num=read(fd,data_buff,1024))>0)
            {
                send(c,data_buff,1024,0);
            }
            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);
        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;
        }
        res=listen(sockfd,5);
        if(res==-1)
        {
            return -1;
        }
        return sockfd;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124

    index.html

    
      
          
          主页
          
          
              下一页
    链接百度

    送孟浩然之广陵


    故人西辞黄鹤楼,
    烟花三月下扬州。
    孤帆远影碧空尽,
    唯见长江天际流。
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    next.html

    
        
            
            新页面
        
        
            上一页
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
  • 相关阅读:
    【模电实验】【超值1 + 1】【验证性实验——比例、求和运算电路实验】【验证性实验——各种非正弦信号发生器实验】
    北京十大知名律师事务所最新排名(2022前十公布)
    剑指 Offer II 031. 最近最少使用缓存
    5081. 重复局面
    营业执照的五大误区,企业千万不要踩雷
    .NET Core(.NET6)中gRPC使用
    羽夏 Bash 简明教程(下)
    gcc安装问题总结
    如何爬取 python 进行多线程跑数据的内容
    【AI】机器学习——朴素贝叶斯
  • 原文地址:https://blog.csdn.net/m0_67402731/article/details/126603850