2.1.1 Select 实现

一、多路复用IO

IO multiplexing 这个词可能有点陌生,但是提到 select/epoll,大概就都能明白了。有些地方
也称这种 IO 方式为事件驱动 IO(event driven IO)。我们都知道,select/epoll 的好处就在于单
个 process 就可以同时处理多个网络连接的 IO。它的基本原理就是 select/epoll 这个 function
会不断的轮询所负责的所有 socket,当某个 socket 有数据到达了,就通知用户进程。它的流
程如图:
在这里插入图片描述
当用户进程调用了 select,那么整个进程会被 block,而同时,kernel 会“监视”所
有 select 负责的 socket,当任何一个 socket 中的数据准备好了,select 就会返回。这
个时候用户进程再调用 read 操作,将数据从 kernel 拷贝到用户进程。

二、Select 接口介绍

(1) fd_set 结构体

fd_set是一种数据类型,在select函数中包含了3个参数,就是这个fd_set类型,fd_set也是理解select模型的关键.
fd_set 类型可以简单的理解为按 bit 位标记句柄的队列,例如要在某 fd_set 中标记一个值为 16 的句柄,则该 fd_set 的第 16 个 bit 位被标记为 1

typedef struct{
    long int fds_bits[32];
}fd_set;

实际上 fd_set 就是一个long int类型的数组。因为每一位可以代表一个文件描述符。所以 fd_set 最多表示1024个文件描述符

(2) 四个操作fd_set 的宏

(a) FD_ZERO

FD_ZERO清除文件描述符集fdset中的所有位(既把所有位都设置为0)

void FD_ZERO(fd_set *fdset);
FD_ZERO(&clientSet);

(b) FD_SET

FD_SET设置文件描述符集fdset中对应于文件描述符fd的位(设置为1)

void FD_SET(int fd, fd_set *fdset);

© FD_CLR

FD_CLR清除文件描述符集fdset中对应于文件描述符fd的位(设置为0)

void FD_CLR(int fd, fd_set *fdset);

(d) FD_ISSET

FD_ISSET来检测文件描述符集fdset中对应于文件描述符fd的位是否被设置

void FD_ISSET(int fd, fd_set *fdset);

(3) select函数

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,struct timeval *timeout)
  • int maxfdp是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1

  • fd_set *readfds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的读变化的,即我们关心是否可以从这些文件中读取数据了,如果这个集合中有一个文件可读,select就会返回一个大于0的值,表示有文件可读;如果没有可读的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的读变化。

  • fd_set *writefds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的写变化的,即我们关心是否可以向这些文件中写入数据了,如果这个集合中有一个文件可写,select就会返回一个大于0的值,表示有文件可写,如果没有可写的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的写变化。

  • fd_set *errorfds 用来监视文件错误异常文件。

  • struct timeval* timeout是select的超时时间,这个参数至关重要,它可以使select处于三种状态,第一,若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;第二,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;第三,timeout的值大于0,这就是等待的超时时间,即 select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。

  • 函数返回

  • (1) 当监视的相应的文件描述符集中满足条件时,读文件描述符集中有数据到来时,内核(I/O)根据状态修改文件描述符集,并返回一个大于0 的数。

  • (2) 当没有满足条件的文件描述符,且设置的timeval 监控时间超时时,select函数会返回一个为0的值。

  • (3) 当select返回负值时,发生错误。

三、Select 服务端程序实现

#include <errno.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

#include <sys/poll.h>
#include <sys/epoll.h>


#include <pthread.h>


#define MAXLNE	4096


