select多连接处理

本文详细介绍了如何在物联网环境中使用select函数处理多客户端socket通信。通过一个服务器端示例,展示了如何在一个线程内同时处理多个socket的IO请求,包括建立连接、接收数据和响应。客户端代码则展示了一个连接服务器并间隔发送数据的场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在物联网socket通信时,对于多客户访问,或多个连接通信,一般不采用多线程处理,而是使用select。selcet可以在一个线程内同时处理多个socket的IO请求。

1、select接口函数:

int select(int maxfdp, fd_set *readset, fd_set *writeset, fd_set *exceptset,struct timeval *timeout);

 参数说明:

maxfdp:被监听的文件描述符的总数,它比所有文件描述符集合中的文件描述符的最大值大1,因为文件描述符是从0开始计数的;

readfds、writefds、exceptset:分别指向可读、可写和异常等事件对应的描述符集合。

timeout:用于设置select函数的超时时间,为NULL 表示无限等待,timeval结构体定义如下:

struct timeval
{      
    long tv_sec;   /*秒 */
    long tv_usec;  /*微秒 */   
};
返回值:超时返回0;失败返回-1;成功返回大于0的整数,表示就绪描述符的数目。

2、和select一起使用的宏

FD_ZERO(fd_set *fdset):清空fdset与所有文件句柄的联系。

FD_SET(int fd, fd_set *fdset):建立文件句柄fd与fdset的联系。

FD_CLR(int fd, fd_set *fdset):清除文件句柄fd与fdset的联系。

FD_ISSET(int fd, fd_set *fdset):检查fdset联系的文件句柄fd是否可读写,当>0表示可读写。

3、server端代码

/*
主要功能:
1、建立socket,监听连接;

2、支持多个客户端连接;

3、客户端断开时释放资源;

4、接收数据并打印字符串,回复ok。
*/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define TCP_PORT    12321

#define CLIENT_MAX_NUMB    8
#define BUFFER_MAX_LEN     1500

//接收数据处理
int recv_data_handle(char *data, int len)
{
	int i;
	
	if(len <= 0){
		return -1;
	}
	
	printf("recv len: %d, data:\n", len);
	
	for(i = 0; i < len; i++){
		if(i !=0 && i%16 == 0){
			printf("\n");
		}
		printf("%02X ", data[i]);
	}
	printf("\n");
	return 0;
}

