网络编程tcp/ip多路复用 对selece()函数的使用

多路io复用:
io模型:
阻塞io:最常用,最简单,效率最低
非阻塞io:防止进程阻塞在io上,需要轮询
多路复用io:允许同时对多个io进行控制:内核添加一张表,监听表里面的信息,当有资源准备就绪
就执行资源==》文件描述符去除与否 创建表==》监听表
缺点:每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大。
同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大。
select支持的文件描述符数量太小,默认1024
信号驱动io:异步通信模型
api: poll接口:
定义:管理多个描述符进行轮询操作(查询文件描述符,如果时间在指定的时间发生立即返回),根据描述符的状态
进行处理。一般通过返回值来确实指定的事件是否发生。没有文件描述符的限定。
圆形:int poll(struct pollfd *fds,nfds_t nfds,int timeout)
功能:指定时间内轮询指定文件描述符,如果有指定事件发生返回一个真值。
参数:fds是一个struct pollfd定义的结构体指针
struct pollfd{
int fd; //文件描述符
short event; //请求事件
short revent; //返回事件
}
事件 POLLIN 普通或优先级带数据可读 。。。。
事件定义可以理解为linux内核层和应用层的约定,内核返回相应的数据,应用层接收判断
可以知道那些定义事件发生。一般最常用为pollin
nfds一般指定为1,表示要轮询的文件个数,其实就是第一个参数数组的大小。
timeout表示超时时间,单位是ms,指定为需要的大小即可。
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/time.h>
#include <unistd.h>
#include <poll.h>
int main()
{
char ch;
struct pollfd fds;
int fd=open("/dev/tty",O_RDONLY|O_NONBLOCK);//键盘驱动注册的设备节点
if(fd<0)
{
printf(“open err!!!\n”);
return -1;
}
fds.fd=fd;
fds.events=POLLIN;
if(poll(&fds,1,5000))//5 秒内去轮询设备节点,如果有数据可读返回真。程序阻塞到这里。
{
read(fd,&ch,1);
printf(“ch = %c\n”,ch);
}
else //5s内键盘不输入数据,就超时。
{
printf(“time out!!!\n”);
}
close(fd);
return 0;
}
select接口:
不会阻塞 就是线程或进程执行此函数时不必非要等待事件的发生,一旦执行肯定返回,以返回值的不同来反映函数的执行情况,
,它能够监视我们需要监视的文件描述符的变化情况,读写或异常。
原型:int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
功能:允许进程监听多个文件描述符组合成的集合,一直阻塞的等待到集合中的一个
或多个文件描述符准备就绪,函数立刻返回。
参数:nfds :文件描述符的范围,即所有文件描述符的最大值+1,不能出错
readfds :监听的读资源
writefds:监听的写资源
exceptfds:异常
timeout: NULL 一直等待
struct timeval{
long tv_sec; //秒
long tv_usec; //微妙
}
返回值:已经准备就绪的文件描述符的个数,失败-1;
关联的api函数:
清除集合中的描述符 void FD_CLR(int fd,fd_set *set)
判断fd是否在集合中 int FD_ISSET(int fd,fd_set *set)
将fd添加到集合中 void FD_SET(int fd,fd_set *set)
将集合清零 void FD_ZERO(fd_set *set)
注意:selete正确返回时,会将准备好的文件描述符在集合中的对应位置1,其它位全部置0
为了保证仍然可以监听其它没有ready描述符,必须先保存之前的fd集合。
//sever.c
#include <stdio.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <strings.h>
#include <string.h>
#include “sys/time.h”
#include “sys/select.h”
#define PORT 6666
#define IP “127.0.0.1”
int main()
{
int ser_fd = 0,cli_fd = 0;
int nfds=0,max_fd=0;
fd_set readfds;
fd_set run_link,bei_limk; //2个监听表 一个用来操作(添加或删除文件描述符),一个用来判断(是否文件描述符发生了改变)
int ret;
char buf[1024];
struct sockaddr_in ser_addr,cli_addr;
socklen_t len;
//1.创建监听套接字
ser_fd = socket(AF_INET,SOCK_STREAM,0);
if(ser_fd < 0)
{
perror(“socket”);
return -1;
}
printf(“创建监听套接字成功\n”);
//2.填充核心结构体
memset(&ser_addr,0,sizeof(ser_addr));
ser_addr.sin_family = AF_INET;
ser_addr.sin_port = htons(PORT);
ser_addr.sin_addr.s_addr = inet_addr(IP);
len = sizeof(cli_addr);
//3.bind绑定
if((bind(ser_fd,(struct sockaddr *)&ser_addr,sizeof(ser_addr))) < 0)
{
printf(“绑定监听套接字失败\n”);
return -1;
}
printf(“绑定套接字成功\n”);
//4.开始监听
if((listen(ser_fd,10)) < 0)
{
printf(“监听失败\n”);
return -1;
}
printf(“监听成功\n”);
//开始多路复用
FD_ZERO(&run_link);
FD_SET(ser_fd,&run_link); //将监听套接字添加入监听表
max_fd=ser_fd;
nfds=max_fd+1;
while(1)
{
int i; //相当于文件描述符
bei_limk=run_link;
ret=select(nfds,&bei_limk,NULL,NULL,NULL); //监听读资源 一直等待
if(ret<0)
{
perror(“select”);
return -1;
}

for(i=0;i<nfds;i++)          //从0到最大
{
 if(FD_ISSET(i,&bei_limk))  //判断套接字i是否在监听表
 {
  if(i==ser_fd)
  {
    printf("说明有客户机连接\n");
	cli_fd = accept(ser_fd,(struct sockaddr *)&cli_addr,&len);
    if(cli_fd < 0)
    {
	printf("连接失败\n");
	return -1;
    }
    printf("连接成功\n");
	FD_SET(cli_fd,&run_link);  //将通信套接字添加进监听表
    max_fd=cli_fd>max_fd?cli_fd:max_fd;
    nfds=max_fd+1;	  
  }
  else   //i在监听列表里且不是监听套接字
  {
  int read_ret;
  //起码说明客户端有人发了消息过来
  bzero(buf,sizeof(buf));
  read_ret=read(i,buf,sizeof(buf));	  
  if(read_ret<0)
  {
  perror("read");      //你在监听表里且发了消息,但是消息出错,即连接异常了
  FD_CLR(i,&run_link);  //异常就清除监听表	
  printf("客户机%d退出\n",i);	  
  close(i);            //将你退出
  }
  if(read_ret==0)      //可能断网异常
  {
  perror("read");
  FD_CLR(i,&run_link);  //异常就清除监听表	  
  printf("客户机%d退出\n",i);
  close(i);            //将你退出  
  }
  if(read_ret>0)       //没毛病了
  {
   printf("客户机%d 的消息%s\n",i,buf);	  
  }	  
  }	 
 }	
}
}
//7.关闭套接字 
close(ser_fd);
close(cli_fd);
return 0;

}

