select()函数_笔记

参考文章:http://www.cnblogs.com/Anker/archive/2013/08/14/3258674.html

 

IO多路复用:内核一旦发现进程指定的一个或者多个IO条件准备读取,就通知该进程。优势是系统不需要创建进程/线程,也不必维护,减少开销

适用场景:

1)当处理多个描述符是(一般是交互式输入和网络socket接口),必须使用IO复用

2)当同时处理多个socket接口

3)TCP服务器既要监听连接请求,又要处理已连接socket

4)既要处理TCP,又要处理UDP

5)服务器需要处理多个服务或者协议

 

#include <sys/select.h>
#include <sys/time.h>

int select(int maxfdp1,fd_set *readset,fd_set *writeset,fd_set *exceptset,const struct timeval *timeout)
返回值:就绪描述符的数目,超时返回0,出错返回-1

 

 

 

 

 

 

Server.c

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


#define IPADDR		"127.0.0.1"
#define PORT		8787
#define MAXLINE		1024			//接收buffer的最大长度
#define LISTENQ		5			    //指定能同时处理的最大连接数
#define SIZE		10				//限制客户端个数


typedef struct server_context_st{
	int cli_cnt;		// 客户端个数
	int clifds[SIZE];	// 客户端,保存客户端FDS
	fd_set allfds;		// 句柄集合
	int maxfd;			// 句柄最大值
}server_context_st;

static server_context_st *s_srv_ctx	= NULL;


//client连接请求
static int accept_client_proc(int srvfd)
{
	int clifd = -1;
	int i;
	struct sockaddr_in cliaddr;
	socklen_t cliaddrlen;
	cliaddrlen = sizeof(cliaddr);
	
	// 接受client连接请求
	clifd = accept(srvfd, (struct sockaddr*)&cliaddr, &cliaddrlen);
	if(clifd == -1){
		printf("accept failed ,errno = %d, %s\r\n", errno, strerror(errno));
		return -1;
	}
	printf("accept new client: %s  %d\r\n", inet_ntoa(cliaddr.sin_addr), cliaddr.sin_port);
	
	// 将客户端描述符保存,在select函数中使用
	for(i = 0 ; i < SIZE; i++){
		if(s_srv_ctx->clifds[i] == -1){
			s_srv_ctx->clifds[i] = clifd;
			s_srv_ctx->cli_cnt++;
			break;
		}
		
	}
	if(i == SIZE){
		printf("client full up\r\n");
		return -2;
	}
	
	return 0;
}


//接收client消息
static void recv_client_msg(fd_set *readfds)
{
	int i;
	char recv_buf[MAXLINE];
	int len;
	int clifd;
	for(i = 0 ; i < s_srv_ctx->cli_cnt; i++){
		clifd = s_srv_ctx->clifds[i];
		if(clifd < 0)
			continue;
		
		// 判断是哪个client 描述符 发送消息
		if(FD_ISSET(clifd, readfds)){
			//读
			len = read(clifd, recv_buf, MAXLINE);
			if(len <= 0){
				// n=0 可能client连接已中断,将对应client 描述符从集合中删除	
				FD_CLR(clifd, &s_srv_ctx->allfds);
				s_srv_ctx->clifds[i] = -1;
				// 关闭TCP连接
				close(clifd);
				printf("close connection ID = %d\r\n", i);
				continue;
			}
			else{
				// 将接收到的消息重新返回client
				printf("server recv %dByte msg : %s\r\n", len, recv_buf);
				write(clifd, recv_buf, len);
			}
			
		}
	}
}