//TCP server
int main(void)
{
	int i = 0;
	int j = 0;
	int ret = 0;
	int tcp_fd = -1;
	int new_fd = -1;
	
	int max_fd = 0;
	fd_set readfds;
	
	int len;
	char buf[BUFFER_MAX_LEN];
	
	struct timeval time_out;
	
	int on = 1;
	struct sockaddr_in server_addr;
	struct sockaddr_in client_addr;
	
	socklen_t sin_size;
	
	int client_fd[CLIENT_MAX_NUMB];

	//创建TCP
	memset(&server_addr, 0, sizeof(server_addr));
	
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(TCP_PORT);
	server_addr.sin_addr.s_addr = htonl(INADDR_ANY);

	//创建socket
	tcp_fd = socket(AF_INET, SOCK_STREAM, 0);
	//允许端口复用
	if(setsockopt(tcp_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0){
		printf("setsockopt error!\n");
		goto RET;
	}
	//绑定
	if(bind(tcp_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0){
		printf("bind error!\n");
		goto RET;
	}
	//监听
	if(listen(tcp_fd, 0) < 0){
		printf("listen error!\n");
		goto RET;
	}
	printf("tcp create ok\n");
	
	for(i = 0; i < CLIENT_MAX_NUMB; i++){
		client_fd[i] = -1;
	}
	
	while(1){
		FD_ZERO(&readfds);

		if(tcp_fd >= 0){
			FD_SET(tcp_fd, &readfds);
		}
		for(i = 0; i < CLIENT_MAX_NUMB; i++){
			if(client_fd[i] >= 0){
				FD_SET(client_fd[i], &readfds);
			}
		}
		
		max_fd = tcp_fd;
		for(i = 0; i < CLIENT_MAX_NUMB; i++){
			if(max_fd < client_fd[i]){
				max_fd = client_fd[i];
			}
		}

		time_out.tv_usec = 0;
		time_out.tv_sec = 2;

		ret = select(max_fd+1, &readfds, NULL, NULL, &time_out);
		if (ret < 0){
			break;
		}
		if (ret == 0){
			printf("seclet time out...\n");
			continue;
		}
		for(i = 0; i < ret; i++){
			if(FD_ISSET(tcp_fd, &readfds)){
				//接收新连接
				memset(&client_addr, 0, sizeof(client_addr));
	
				sin_size = sizeof(client_addr);
				new_fd = accept(tcp_fd, (struct sockaddr *)(&client_addr), &sin_size);
				if(new_fd < 0){
					printf("accept error\n");
					continue;
				}
				printf("accept a client!\naddr = %s:%d\n", inet_ntoa(client_addr.sin_addr), client_addr.sin_port);

				for(j = 0; j < CLIENT_MAX_NUMB; j++){
					if(client_fd[j] < 0){
						client_fd[j] = new_fd;
						break;
					}
				}
				if(j == CLIENT_MAX_NUMB){
					printf("client too mach\n");
					close(new_fd);
				}
				continue;
			}
			//处理多个客户端数据
			for(j = 0; j < CLIENT_MAX_NUMB; j++){
				if(client_fd[j] < 0){
					continue;
				}
				if(FD_ISSET(client_fd[j], &readfds)){
					memset(buf, 0, BUFFER_MAX_LEN);
					len = recv(client_fd[j], buf, BUFFER_MAX_LEN, 0);
					if(len <= 0){ 
						close(client_fd[j]);
						client_fd[j] = -1;
						printf("recv data error\n");
						continue;
					}
					printf("\nrecv, client[%d]: %s\n", j, buf);
					//recv_data_handle(buf, len);
					send(client_fd[j], "ok", strlen("ok"), 0);
					printf("send, client[%d]: ok\n\n", j);
				}
			}
		}		
	}
RET:
	//关闭所有client fd
	for(i = 0; i < CLIENT_MAX_NUMB; i++){
		if(client_fd[i] > 0){
			close(client_fd[i]);
			client_fd[i] = -1;
		}
	}
	//关闭server fd
	close(tcp_fd);
	tcp_fd = -1;
	
	printf("select error, exit!\n");

	return -1;
}

 4、client端代码

/*
 功能:
 1、自动连接本地服务器端;
 2、间隔发送数据hello,并打印回复;
 3、服务器断开自动重连;
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/select.h>

#include <time.h>

#define SERVER_PORT 12321
#define BUFFER_MAX_LEN     1500

int main(void)
{
	int ret;
	int len;
	
	int max_fd;
	int client_fd = -1;
	
	int numb = 2;

	struct sockaddr_in addr;

	char buf[BUFFER_MAX_LEN];
	
	fd_set readfds;
	
	struct timeval time_out;

	// 套接字信息
	memset(&addr, 0, sizeof(addr));

	addr.sin_family = AF_INET;
	addr.sin_port = htons(SERVER_PORT);
	addr.sin_addr.s_addr = inet_addr("127.0.0.1");
   
	while(1)
	{
		if(client_fd < 0){
			// 建立套接字
			client_fd = socket( AF_INET, SOCK_STREAM, 0);
			if(client_fd < 0){
				printf("Socket Error\n");
				return -1;
			}
			// 链接server
			if(connect(client_fd, ( struct sockaddr * )&addr, sizeof(addr)) < 0){
				printf("Connect error\n");
				close(client_fd);
				client_fd = -1;
				sleep(2);
				continue;
			}
			
			printf("conent ok!\n\n");
		}
		FD_ZERO(&readfds);
		FD_SET(client_fd, &readfds);
		
		max_fd = client_fd;
		
		time_out.tv_usec = 0;
		time_out.tv_sec = 2;
		
		if(numb++ == 3 && client_fd >= 0){
			numb = 0;
			send(client_fd, "hello", strlen("hello"), 0);
			printf("send: hello\n");
		};
		
      
		ret = select(max_fd+1, &readfds, NULL, NULL, &time_out);
		if(ret < 0){
			printf("Client Select Error..\n");
			goto RET;
		}
		if(ret == 0){
			continue;
		}

		if(FD_ISSET(client_fd, &readfds)){
			memset(buf, 0, sizeof(buf));
			len = recv(client_fd, buf, BUFFER_MAX_LEN, 0);
			if(len > 0){
				printf("recv: %s\n\n", buf);
			}
			if(len == 0){
				close(client_fd);
				client_fd = -1;
			}
		}
	}
RET:
	close(client_fd);
	printf("Exit\n");
   
	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值