//客户端
//client.c
#include <stdio.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <strings.h>
#include <string.h>
#define PORT 6666
#define IP “127.0.0.1”
int main()
{
int ser_fd = 0;
char buf[1024];
struct sockaddr_in ser_addr;
//1.创建通信套接字
ser_fd = socket(AF_INET,SOCK_STREAM,0);
if(ser_fd < 0)
{
perror(“socket”);
return -1;
}
printf(“创建通信套接字成功\n”);
//2.填充核心结构体
memset(&ser_addr,0,sizeof(ser_addr));
ser_addr.sin_family = AF_INET;
ser_addr.sin_port = htons(PORT);
ser_addr.sin_addr.s_addr = inet_addr(IP);
//3.建立连接
if((connect(ser_fd,(struct sockaddr *)&ser_addr,sizeof(ser_addr))) < 0)
{
printf(“连接失败\n”);
return -1;
}
printf(“连接成功\n”);
//4.数据收发
while(1)
{
bzero(buf,sizeof(buf));
printf(“请输入你要发送的消息…\n”);
scanf("%s",buf);
send(ser_fd,buf,sizeof(buf),0);
//bzero(buf,sizeof(buf));
//recv(ser_fd,buf,sizeof(buf),0);
//printf(“来自服务器的消息%s\n”,buf);
}
//5.关闭套接字
close(ser_fd);
return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值