int main(int argc, char **argv){

	int listenfd, connfd, n;
    struct sockaddr_in servaddr;
    char buff[MAXLNE];
	
 	//create socket
    if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        printf("create socket error: %s(errno: %d)\n", strerror(errno), errno);
        return 0;
    }
 
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(9999);
	
 	//bind
    if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1) {
        printf("bind socket error: %s(errno: %d)\n", strerror(errno), errno);
        return 0;
    }
	
 	//listen
    if (listen(listenfd, 10) == -1) {
        printf("listen socket error: %s(errno: %d)\n", strerror(errno), errno);
        return 0;
    }
	

	fd_set rfds,rset,wfds,wset;
	FD_ZERO(&rfds);
	FD_SET(listenfd,&rfds);
	FD_ZERO(&wfds);
	int max_fd = listenfd;

	while(1){
		// Select
		
		rset = rfds;
		wset = wfds;
		
		int nready = select(max_fd+1, &rset, &wset, NULL, NULL);
		// accept
		if(FD_ISSET(listenfd,&rset)){
			struct sockaddr_in client;
			socklen_t len = sizeof(client);
			if((connfd = accept(listenfd,(struct sockaddr *)&client,&len)) == -1){
				printf("accept error: %s(error: %d\n)",strerror(errno),errno);
				return 0;
			}
			// 对connfd 操作
			FD_SET(connfd,&rfds);

			if(connfd > max_fd)	max_fd = connfd;

			if(--nready == 0) continue;
				
		}
		int i = 0;
		for(i = listenfd+1; i<=max_fd; i++){
			if(FD_ISSET(i,&rset)){
				n = recv(i,buff,MAXLNE,0);
				if(n>0){
					buff[n] = '\0';
					printf("recv msg from client: %s\n", buff);

					FD_SET(i, &wfds);
					
				}else if (n == 0){
					FD_CLR(i, &rfds);
					close(i);
				}
				if (--nready == 0) break;
			}else if(FD_ISSET(i,&wset)){
				send(i,buff,n,0);
				FD_SET(i,&rfds);
			}
		}
		
		
	}
	
	close(listenfd);
	return 0;
}

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Rinex 2.1.1是一种用于存储全球定位系统(GPS)观测数据的格式。Rinex是"Receiver Independent Exchange"的缩写,表明了其独立于接收设备的特点。Rinex 2.1.1是Rinex版本2的一个子版本。 Rinex格式的设计目的是为了让不同厂家、不同型号的GPS接收设备能够互相兼容和交换观测数据。它提供了一种标准化的数据格式,使得用户可以方便地处理和分析GPS观测数据,而不需要考虑数据来自于何种设备或来源。 Rinex 2.1.1的文件结构包括头文件和数据文件两部分。头文件中包含了实验的元数据信息,如GPS观测站点的位置和高度、观测时间段、GPS系统类型等。数据文件中包含了具体的观测数据,如卫星的伪距观测值和载波相位观测值。 Rinex 2.1.1的使用广泛应用于地理测绘、大地测量、导航、气象和科学研究等领域。通过分析和处理Rinex格式的观测数据,可以得到具体的GPS测量结果,如位置坐标、高度、速度和时间等。这些结果在地图制作、导航系统精度评定、地壳变形分析以及气象预测等方面具有重要意义。 总之,Rinex 2.1.1是一种用于存储GPS观测数据的标准格式,可以实现不同厂家、型号的接收设备的数据交换和处理。它在地理测绘、导航、大地测量和科学研究等领域有着广泛应用,为相关领域的数据分析和结果获取提供了方便和可靠的方式。 ### 回答2: RINEX 2.1.1是一种通用的卫星导航接收机数据格式。RINEX代表"Receiver Independent Exchange",是一种国际标准格式,用于存储和交换全球卫星定位系统(GNSS)接收机的原始观测数据。 RINEX 2.1.1格式是RINEX系列中的一种,由RINEX工作组在2003年发布。它是RINEX 2.0版本的升级,主要用于记录GPS导航卫星系统和GLONASS导航卫星系统的观测数据。RINEX 2.1.1格式允许用户在GNSS数据处理软件之间进行数据交换,以进行后续的数据处理和分析。 RINEX 2.1.1文件包含了GNSS接收机的观测数据、导航电文、接收机参数和站点配置信息等。观测数据包括接收机每个可见卫星的伪距观测值、载波相位观测值以及卫星的导航电文数据。这些数据对于卫星信号的精密导航定位和测量非常重要。 RINEX 2.1.1格式的文件由ASCII文本组成,可以使用任何文本编辑器查看和编辑。每个文件通常对应一次观测的时间段,可以包括多个观测站的数据。文件中的数据以可读的格式进行记录,包括时间、卫星编号、观测值等等。 RINEX 2.1.1是过去几十年来最常用的数据交换格式之一,但是随着GNSS技术的不断发展,新的RINEX版本也在不断推出。RINEX 2.1.1的局限性在于只支持GPS和GLONASS系统,不支持其他GNSS系统如Galileo和北斗。因此,如果需要处理包含更多GNSS系统的数据,可能需要使用更新的RINEX版本或其他格式。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值