int main(int agrc , char **agrv)
{
	int i;
	int srvfd;
	int ret;
/* Step1 初始化 s_srv_ctx 结构体,将所有client成员设置为无效-1*/	
	s_srv_ctx = (server_context_st *)malloc(sizeof(server_context_st));
	if(s_srv_ctx == NULL){
		return -1;
	}
	memset(s_srv_ctx, 0, sizeof(server_context_st));
	for(i = 0 ; i < SIZE; i++){
		s_srv_ctx->clifds[i] = -1;
	}

/* Step2 创建server socket  bind ip和port  监听客户端连接 */
	srvfd = socket(AF_INET, SOCK_STREAM, 0);
	if(srvfd == -1){
		printf("creat socket failed, errno = %d, reason: %s\r\n", errno, strerror(errno));
		goto s_exit;
	}
	// SO_REUSEADDR 设置端口释放后可以立即再次使用
	int reuse = 1;
	if(setsockopt(srvfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1){
		printf("setsockopt failed \r\n");
		goto s_exit;
	}
	
	struct sockaddr_in servaddr;
	bzero(&servaddr, sizeof(struct sockaddr_in));
	servaddr.sin_family = AF_INET;
	inet_pton(AF_INET, IPADDR, &servaddr.sin_addr);
	servaddr.sin_port = htons(PORT);
	
	if(bind(srvfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){
		printf("bind error\r\n");
		goto s_exit;
	}
	listen(srvfd, LISTENQ);

/* Step3 接收并处理客户端请求 */
	struct timeval tv;
	while(1){
	// 每次调用select之前都要重新设置文件描述符和时间,因为事件发生后文件描述符和时间被系统修改
		// 添加描述符srvfd,用于监听是否有客户端连接
		FD_ZERO(&s_srv_ctx->allfds);
		FD_SET(srvfd, &s_srv_ctx->allfds);
		s_srv_ctx->maxfd = srvfd;
		// 轮询client列表,判断客户端是否有效,并添加有效的客户端描述符clifds[i]
		for(i = 0 ; i < s_srv_ctx->cli_cnt; i++){
			if(s_srv_ctx->clifds[i] != -1){
				FD_SET(s_srv_ctx->clifds[i], &s_srv_ctx->allfds);
			}
			//设置最大描述符
			if(s_srv_ctx->clifds[i] > s_srv_ctx->maxfd){
				s_srv_ctx->maxfd = s_srv_ctx->clifds[i];
			}
		}

		tv.tv_sec = 20;
		tv.tv_usec = 0;
		ret = select(s_srv_ctx->maxfd+1, &s_srv_ctx->allfds, NULL, NULL, &tv);
		if(ret == -1){
			printf("select error = %d %s\r\n", errno, strerror(errno));
			break;
		}else if(ret == 0){
			printf("time out\r\n");
			continue;
		}
		
		if(FD_ISSET(srvfd, &s_srv_ctx->allfds)){
			//printf("client request\r\n");
			accept_client_proc(srvfd);      		// 处理client请求连接
		}else{
			//printf("client msg proc\r\n");	
			recv_client_msg(&s_srv_ctx->allfds);   	// 处理client发送消息
		}
	}
	
// 退出,释放资源	
s_exit:
	free(s_srv_ctx);
	s_srv_ctx = NULL;
	
}

 

Client.c

 

 

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


#define MAXLINE			1024
#define IPADDRESS		"127.0.0.1"
#define SERV_PORT 		8787

static char recv_buf[MAXLINE];
static char hello_server[] = {"hello server"};


int main(int argc , char **argv)
{
	int sockfd;
	int ret;
	struct sockaddr_in	servaddr;
// Step1 创建client socket	
	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	
	bzero(&servaddr, sizeof(struct sockaddr_in));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(SERV_PORT);
	inet_pton(AF_INET, IPADDRESS, &servaddr.sin_addr);
// Step2 请求连接server	
	ret = connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
	if(ret < 0){
		printf("connect failed ,errno = %d, %s\r\n", errno, strerror(errno));
		return -1;
	}
// 发送消息 hello	
	write(sockfd, hello_server, sizeof(hello_server));
	
	int len;
	fd_set readfds;
	struct timeval tv;
	while(1){
		FD_ZERO(&readfds);
		FD_SET(sockfd, &readfds);
		
		//
		tv.tv_sec = 5;
		tv.tv_usec = 0;	
		ret = select(sockfd+1, &readfds, NULL, NULL, &tv);
		if(ret == -1){
			printf("client select error = %d, %s\r\n", errno, strerror(errno));
			return -2;
		}else if(ret == 0){
			printf("client time out\r\n");
			continue;
		}
		
		if(FD_ISSET(sockfd, &readfds)){
			len = read(sockfd, recv_buf, MAXLINE);
			if(len <= 0){
				printf("server connection closed\r\n");
				goto c_exit;
			}
			else{
				// 将server发送的消息返回server ,其实是第一次从client发送的hello
				printf("client recv %dByte msg : %s\r\n", len, recv_buf);
				sleep(5);
				write(sockfd, recv_buf, len);
			}
		}
		
	}
	
c_exit:
	close(sockfd);
	FD_CLR(sockfd, &readfds);
	return 0;
}

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在SQL查询中,SELECT聚合函数用于对一组数据执行计算并返回一个值。常见的SELECT聚合函数包括: - AVG():计算指定字段的平均值。 - SUM():计算指定字段的总和。 - COUNT():计算指定字段的数量。 - MIN():返回指定字段的最小值。 - MAX():返回指定字段的最大值。 使用聚合函数时,通常需要配合GROUP BY子句来指定分组的字段。这样可以对每个分组执行聚合函数,并返回每个分组的计算结果。 例如,以下是使用SELECT聚合函数的示例查询语句: SELECT AVG(salary), department_id FROM employees GROUP BY department_id; 上述查询会计算每个部门的平均工资,并返回每个部门的平均工资和部门ID。 除了聚合函数之外,SELECT语句还可以包含其他字段和条件,以进一步筛选和排序查询结果。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [SELECT数据查询4——聚合函数详解](https://blog.csdn.net/qq_43468008/article/details/105206204)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [MySQL学习笔记10-----SELECT的聚合函数们](https://blog.csdn.net/pythonofstar/article/details/124523806)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值