欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

linux网络高级编程之非阻塞

程序员文章站 2022-05-05 16:33:56
...

  在实际应用中,常常遇到多个客户端连接服务器的情况。如果资源没有准备好,则调用该函数的进程将进入睡眠状态,这样就不能处理其他请求了。有三种解决I/O多路复用的方式,分别为非阻塞和异步处理,以及多路复用处理。


实例

采用fcntl()函数将套接字设置为非阻塞I/O

/*nonblock_server.c*/

/*nonblock_server.c*/
/*server.c*/
//使用fcntl()将套接字设置为非阻塞方式
//每一秒轮询是否有等待处理的连接请求
//tcp网络协议编程
#include <stdio.h>  
#include <sys/socket.h>  
#include <string.h>  
#include <netinet/in.h>  
#include <fcntl.h>
#include <errno.h>

#define PORT 4321
#define MAX_QUE_CONN_NM 5
#define BUFFER_SIZE 1024

int main()
{
    int sockid;  //套接字描述符
    struct sockaddr_in server_sockaddr,client_sockaddr;
    int i=1;
    int sin_size;
    char buf[BUFFER_SIZE];
    int client_fd;  //接收到的套接字ID
    int recvbytes;  //接收到的字节数
    int flags;
    //创建socket
    /**********************
    函数原型:int socket(int family,int type,int protocol)
    函数参数:family 协议族
                     AF_INET IPv4协议
                     AF_INET6 IPv6协议
                     AF_LOCAL UNIX域协议
                     AF_ROUTE 路由套接字(socket)
              type 套接字类型
                   SOCK_STREAM 字节流套接字socket 
              protocol 0(原始套接字除外)
    函数返回值: 成功 非负套接字描述符
                 出错 -1
    **********************/
    sockid=socket(AF_INET,SOCK_STREAM,0);
    if(sockid == -1)
    {
    	//创建套接字失败
    	printf("socket error\n");
    	exit(1);
    }
    
    
    server_sockaddr.sin_family=AF_INET; 
    server_sockaddr.sin_port=htons(PORT);  //端口号
    server_sockaddr.sin_addr.s_addr=INADDR_ANY;  //IP地址 //IP地址可以指定,或者使用宏INADDR_ANY
                                                          //表示运行套接字与服务器的任一网络接口进行绑定
    bzero(&(server_sockaddr.sin_zero),8);
    
    //允许重复使用本地地址与套接字进行绑定
    /*******************************
    函数原型:int setsockopt(int sockfd, 
                             int level, 
                             int optname,
                             const void *optval,
                             socklen_t optlen);
    函数参数:sockfd 套接字的ID
              level  选项定义的层次 支持SOL_SOCKET、IPPROTO_TCP、IPPROTO_IP和IPPROTO_IPV6
              optname 需设置的选项
              optval 指针,指向存放选项待设置的新值的缓冲区
              optlen optval缓冲区长度
    ********************************/
    setsockopt(sockid,SOL_SOCKET,SO_REUSEADDR,&i,sizeof(i));
    
    //绑定地址
    if( bind(sockid,(struct sockaddr *)&server_sockaddr,sizeof(struct sockaddr))  == -1 )
    {
        printf("bind error\n");
        exit(1);	
    }
    printf("bind success\n");
    
    /*调用listen()函数,创建为处理请求的队列*/
    if( listen(sockid,MAX_QUE_CONN_NM) == -1 )
    {
        printf("listen error\n");	
    	exit(1);
    }
    printf("listen...\n");
    
    

    //调用fcntl()函数给套接字设置为非阻塞属性
    /**************************
    函数原型: int fcntl(int fd, int cmd); 
    函数参数:fd 欲设置文件的描述符
              cmd F_GETFL 返回由fd指向的文件的状态标志
                  F_SETFL 将arg设置为O_NONBLOCK 非阻塞I/O形式
                  
    函数返回值:
    备注:fcntl()是计算机中的一种函数,通过fcntl可以改变已打开的文件性质。
          fcntl针对描述符提供控制。
          参数fd是被参数cmd操作的描述符。针对cmd的值,fcntl能够接受第三个参数int arg。    
    ******************************/
    flags=fcntl(sockid,F_GETFL);
    if(flags<0  || fcntl(sockid,F_SETFL,flags|O_NONBLOCK) <0)
    {
        printf("fcntl error\n");
        exit(1);	
    }
    while(1)
    {
    	    sin_size=sizeof(client_sockaddr);
	    do{
		    //等待连接
		    if( !( (client_fd=accept(sockid,(struct sockaddr*)&client_sockaddr,&sin_size)) < 0))
		    {
		    	break;
		    }
		   if(errno == EAGAIN)
		   {
		       printf("resourse temporarily unavailable\n");	
		       sleep(1);
		   }
		   else
		   {
		       /*其他错误*/
		       printf("accept error\n");
		       exit(1);	
		   }		    
	       }while(1);
	   memset(buf,0,sizeof(buf));

           if( (recvbytes= recv(client_fd,buf,BUFFER_SIZE,0) )  == -1 )
           {
		printf("recv error\n");
		exit(1);
           }
           printf("receive data %d\n",strlen(buf));
           printf("receive a message:%s\n",buf);
	    
    }//end whlie
    
    //结束连接	
    close(sockid);
    exit(0);	
}
/*client.c*/

