posts - 200, comments - 8, trackbacks - 0, articles - 0

回顾:
  多进程的问题:数据共享。
  多进程的问题: 进程的上下文环境(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文件


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理