1. 程式人生 > >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 <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檔案