/*client.c*/
#include <stdio.h>  
#include <sys/socket.h>  
#include <string.h>  
#include <netinet/in.h>  
#include <netdb.h>

#define PORT 4321
#define BUFFER_SIZE 1024


int main(int argc,char *argv[])
{
    struct hostent *host;
    int sockid;  //套接字描述符
    int sendbyts;  //实际发送的字节数
    char buf[BUFFER_SIZE];
    struct sockaddr_in serv_addr;
    
    if(argc<3)
    {
        fprintf(stderr,"USAGE:./client Hostname(or ip address) Text\n");
        exit(1);	
    }
    
    /*地址解析函数*/
    /***************************
    函数原型:struct hostent *gethostbyname(const char *hostname);
    函数参数:hostname 主机名
    函数返回值:成功 指向hostent的指针
                失败 -1    
    *****************************/
    if( (host=gethostbyname(argv[1])) == NULL )
    {
        printf("gethostbyname error\n");	
        exit(1);
    }
    
    memset(buf,0,sizeof(buf));
    sprintf(buf,"%s",argv[2]);
    
    //创建socket
    
    sockid=socket(AF_INET,SOCK_STREAM,0);
    if(sockid == -1)
    {
    	//创建套接字失败
    	printf("socket error\n");
    	exit(1);
    }
    /*设置socket_in中的相关参数*/
    serv_addr.sin_family=AF_INET; 
    serv_addr.sin_port=htons(PORT);  //端口号
    serv_addr.sin_addr=*((struct in_addr *)host->h_addr);  //IP地址 //IP地址可以指定,或者使用宏INADDR_ANY
                                                          //表示运行套接字与服务器的任一网络接口进行绑定
    bzero(&(serv_addr.sin_zero),8);
 
    //连接服务器
    /*调用connect()函数主动发起对服务器端的连接*/
    /************************
    函数原型: int connect(int sockfd, const struct sockaddr * addr, socklen_t *addrlen)
    函数参数: sockfd  套接字描述符
               addr    客户端地址
               addrlen 地址长度
    函数返回值: 成功 接收到的非负套接字
                失败 -1
    ***********************/
    if( connect(sockid,(struct sockaddr*)&serv_addr,sizeof(struct sockaddr)) == -1 )
    {
        printf("connect error\n");	
        exit(1);
    }
    
        
    /*发送消息给服务端*/
    /*****************
    函数原型:int send(int sockfd,const void *msg,int len,int flags)
    函数参数:sockfd 套接字描述符
              msg 指向要发送数据的指针
              len 数据长度
              flags 一般为0
    函数返回值:成功 实际发送的字节数
                失败 -1   
    ******************/
    if( (sendbyts=send(sockid,buf,sizeof(buf),0)) == -1 )
    {
        printf("send error\n");
        exit(1);	
    }
    
    close(sockid);
	
    return 0;
}

运行结果

linux网络高级编程之非阻塞linux网络高级编程之非阻塞