• linux高级编程day11 笔记


    回顾:
      多进程的问题:数据共享。
      多进程的问题: 进程的上下文环境(context)
              文件描述符号是整数以及对应上下文环境
      多进程的问题:上下文环境共享

    一.SELECT TCP服务器编程模式
     1.select函数
      int select(
       int fds,//建议是监控的文件描述符号的最大值+1
       fd_set *readfds,//读文件描述符号集合
               //该参数既是输入,也是输出
               //输入:被监控的描述符号
               //输出:有数据的描述符号
       fd_set *writefds,
       fd_set *errfds,
       struct timeval*timeout);//指定阻塞时间限制
                   //为NULL,永久
      返回:
        >0:发生改变的文件描述符号个数
        =0:时间限制过期
        =-1:异常  

    View Code
    //select用法
    #include <stdio.h>
    #include <fcntl.h>
    #include <signal.h>
    #include <unistd.h>
    #include <sys/select.h>
    main()
    {    
        fd_set fds;
        int r;
        char buf[100];
        while(1)
        {
            FD_ZERO(&fds);  //清空
            FD_SET(0,&fds);  //将描述符0加入
            r=select(1,&fds,0,0,0);  //监控
            printf("有数据输入!\n");
            r=read(0,buf,99);
        }
    }        

     2.IO能否发出信号?
       异步IO就是通过信号工作.
     3.应用使用select
     4.使用select实现TCP的多客户连接与处理
    看个小例子:

    View Code
    //chatServer.c
    //聊天服务器端
    #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 <sys/select.h>
    main()
    {
        int sfd;//服务器描述符号
        int fdall[100];//客户描述符号
        int count;//客户个数
        int r;//返回值(异常处理)
        struct sockaddr_in dr;//IP地址与端口
        fd_set fds;//被select监控的描述符号集合
        int maxfd;//最大文件描述符号
        int i,j;//循环变量
        char buf[1024];//客户聊天数据
        
        //1.建立socket
        sfd=socket(AF_INET,SOCK_STREAM,0);
        if(sfd==-1) printf("1:%m\n"),exit(-1);
        printf("socket ok!\n");
        //2.绑定地址与端口
        dr.sin_family=AF_INET;
        dr.sin_port=htons(8866);
        inet_aton("192.168.180.92",&dr.sin_addr);
        r=bind(sfd,(struct sockaddr*)&dr,sizeof(dr));
        if(r==-1) printf("2:%m\n"),close(sfd),exit(-1);
        printf("bind ok!\n");
        //3.监听
        r=listen(sfd,10);
        if(r==-1) printf("3:%m\n"),close(sfd),exit(-1);
        printf("listen ok!\n");
        //初始化
        count=0;
        maxfd=0;
        FD_ZERO(&fds);
        for(i=0;i<100;i++)
        {
            fdall[i]=-1;
        }
        while(1)
        {
            //4.构造监听的描述符号集合
            //4.1.清空
            FD_ZERO(&fds);
            maxfd=0;
            //4.2.加入服务器描述符号
            FD_SET(sfd,&fds);
            maxfd=maxfd>=sfd?maxfd:sfd;
            //4.3.加入客户描述符号
            for(i=0;i<count;i++)
            {
                if(fdall[i]!=-1)
                {
                    FD_SET(fdall[i],&fds);
                    maxfd=maxfd>=fdall[i]?maxfd:fdall[i];
                }
            }
            //5.使用select循环控制描述符号集合
            r=select(maxfd+1,&fds,0,0,0);
            if(r==-1) 
            {
                printf("服务器崩溃!\n");
                break;
            }        
            //6.分两种情况处理:        
            //6.1.有客户连接:服务器描述符号
            if(FD_ISSET(sfd,&fds))
            {
                fdall[count]=accept(sfd,0,0);
                if(fdall[count]==-1)
                {
                    printf("服务器崩溃!\n");
                    //释放所有客户
                    break;
                }
                printf("有客户连接!\n");
                count++;
            }
            //6.2.有客户发送数据:客户描述符号
            for(i=0;i<count;i++)
            {
                //判定改变描述符号是否存在
                if( fdall[i]!=-1 &&
                    FD_ISSET(fdall[i],&fds))
                {
                    //读取数据
                    r=recv(fdall[i],buf,1023,0);
                    if(r==0){
                        printf("有客户退出!\n");
                        close(fdall[i]);
                        fdall[i]=-1;
                    }
                    if(r==-1){
                        printf("网络故障!\n");
                        close(fdall[i]);
                        fdall[i]=-1;
                    }
                    if(r>0)
                    {
                        //广播数据
                        buf[r]=0;
                        printf("广播数据:%s\n",buf);
                        for(j=0;j<count;j++)
                        {
                            if(fdall[j]!=-1)
                            {
                                send(fdall[j],buf,r,0);
                            }
                        }
                    }
                }
            }
        }
    }

     5.poll模式
      int poll(
        struct pollfd *fds,//监控的描述符号
        int nfds,//监控的描述符号的个数
        int timeout ); //阻塞超时

    View Code
    #include <stdio.h>
    #include <fcntl.h>
    #include <signal.h>
    #include <unistd.h>
    #include <sys/poll.h>
    main()
    {    
        struct pollfd fds[1];
        int r;
        char buf[100];
        
        fds[0].fd=0;
        fds[0].events=POLLIN;
        while(1)
        {
            
            r=poll(fds,1,-1);
            if(fds[0].revents & POLLIN)
            {
                printf("有数据输入!\n");
                r=read(0,buf,99);
            }
        }
    }

    二.Socket选项设置
      1.socket有哪些选项可以设置
         ARP
          |
         IP
          |
      |-----------------|
      UDP            TCP    
      通用选项:
        SOL_SOCKET 
          SO_BROADCAST  广播
          SO_RCVBUF    描述符号的缓冲的大小
          SO_SNDBUF    描述符号的缓冲的大小
          SO_REUSEADDR  地址反复绑定
          SO_TYPE     描述符号类型SOCK_STREAM SOCK_DGRAM?
      ICMP选项
        IPPTOTO_ICMP
          ICMP_FILTER
      IP选项(干预系统生成IP头)
        IPPROTO_IP
          ......
          ......
      UDP选项
        IPPROTO_UDP
          ......
      
      TCP选项    
        IPPROTO_TCP
          ......      
      setsockopt设置选项
      getsockopt获取选项
    案例:
      判定一个socket的数据类型AF_INET:SOCK_STREAM  SOCK_DGRAM SOCK_RAW

    View Code
    #include <stdio.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    
    main()
    {
        int fd;
        int type;
        int len;
        len=sizeof(type);
        
        fd=socket(AF_INET,SOCK_DGRAM,0);
        
        getsockopt(fd,SOL_SOCKET,SO_TYPE,&type,&len);
        
        printf("%u:%u\n",SOCK_STREAM,type);
        if(type & SOCK_STREAM)
        {
            printf("流!\n");
        }
        if(type & SOCK_DGRAM)
        {
            printf("报文!\n");
        }
    }
    View Code
    //获得缓冲大小
    #include <stdio.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    
    main()
    {
        int fd;
        int type;
        int len;
        len=sizeof(type);
        
        fd=socket(AF_INET,SOCK_DGRAM,0);
        getsockopt(fd,SOL_SOCKET,SO_RCVBUF,&type,&len);  //在这里把参数SO_TYPE变成SO_RCVBUF即可
        printf("缓冲大小:%u\n",type);
    }

    案例:
      使用选项进行数据广播.
      cast_A发送
        建立socket
        设置广播选项
        发送数据(广播方式发送)

    View Code
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    main()
    {
        int fd;
        int opt=1;
        int r;
        struct sockaddr_in dr;
        
        //1.选项设置
        fd=socket(PF_INET,SOCK_DGRAM,0);
        if(fd==-1) printf("1:%m\n"),exit(-1);
        
        r=setsockopt(fd,SOL_SOCKET,SO_BROADCAST,
                    &opt,sizeof(opt));
        if(r==-1) printf("2:%m\n"),exit(-1);
        dr.sin_family=AF_INET;
        dr.sin_port=htons(9999);
        //2.使用广播IP地址
        dr.sin_addr.s_addr=inet_addr("192.168.180.255");
        
        r=sendto(fd,"Hello",5,0,
            (struct sockaddr*)&dr,sizeof(dr));
        if(fd==-1) printf("3:%m\n");
        
        close(fd);
    }

      case_B接收
        建立socket
        设置地址可重用选项
        绑定地址
        接收数据 

    View Code
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    main()
    {
        int fd;
        int opt=1;
        char buf[100];
        int r;
        struct sockaddr_in dr;
        
        fd=socket(PF_INET,SOCK_DGRAM,0);
        if(fd==-1) printf("1:%m\n"),exit(-1);
        //1.选项
        r=setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,
                    &opt,sizeof(opt));
        if(r==-1) printf("2:%m\n"),exit(-1);
        
        dr.sin_family=AF_INET;
        dr.sin_port=htons(9999);
        //2.广播地址
        dr.sin_addr.s_addr=inet_addr("192.168.180.255");
        
        r=bind(fd,(struct sockaddr*)&dr,sizeof(dr));
        if(r==-1) printf("3:%m\n"),exit(-1);
        
        r=recv(fd,buf,100,0);
        if(r>0)
        {
            buf[r]=0;
            printf("广播数据:%s\n",buf);
        }
        close(fd);
        
    }        

    三.OOB数据(TCP)  
      优先数据(带外数据)
      send(,MSG_OOB);
      recv(,MSG_OOB);
    案例:
      oob_server.c
         recv MSG_OOB
      oob_client.c 
         send MSG_OOB

    View Code
    //oobServer
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <signal.h>
    #include <fcntl.h>
    int fd,cfd;
    void handle(int s)
    {
    
        char data[100];
        int r;
        if(s==SIGURG)
        {
            r=recv(cfd,data,100,MSG_OOB);        
        
            data[r]=0;
            printf("$$%s\n",data);
        }
    }
    main()
    {
        
        int opt=1;
        char buf[100];
        int r;
        struct sockaddr_in dr;
        
        fd=socket(PF_INET,SOCK_STREAM,0);
        if(fd==-1) printf("1:%m\n"),exit(-1);
        printf("1\n");
        dr.sin_family=AF_INET;
        dr.sin_port=htons(10000);    
        dr.sin_addr.s_addr=inet_addr("192.168.180.92");
        
        r=bind(fd,(struct sockaddr*)&dr,sizeof(dr));
        if(r==-1) printf("2:%m\n"),exit(-1);
        printf("2\n");
        r=listen(fd,10);
        if(r==-1) printf("3:%m\n"),exit(-1);
        printf("3\n");
        signal(SIGURG,handle);
        cfd=accept(fd,0,0);
        fcntl(cfd,F_SETOWN,getpid());
        if(cfd==-1) printf("4:%m\n"),exit(-1);
        printf("4\n");
        while(1)
        {
            r=recv(cfd,buf,100,0);
            if(r>0)
            {
                buf[r]=0;
                printf("接收数据:%s\n",buf);
            }        
            else
            {
                break;
            }        
        }
        close(cfd);
        close(fd);
        
    }
    View Code
    //oobClient
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    main()
    {
        int fd;
        int opt=1;
        char buf[100];
        int r;
        struct sockaddr_in dr;
        fd_set fds;
        
        fd=socket(PF_INET,SOCK_STREAM,0);
        if(fd==-1) printf("1:%m\n"),exit(-1);
        printf("1\n");
        dr.sin_family=AF_INET;
        dr.sin_port=htons(10000);    
        dr.sin_addr.s_addr=inet_addr("192.168.180.92");
        
        r=connect(fd,(struct sockaddr*)&dr,sizeof(dr));
        if(r==-1) printf("2:%m\n"),exit(-1);    
        while(1)
        {
            FD_ZERO(&fds);
            FD_SET(fd,&fds);
            select(fd+1,0,&fds,0,0);
            send(fd,"Hello",5,MSG_OOB);        
        }
        close(fd);
        
    }

    OOB总结:
      1.OOB数据只能一个字符
      2.普通数据使用一般方式接收与发送,OOB数据使用MSG_OOB接收与发送
      3.一个数据使用MSG_OOB,则最后一个是OOB,其他非OOB数据
      4.问题:OOB数据是优先数据。优先体现在什么地方? 接收OOB数据的时候,会产生一个URG信号

    四.HTTP协议以及应用            
     1.HTTP协议版本HTTP1.0 HTTP1.1 
     2.HTTP是应用协议
     3.HTTP协议分成:
        请求协议
        响应协议
     4.请求协议的格式:
       请求行(请求方法 请求资源 协议版本) 
       请求体(请求头:请求值)
       空行
       数据(querystring:key=value&key=value)

    View Code
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    
    main()
    {
        int fd;
        struct sockaddr_in dr;
        char strreq[1024];
        char buf[10*1024];
        int r; 
        //建立socket
        fd=socket(AF_INET,SOCK_STREAM,0);
        
        //连接服务器192.168.0.72
        dr.sin_family=AF_INET;
        dr.sin_port=htons(80);
        dr.sin_addr.s_addr=inet_addr("192.168.0.72");
        r=connect(fd,(struct sockaddr*)&dr,sizeof(dr));
    
        
        //构建http请求字符串
        sprintf(strreq,
            "GET /index.php HTTP/1.1\r\n"
            "Host: 192.168.0.72:80\r\n"
            "User-Agent: Tarena5.0\r\n"
            "Accept: text/html,image/png\r\n"
            "Accept-Language: zh-cn\r\n"
            "Accept-Charset: gb2312,utf-8\r\n"
            "Keep-Alive: 300\r\n"
            "Connection: keep-alive\r\n"
            "\r\n");
        //发送http请求字符串
        r=send(fd,strreq,strlen(strreq),0);
        //等待服务器响应
        //while(1)
        //{
            r=recv(fd,buf,1024,0);
            //if(r<=0) break;
            printf("========================\n");
            printf("%s\n",buf);
            printf("========================\n");
        //}
        close(fd);
    }

     5.响应协议的格式
       响应行(协议版本 响应码 响应码的文本描述)
       响应体(响应头: 响应值)
       空行
       数据(普通数据/分块数据)

     响应码:    
       1XX  正在处理
       2XX  响应成功(200表示完全成功)
       3XX  继续处理
       4XX  客户错误
       5XX  服务器错误

    View Code
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <signal.h>
    #include <fcntl.h>
    #include <string.h>
    int fd,cfd;
    main()
    {
        char buf[1024];
        int r;
        struct sockaddr_in dr;
        char strres[1024];
        
        fd=socket(PF_INET,SOCK_STREAM,0);
        if(fd==-1) printf("1:%m\n"),exit(-1);
        printf("1\n");
        dr.sin_family=AF_INET;
        dr.sin_port=htons(10000);    
        dr.sin_addr.s_addr=inet_addr("192.168.180.92");
        
        r=bind(fd,(struct sockaddr*)&dr,sizeof(dr));
        if(r==-1) printf("2:%m\n"),exit(-1);
        printf("2\n");
        r=listen(fd,10);
        if(r==-1) printf("3:%m\n"),exit(-1);
        printf("3\n");    
        cfd=accept(fd,0,0);    
        if(cfd==-1) printf("4:%m\n"),exit(-1);
        printf("4\n");
        sprintf(strres,
            "HTTP/1.1 200 OK\r\n"
            "Server: tarena2.0\r\n"
            "Content-Type: text/html\r\n"
            "Content-Length: 28\r\n"
            "Connection: keep-alive\r\n"
            "\r\n"
            "<font color=red>靓崽!</font>");
        while(1)
        {
            r=recv(cfd,buf,1024,0);
            if(r>0)
            {
                buf[r]=0;
                printf("接收数据:%s\n",buf);
                send(cfd,strres,strlen(strres),0);
            }        
            else
            {
                break;
            }
                    
        }    
        close(cfd);
        close(fd);
    }

    五.ioctl函数
      实现ifconfig工具

    总结:
      重点:
        select
        广播

      了解:
        OOB数据
        HTTP协议

      应用:
        独立编写TCP服务器端的select模式
        编写广播
        能够请求一个网页,并且解析响应
    作业:
      1.把聊天程序使用poll实现
      2.使用UDP的广播,发送一个文件    
      3.随意挑选网站,把主页下载并保存成html文件

  • 相关阅读:
    SSL证书的生成方法
    某些系统文件破坏后的修复方式
    dd命令的使用简介
    浅谈访问控制列表(ACL)
    发送端usleep(900)单线程带宽
    1024个读出线程的测试结果
    寻找ROS取数的瓶颈:思路整理(2)
    ROS:32个接收进程的接收带宽
    ROS: 将接收端的接收函数注释掉,TCP只发送,不接受数据时的recv-Q和send-Q长度实验
    ROS:16个接收进程的接收带宽
  • 原文地址:https://www.cnblogs.com/tangzhengyue/p/2617271.html
Copyright © 2020-2023  润新知