select完成服务器并发

服务器:

cpp 复制代码
#include <myhead.h>
 
#define PORT 4399 	//端口号
#define IP "192.168.0.191"//IP地址
 
//键盘输入事件
int keybord_events(fd_set readfds);
//客户端交互事件
int cliRcvSnd_events(int , struct sockaddr_in*, fd_set *, int *);
//客户端连接事件
int cliConnect_events(int , struct sockaddr_in*, fd_set *, int *);
 
int main(int argc, const char *argv[])
{
	//创建流式套接字
	int sfd = socket(AF_INET, SOCK_STREAM, 0);
	if(sfd < 0)
	{
		ERR_MSG("socket");
		return -1;
	}
	printf("socket create success sfd=%d\n", sfd);
 
	//允许端口被快速复用
	int reuse = 1;
	if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
	{
		ERR_MSG("setsockopt");
		return -1;
	}
	//填充地址信息结构体
	struct sockaddr_in sin;
	sin.sin_family = AF_INET;//必须填AF_INET
	sin.sin_port = htons(PORT);//端口号的网络字节序
	sin.sin_addr.s_addr = inet_addr(IP);//本机IP
 
	//绑定服务器的地址信息
	if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) < 0)
	{
		ERR_MSG("bind");
		return -1;
	}
	printf("bind success\n");
	//将套接字设置为被动监听状态
	if(listen(sfd, 128) < 0)
	{
		ERR_MSG("listen");
		return -1;
	}
	printf("listen success\n");
	//创建一个读集合,一个操作集合
	fd_set readfds, tempfds;
 
	//清空集合
	FD_ZERO(&readfds);
 
	//将需要监测的文件描述符加入读集合
	FD_SET(0, &readfds);
	FD_SET(sfd, &readfds);
 
	int maxfd = sfd; 	//存储最大的文件描述符
	int s_res = -1;
	ssize_t res = -1;
	char buf[128] = "";
	struct sockaddr_in saveCin[1024]; 	//备份连接成功的客户端的地址信息,且用下标对应文件描述符
 
	while(1)
	{
		tempfds = readfds;
		//执行多路复用函数
		s_res = select(maxfd+1, &tempfds, NULL, NULL, NULL);
		if(s_res < 0)
		{
			ERR_MSG("select");
			return -1;
		}
		else if(0 == s_res)
		{
			printf("time out...\n");
			break;
		}
 
 
		//此时代表select函数解除阻塞,集合中有文件描述符存在
		for(int i=0;i<=maxfd;i++)
		{
			if(FD_ISSET(i, &tempfds) == 0)
 
				continue;
 
			//此时代表i所对应的文件描述符在集合中
			if(0 == i)
			{
				keybord_events(readfds);
			}
			else if(sfd == i)//代表sfd在集合中
			{
				//	printf("触发客户端连接事件\n");
				cliConnect_events(sfd, saveCin, &readfds, &maxfd);
			}
			else
			{
				//	printf("触发客户端交互事件\n");
				cliRcvSnd_events(i, saveCin, &readfds, &maxfd);
			}
		}
 
	}
 
	if(close(sfd) < 0)
	{
		ERR_MSG("close");
		return -1;
	}
 
 
 
	return 0;
}
 
//键盘输入事件
int keybord_events(fd_set readfds)
{
	char buf[128] = "";
	int sndfd = -1;
	bzero(buf, sizeof(buf));
 
	int res = scanf("%d %s", &sndfd, buf);
	while(getchar() != 10);
	if(res != 2)
	{
		printf("输入数据的格式错误 :fd string\n");
		return -1;
	}
 
	if(sndfd<=2 || FD_ISSET(sndfd, &readfds) == 0)
	{
		printf("非法的文件描述符:sndfd=%d\n", sndfd);
		return -1;
	}
 
	if(send(sndfd, buf, sizeof(buf), 0) < 0)
	{
		ERR_MSG("send");
		return -1;
	}
 
	printf("send success\n");
 
	return 0;
}
 
//客户端连接事件
int cliConnect_events(int sfd, struct sockaddr_in saveCin[], fd_set *preadfds, int *pmaxfd)
{
	int newfd = -1;
	struct sockaddr_in cin;//存储客户端地址信息
	socklen_t addrlen = sizeof(cin); //真实的地址信息结构体的大小
 
	newfd = accept(sfd, (struct sockaddr*)&cin, &addrlen);
	if(newfd < 0)
	{
		ERR_MSG("newfd");
		return -1;
	}
	printf("[%s:%d]客户端连接成功 newfd=%d\n",\
			inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd);
	saveCin[newfd] = cin; 	//将cin另存到newfd对应的下表位置
	FD_SET(newfd, preadfds); //将newfd添加到集合中
	*pmaxfd = *pmaxfd>newfd ? *pmaxfd:newfd; 	//更新maxfd
 
	return 0;
}
 
