c++ 学习笔记(高级linux编程) day11

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:异常  

//select用法

#include

#include

#include

#include

#include

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的多客户连接与处理
看个小例子:

//chatServer.c

//聊天服务器端

#include

#include

#include

#include

#include

#include

#include

#include

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

       {

           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

       {

           //判定改变描述符号是否存在

           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

                    {

                        if(fdall[j]!=-1)

                        {

                           send(fdall[j],buf,r,0);

                        }

                    }

                }

           }

       }

    }

}

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

#include

#include

#include

#include

#include

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_STREAMSOCK_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

#include

#include

#include

 

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");

    }

}

//获得缓冲大小

#include

#include

#include

 

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
    设置广播选项
    发送数据(广播方式发送)

#include

#include

#include

#include

#include

#include

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
    设置地址可重用选项
    绑定地址
    接收数据 

#include

#include

#include

#include

#include

#include

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

//oobServer

#include

#include

#include

#include

#include

#include

#include

#include

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);

   

}

//oobClient

#include

#include

#include

#include

#include

#include

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)

#include

#include

#include

#include

#include

#include

#include

 

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  服务器错误

#include

#include

#include

#include

#include

#include

#include

#include

#include

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"

       "靓崽!");

   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文件

你可能感兴趣的:(c++,linux)