zoukankan      html  css  js  c++  java
  • UNIX网络编程——fcntl函数

    fcntl函数提供了与网络编程相关的如下特性:

    • 非阻塞式I/O。  通过使用F_SETFL命令设置O_NONBLOCK文件状态标志,我们可以把一个套接字设置为非阻塞型。
    • 信号驱动式I/O。 通过使用F_SETFL命令设置O_ASYNC文件状态标志,我们可以把一个套接字设置成O_ASYNC,一旦其状态发生变化,内核就产生一个SIGIO信号。
    • F_SETOWN命令允许我们指定用于接收SIGIO和SIGURG信号的套接字属主(进程ID或进程组ID)。其中SIGIO信号是套接字被设置为信号驱动式I/O型产生的,SIGURG信号是在新的带外数据到达套接字时产生的。F_GETOWN命令返回套接字的当前属主。

     

    fcntl()函数有如下特性:

    • 非阻塞I/O: 可将cmd 设为F_SETFL,将lock设为O_NONBLOCK。
    • 信号驱动I/O:可将cmd设为F_SETFL,将lock设为O_ASYNC。


    用以下方法将socket设置为非阻塞方式 :

    int flags = fcntl(socket, F_GETFL, 0); 
    fcntl(socket, F_SETFL, flags | O_NONBLOCK);

    将非阻塞的设置回阻塞可以用:

    int flags = fcntl(socket, F_GETFL, 0); 
    fcntl(socket, F_SETFL, flags & ~O_NONBLOCK);


    示例代码:

    #include <sys/types.h>  
    #include <sys/socket.h>  
    #include <sys/wait.h>  
    #include <stdio.h>  
    #include <stdlib.h>  
    #include <errno.h>  
    #include <string.h>  
    #include <sys/un.h>  
    #include <sys/time.h>  
    #include <sys/ioctl.h>  
    #include <unistd.h>  
    #include <netinet/in.h>  
    #include <fcntl.h>  
    #include <unistd.h>  
    #define SERVPORT 3333  
    #define BACKLOG 10  
    #define MAX_CONNECTED_NO 10  
    #define MAXDATASIZE 100  
      
    int main()  
    {  
        struct sockaddr_in server_sockaddr,client_sockaddr;  
        int sin_size,recvbytes,flags;  
        int sockfd,client_fd;  
        char buf[MAXDATASIZE];  
    /*创建socket*/  
        if((sockfd = socket(AF_INET,SOCK_STREAM,0))==-1){  
            perror("socket");  
            exit(1);  
        }  
        printf("socket success!,sockfd=%d
    ",sockfd);  
      
    /*设置sockaddr结构*/  
        server_sockaddr.sin_family=AF_INET;  
        server_sockaddr.sin_port=htons(SERVPORT);  
        server_sockaddr.sin_addr.s_addr=INADDR_ANY;  
        bzero(&(server_sockaddr.sin_zero),8);  
      
    /*将本地ip地址绑定端口号*/  
        if(bind(sockfd,(struct sockaddr *)&server_sockaddr,sizeof(struct sockaddr))==-1){  
            perror("bind");  
            exit(1);  
        }  
        printf("bind success!
    ");  
      
    /*监听*/  
        if(listen(sockfd,BACKLOG)==-1){  
            perror("listen");  
            exit(1);  
        }  
        printf("listening....
    ");  
      
    /*fcntl()函数,处理多路复用I/O*/  
        if((flags=fcntl( sockfd, F_GETFL, 0))<0)  
                perror("fcntl F_GETFL");  
        flags |= O_NONBLOCK;  
        if(fcntl( sockfd, F_SETFL,flags)<0)  
                perror("fcntl");  
        while(1){  
            sin_size=sizeof(struct sockaddr_in);  
            if((client_fd=accept(sockfd,(struct sockaddr*)&client_sockaddr,&sin_size))==-1){  //服务器接受客户端的请求,返回一个新的文件描述符  
                perror("accept");  
                exit(1);  
            }  
            if((recvbytes=recv(client_fd,buf,MAXDATASIZE,0))==-1){  
                perror("recv");  
                exit(1);  
            }  
            if(read(client_fd,buf,MAXDATASIZE)<0){  
                perror("read");  
                exit(1);  
            }  
            printf("received a connection :%s",buf);  
      
        /*关闭连接*/  
        close(client_fd); 
        close(sockfd);	
        exit(0);  
        }/*while*/  
    }  
    运行结果:

    huangcheng@ubuntu:~$ ./a.out
    socket success!,sockfd=3
    bind success!
    listening....
    accept: Resource temporarily unavailable

    可以看到,当accept的资源不可用时,程序会自动返回。

    若将54--58行代码替换为:

    if((flags=fcntl( sockfd, F_SETFL, 0))<0)  
        perror("fcntl F_SETFL");  
    flags |= O_ASYNC; 
    if(fcntl( sockfd, F_SETFL,flags)<0)  
        perror("fcntl");  

    运行结果如下:

    huangcheng@ubuntu:~$ ./a.out
    socket success!,sockfd=3
    bind success!
    listening....
    可以看到,进程一直处于等待中,直到另一相关信号驱动它为止。


    由select函数实现的,示例代码:

    #include <sys/types.h>  
    #include <sys/socket.h>  
    #include <sys/wait.h>  
    #include <stdio.h>  
    #include <stdlib.h>  
    #include <errno.h>  
    #include <string.h>  
    #include <sys/un.h>  
    #include <sys/time.h>  
    #include <sys/ioctl.h>  
    #include <unistd.h>  
    #include <netinet/in.h>  
    #define SERVPORT 3333  
    #define BACKLOG 10   
    #define MAXDATASIZE 100  
    int main()  
    {  
        struct sockaddr_in server_sockaddr,client_sockaddr;  
        int sin_size,recvbytes;  
        fd_set readfd;  
        fd_set writefd;  
        int sockfd,client_fd;  
        char buf[MAXDATASIZE];  
    /*创建socket*/  
        if((sockfd = socket(AF_INET,SOCK_STREAM,0))==-1){  
            perror("socket");  
            exit(1);  
        }  
        printf("socket success!,sockfd=%d
    ",sockfd);  
    /*设置sockaddr结构*/  
        server_sockaddr.sin_family=AF_INET;  
        server_sockaddr.sin_port=htons(SERVPORT);  
        server_sockaddr.sin_addr.s_addr=INADDR_ANY;  
        bzero(&(server_sockaddr.sin_zero),8);  
    /*将本地ip地址绑定端口号*/  
        if(bind(sockfd,(struct sockaddr *)&server_sockaddr,sizeof(struct sockaddr))==-1){  
            perror("bind");  
            exit(1);  
        }  
        printf("bind success!
    ");  
    /*监听*/  
        if(listen(sockfd,BACKLOG)==-1){  
            perror("listen");  
            exit(1);  
        }  
        printf("listening....
    ");  
    /*select*/  
        FD_ZERO(&readfd);              // 将readfd 清空   
        FD_SET(sockfd,&readfd);         //将sockfd加入到readfd集合中  
        while(1){  
        sin_size=sizeof(struct sockaddr_in);
        int max_fd = sockfd + 1;	
        if(select(max_fd,&readfd,NULL,NULL,(struct timeval *)0)>0){  //第一个参数是0和sockfd的最大值加1,第二个参数是读集,第三、四个参数是写集                                                                                //和异常集  
            if(FD_ISSET(sockfd,&readfd)>0){         // FD_ISSET 这个宏判断 sockfd 是否属于可读的文件描述符。从 sockfd 中读入, 输出到标准输出上去.  
                if((client_fd=accept(sockfd,(struct sockaddr *)&client_sockaddr,&sin_size))==-1){   //client_sockaddr:客户端地址  
                    perror("accept");  
                    exit(1);  
                }  
                if((recvbytes=recv(client_fd,buf,MAXDATASIZE,0))==-1){  
                    perror("recv");  
                    exit(1);  
                }  
                if(read(client_fd,buf,MAXDATASIZE)<0){  
                    perror("read");  
                    exit(1);  
                }  
                printf("received a connection :%s",buf);  
            } 
            close(client_fd); 
            close(sockfd);		
        }/*select*/  
      }/*while*/  
    }  
    运行结果:

    huangcheng@ubuntu:~$ ./a.out
    socket success!,sockfd=3
    bind success!
    listening....
    

  • 相关阅读:
    SQL 高级查询(层次化查询,递归)
    IntelliJ IDEA添加注释常用的快捷键
    java配置环境变量
    Python单例模式的4种实现方法 ++ redis pool的一种单例实现方式
    MYSQL安装配置文件my-small.ini、my-medium.ini、my-large.ini、my-huge.ini文件的作用
    flask中使用flask-sqlalchemy
    Python向Mysql写入时间类型数据
    [慢查优化]慎用MySQL子查询,尤其是看到DEPENDENT SUBQUERY标记时
    Cocos2d-x 3.2 创建新应用
    In-App Purchase Programming Guide----(六) ----Working with Subscriptions
  • 原文地址:https://www.cnblogs.com/wangfengju/p/6172570.html
Copyright © 2011-2022 走看看