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

// 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 ); //阻塞超时

#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

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

// 获得缓冲大小
#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
    设置广播选项
    发送数据(广播方式发送)

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

#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  

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

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


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


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

你可能感兴趣的:(linux高级编程day11 笔记 )