Linux非阻塞编程

处理模型分为两种:阻塞模型和非阻塞模型。

		阻塞模型:若所调用的I/O函数没有完成相关的功能,则会使进程挂起,直到相关数据到达才会返回。(适用于单个设备的操作)
		非阻塞模型:请求的I/O操作不能完成时,则不让进程睡眠,而且立即返回。(适用于多路IO复用)

常见的非阻塞模型:poll、select、epoll,介绍一下poll和select

poll接口:管理多个描述符进行轮询操作,根据描述符的状态进行处理。

函数原型		int poll(struct pollfd *fds, nfds_t nfds, int timeout);

fds是一个struct pollfd定义的一个结构体。

struct pollfd{
	int fd;//文件描述符
	short events;//请求事件
	short revents; //返回事件
}

函数参数: 第二个参数nfds一般指定为1,表示要轮询的文件个数,其实就是第一个参数数组的大小。timeout表示超时时间,单位为ms,指定为需要的大小即可。
函数返回值:有事件发送返回事件的个数;超时,返回0。

代码实例

#include<signal.h>
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string.h>
#include <poll.h>
int main(int argc,char *argv[])
{
	int ret;
	char buf[100]={0};
	//填充结构体
	struct pollfd poll_d=
	{
		.fd = 0,
		.events = POLLIN,
	};
	//运用poll函数轮询
	ret = poll(&poll_d,1,5000);
	//根据返回值判断是否有事件发生
	if(ret == -1)
	{
		perror("poll");
		return -1;
	}
	if(ret == 0)
	{
		printf("time out\n");
	}
	else
	{
		//如果返回事件等于请求事件,执行读写操作
		if(poll_d.revents == poll_d.events)
		{
			read(0,buf,sizeof(buf));
		}	puts(buf);
	}
	return 0;
}

select接口:管理多个描述符进行轮询操作,根据描述符的状态进行处理。、

函数原型		 int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);

函数参数:nfds 最大的文件描述符+1; readfds、writefds、execptfds指定文件的动作,需要绑定,绑定前需要使用FD_ZERO清零,再用FD_SET进行设置;timeout为指定轮询时间。
函数返回值:有事件发送返回事件的个数,超时返回0,失败返回-1.

代码实例:(TCP)

//server
#include<signal.h>
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/select.h>
/*
 * 1.创建套节字 
 * 2.绑定
 * 3.监听
 * 4.建立连接
 * 5.收发数据
 */
#define IP "192.168.43.160"
#define PORT 6666
int main(int argc,char *argv[])
{
    struct sockaddr_in addr_s,addr_c;
    int len,ret,comfd[10],i,j;
    int count = 0;
    //轮询集合
    fd_set readfds;
    //存放最大的文件描述符
    int maxfd;
    char r_buf[100]={0};
    char w_buf[100]={0};
    //创建套节字
    int sock = socket(AF_INET,SOCK_STREAM,0);
    if(sock == -1)
    {
        perror("socket");
        return -1;
    }
    printf("创建套节字成功\n");
    //绑定 将IP和端口号绑定到套节字中
    addr_s.sin_family = AF_INET;
    //addr_s.sin_addr.s_addr = inet_addr(IP);
    inet_aton(IP,&addr_s.sin_addr);
    addr_s.sin_port = htons(PORT);
    len = sizeof(addr_s);
    ret = bind(sock,(struct sockaddr *)&addr_s,len);
    if(ret == -1)
    {
        perror("bind");
        return -1;
    }
    printf("绑定成功\n");
    //监听  同时允许多少个客户端连接服务器
    ret = listen(sock,10);
    if(ret == -1)
    {
        perror("listen");
        return -1;
    }
    printf("监听成功\n");
   
while(1)
{
	//清空轮询集合
   FD_ZERO(&readfds);
   //在文件描述符集合中增加一个新的文件描述符
   FD_SET(sock,&readfds);
   //监视最大的文件描述符
   maxfd = sock;
   for(i=0;i<count;i++)
   {
     FD_SET(comfd[i],&readfds);
     if(comfd[i]>maxfd)
     maxfd = comfd[i];
   }
   //执行select轮询
   ret = select(maxfd+1,&readfds,NULL,NULL,NULL);
   if(ret == -1)
   {
     perror("select");
     return -1;
   }
   else
   {
     for(i=0;i<=maxfd;i++)
     {
        //测试指定的文件描述符是否在该集合中
        if(FD_ISSET(i,&readfds))
        {
          if(i == sock)
          {
          		//建立连接
				comfd[count] = accept(sock,(struct sockaddr *)&addr_c,&len);
            if(comfd[count] == -1)
            {
              perror("accept");
              return -1;
            }
            printf("客户端IP: %s\n",inet_ntoa(addr_c.sin_addr));
            printf("客户端端口号: %d\n",ntohs(addr_c.sin_port));
            count++;
          }
          else
          {
          	recv(i,r_buf,sizeof(r_buf),0);
          	printf("server_i: %s\n",r_buf);
          	for(j=0;j<count;j++)
          	{
          		if(comfd[j] == i)
          			continue;
          		send(comfd[j],r_buf,sizeof(r_buf),0);
          	}
          }
  		}


           }
        }

    }
    return 0;
}
//client
#include<signal.h>
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>

/*
 * 1.创建套节字 
 * 2.建立连接
 * 3.收发数据
 */
#define PORT 6666
int main(int argc,char *argv[])
{
    struct sockaddr_in addr_s;
    int len,ret;
    char r_buf[100]={0};
    char w_buf[100]={0};
    char ip[20]={0};
    //创建套节字
    int sock = socket(AF_INET,SOCK_STREAM,0);
    if(sock == -1)
    {
        perror("socket");
        return -1;
    }
    printf("创建套节字成功\n");
    addr_s.sin_family = AF_INET;
    addr_s.sin_port = htons(PORT);
    printf("请输入服务器ip\n");
    scanf("%s",ip);
    addr_s.sin_addr.s_addr = inet_addr(ip);
    len = sizeof(addr_s);
    //建立连接
    ret  = connect(sock,(struct sockaddr *)&addr_s,len);
    if(ret == -1)
    {
        perror("connect");
        return -1;
    }
    printf("成功连接到服务器\n");
    while(1)
    {
        memset(w_buf,0,sizeof(w_buf));
        printf("请输入发送的内容:\n");
        scanf("%s",w_buf);
        ret = send(sock,w_buf,sizeof(w_buf),0);
        if(ret == -1)
        {
            perror("send");
            continue;
        }
        printf("发送成功\n");
        memset(r_buf,0,sizeof(r_buf));
        ret = recv(sock,r_buf,sizeof(r_buf),0);
        if(ret == -1)
        {
            perror("recv");
            continue;
        }
        printf("接收成功,内容为 %s\n",r_buf);
    }
    return 0;
}

测试结果:

客户机端:
在这里插入图片描述
服务器端:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值