//客户端交互事件
int cliRcvSnd_events(int fd, struct sockaddr_in* saveCin, fd_set *preadfds, int *pmaxfd)
{
	char buf[128] = "";
	//清空字符串
	bzero(buf, sizeof(buf));
 
	//接收
	ssize_t res = recv(fd, buf, sizeof(buf), 0);
	if(res < 0)
	{
		ERR_MSG("recv");
		return -1;
	}
	else if(0 == res)
	{
		printf("[%s:%d]客户端下线 newfd=%d\n",\
				inet_ntoa(saveCin[fd].sin_addr), ntohs(saveCin[fd].sin_port), fd);
		close(fd);//关闭文件描述符
		FD_CLR(fd, preadfds); //将文件描述符从集合中删除
 
		//由于删除的文件描述符可能是最大的文件描述符,所以要更新maxfd
		while(FD_ISSET(*pmaxfd, preadfds) == 0 && (*pmaxfd)-- >= 0);
		return 0;
	}
	printf("[%s:%d] newfd=%d:%s\n",\
			inet_ntoa(saveCin[fd].sin_addr), ntohs(saveCin[fd].sin_port), fd, buf);
 
	//发送信息
	strcat(buf, "*_*");
	if(send(fd, buf, sizeof(buf), 0) < 0)
	{
		ERR_MSG("send");
		return -1;
	}
	printf("send success\n");
	return 0;
}

客户端:

cpp 复制代码
#include<myhead.h>
 
#define PORT 4399             //服务器绑定的端口号
#define IP  "192.168.0.191"   //服务器绑定的IP
 
 
int main(int argc, const char *argv[])
{
	//创建流式套接字 socket
	int cfd = socket(AF_INET, SOCK_STREAM, 0);
	if(cfd < 0)
	{
		ERR_MSG("socket");
		return -1;
	}
	printf("socket create success cfd=%d\n", cfd);
 
	//绑定客户端的地址信息---》非必须绑定
	//当不手动绑定的时候,操作系统会自动给客户端绑定本机IP和随机端口。   
 
 
	//填充服务器的地址信息结构体给connect函数连接,
	//想连接哪个服务器,就填哪个服务器绑定的地址信息
	//真实的地址信息结构体根据地址族指定 AF_INET:man 7 ip
	struct sockaddr_in sin;
	sin.sin_family      = AF_INET;      //必须填AF_INET;
	sin.sin_port        = htons(PORT);  //服务器绑定的端口号
	sin.sin_addr.s_addr = inet_addr(IP);//服务器绑定的IP
 
	//连接指定服务器 connect
	if(connect(cfd, (struct sockaddr*)&sin, sizeof(sin)) < 0)
	{
		ERR_MSG("connect");
		return -1;
	}
	printf("connect success\n");
 
 
	//定义要监测的集合
	fd_set readfds,tempfds;
 
	FD_ZERO(&readfds);
 
	//将需要的文件描述符加入集合
	FD_SET(0,&readfds);
	FD_SET(cfd,&readfds);
 
	int s_res = -1;
 
	char buf[128] = "";
	ssize_t res = 0;
 
	while(1)
	{
		tempfds=readfds;
		s_res = select(cfd+1, &tempfds, NULL, NULL, NULL);
		if(s_res < 0)
		{
			ERR_MSG("selsct");	
			return -1;
		}
		else if(0 == s_res)
		{
			printf("time out....\n");
			break;
		}
		//运行到此,则代表集合中有文件描述符准备就绪
 
		if(FD_ISSET(0, &tempfds))
		{
			//清空字符串
			bzero(buf, sizeof(buf));    
 
			fgets(buf, sizeof(buf), stdin);
			buf[strlen(buf)-1] = 0;
 
			//发送
			if(send(cfd, buf, sizeof(buf), 0) < 0)
			{
				ERR_MSG("send");
				return -1;
			}
			printf("send success\n");
		}
 
		if(FD_ISSET(cfd, &tempfds))
		{
			bzero(buf, sizeof(buf));    //memset
			//接收
			res = recv(cfd, buf, sizeof(buf), 0);
			if(res < 0)
			{
				ERR_MSG("recv");
				return -1;
			}
			else if(0 == res)
			{
				printf("服务器下线 cfd=%d\n", cfd);
				break;
			}
			printf("cfd=%d : %s\n", cfd, buf);
 
		}
	}
 
 
	//关闭文件名描述符
	if(close(cfd) < 0)
	{
		ERR_MSG("close");
		return -1;
	}
 
	return 0;
}

效果:

相关推荐
Channing Lewis1 小时前
zoho crm为什么xx是deal的关联对象但是调用函数时报错说不是关联对象
运维·服务器·windows
段嘉许OvO1 小时前
Red靶机攻略
linux·运维·服务器
仍然探索未知中2 小时前
Linux守护进程
linux·运维·服务器
馨羽的玩具3 小时前
查哪个程序一直登录sql server失败
运维·服务器·数据库
luoqice3 小时前
在嵌入式 Linux 系统中,配置 DNS 以实现内网或外网连接
linux·运维·服务器
妙妙屋(zy)4 小时前
基于Astro开发的Fuwari静态博客模版配置CICD流程
服务器·docker
IvanCodes4 小时前
三、Linux用户与权限管理详解
linux·运维·服务器
默默敲代码的徐哥儿4 小时前
八股文整理——计算机网络
服务器·网络·计算机网络
SBFE5 小时前
出现错误,Microsoft store初始化失败。请尝试刷新或稍后返回。
microsoft
cyber_两只龙宝5 小时前
RHCE综合项目:分布式LNMP私有博客服务部署
linux·运维·服务器·分布式·虚拟机·dns·nfs