Linux网络编程|网络高级编程

网络高级编程

Linux下主要有4中IO模型

  • 阻塞IO:最常用,缺省情况下套接字建立后即处于阻塞IO模式
  • 非阻塞IO:可防止进程阻塞在IO操作上,需轮询
  • 信号驱动IO:一种异步通讯模型
  • IO多路复用:允许同时对多个IO进行控制

在实际应用中,通常是多个客户端连接服务器端的情况。若使用阻塞函数,如果资源没有准备好,则调用该函数的进程将进入睡眠状态,这样就无法处理其他请求了。本节给出了3中解决IO多路复用的方法,分别为非阻塞和异步式处理(使用fcntl()函数)、以及多路复用处理(使用select()函数或poll()函数)。此外还有多进程和多线程,他们都是网络编程中常用的事务处理方法

1. 非阻塞

当我们将一个套接字设置为非阻塞模式,相当于告诉内核:“当我请求的IO操作不能够马上完成,你想让我的进程进行休眠等待的时候,不要这么做,请马上返回一个错误给我”。当应用程序使用了非阻塞模式的套接字,它需要使用一个循环来不停地测试是否一个文件描述符有数据可读(称做polling)

  • 使用 fcntl(int fd, int cmd, int arg) 函数来设置非阻塞模式
  • cmd 设置为 F_SETFL
  • arg 设置为 O_NONBLOCK
/*****设置非阻塞IO模式*****/
int flag = fcntl(sockfd, F_GETFL);	//cmd为F_GETFL即返回fd指向的文件的状态
flag |= O_NONBLOCK;
fcntl(sockfd, F_SETFL, flag);

2. 异步IO

使用阻塞式和非阻塞式及多路复用等机制可以有效地进行网络通讯,但效率最高的方法是使用异步通知机制,这种方法在设备IO编程中最常见。内核通过使用异步IO在某一个进程需要处理的时间发生时,向该进程发送一个SIGIO信号。这样应用程序不需要不停地等待某些事件的发生,而可以往下运行,以完成其他工作。只有收到从内核发来的SIGIO信号时,去处理它就可以了

  • 使用 fcntl(int fd, int cmd, int arg) 函数来设置异步IO模式
  • 使用 fcntl() 函数的 F_SETOWN 命令,使套接字归属于当前进程
  • 使用 fcntl() 函数的 F_SETFL 命令,将 arg 设置为 O_ASYNC
/*****设置异步IO模式*****/
fcntl(sockfd, F_SETOWN, getpid());	//将套接字归属于该进程,使内核判断应该向哪个进程发送信号
int flag = fcntl(sockfd, F_GETFL);	//cmd为F_GETFL即返回fd指向的文件的状态
flag |= O_ASYNC;
fcntl(sockfd, F_SETFL, flag);

3. 多路复用IO

应用程序中同时处理多路输入输出流,若采用阻塞模式,将得不到预期的目的;若采用非阻塞模式,对多个输入进行轮询将太浪费CPU时间;若设置多个进程分别处理一条数据链路,新进程间的同步与通讯问题将使程序变得复杂;比较好的方法是使用IO多路复用,其基本思想是:

  • 先构造一张有关描述符的表,然后调用一个函数。当这些文件描述符中的一个或多个已准备好进行I/O时函数才返回
  • 函数返回时告诉进程哪个描述符已就绪,可以进行I/O操作

使用select() / poll() 函数实现多路复用:

/*****select()函数*****/
函数原型:int select(int maxfd, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)
传 入 值:maxfd 所有文件描述符的最大值加1
		 readfds 所有要读的文件的文件描述符的集合
		 writefds 所有要写的文件的文件描述符的集合
		 exceptfds 其他要向我们通知的文件描述符
		 timeout 超时设置 -->NULL:一直阻塞,直到有文件描述符就绪或出错
		 				 -->0:仅仅检测文件描述符集的状态,然后立即返回
		 				 -->不为0:在指定时间内,如果没有事件发生,则超时返回

调用 select() 函数时进程会一直阻塞直到有文件可读、有文件可写或者超时时间到。为了设置文件描述符需要使用几个宏:

#include <sys/select.h>
int FD_ZERO(fd_set *fdset);			//从fdset中清除所有的文件描述符
int FD_CLR(int fd,fd_set *fdset);	//将fd从fdset中清除
int FD_SET(int fd,fd_set *fdset);	//将fd加入到fdset
int FD_ISSET(int fd,fd_set *fdset);	//判断fd是否在fdset集合中
/*例如*/
fd_set rset;
int fd;
FD_ZERO(&rset);
FD_SET(fd,&rset);
FD_SET(stdin&rset);
//在select返回之后,可以使用FD_ISSET(fd,&rset)测试给定的位置是否置位。
if(FD_ISSET(fd,&rset))
{
    
    ......}

下图为TCP多路复用模型
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/Chuangke_Andy/article/details/108437802