欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

linux c IO多路复用之select函数模型,构建C/S来实现服务器与客户端之间进行通信

程序员文章站 2022-06-06 09:20:32
...
//"util.h"
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/time.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<sys/select.h>
#define backlogs 10
#define BUF_SIZE 50
int Socket(int domain,int type,int protocol)
{
	int fd=socket(domain,type,protocol);
	if(fd==-1)
		return -1;
	return fd;
}
int Bind(int sockfd,const struct sockaddr *addr,socklen_t addrlen)
{
	int ret=bind(sockfd,addr,addrlen);
	if(ret==-1)
		return -1;
	return ret;
}
int Listen(int sockfd,int backlog)
{
	int ret=listen(sockfd,backlog);
	if(ret==-1)
		return -1;
	return ret;
}
int start_up(const char* ip,int port)
{
	int fd=Socket(AF_INET,SOCK_STREAM,0);
	struct sockaddr_in ser_addr;
	ser_addr.sin_family=AF_INET;
	ser_addr.sin_port=htons(port);
	ser_addr.sin_addr.s_addr=inet_addr(ip);
	int on=1;
	setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(int));
	socklen_t addrlen=sizeof(struct sockaddr);
	Bind(fd,(struct sockaddr*)&ser_addr,addrlen);
	Listen(fd,backlogs);
	return fd;
}

//ser.c 服务器端
#include"util.h"
int main(int argc,char *argv[])
{
	int i,j,n,maxi;
	int nready,client[FD_SETSIZE];//FD_SETSIZE  1024
	int maxfd,listenfd,connfd,sockfd;
	char buf[BUF_SIZE];
	char str[INET_ADDRSTRLEN];//16
	struct sockaddr_in clie_addr,serv_addr;
	socklen_t clie_addr_len;
	fd_set rset,allset;
	listenfd=start_up(argv[1],atoi(argv[2]));
	maxfd=listenfd;             //起初listenfd 即为最大文件描述符
	maxi=-1;                    //将来用作client[]的下标,初始值指向下标为-1的位置
	for(i=0;i<FD_SETSIZE;i++)//1024
		client[i]=-1;//用-1初始化client
	FD_ZERO(&allset);
	FD_SET(listenfd,&allset);         //构造select监控文件描述符集

	while(1)
	{
		rset=allset;                  //每次循环时都从新设置select监控信号集
		nready=select(maxfd+1,&rset,NULL,NULL,NULL);//rset在select返回后在改变  //没有发生可读事件的fd将被置0
		if(nready<0)
		{
			perror("select error\n");
			continue;
		}
		if(FD_ISSET(listenfd,&rset))        //说明有新的客户端连接
		{
			clie_addr_len=sizeof(struct sockaddr);
			connfd=accept(listenfd,(struct sockaddr *)&clie_addr,&clie_addr_len);//accept() 不会阻塞
			printf("received from %s at PORT %d\n",
					inet_ntop(AF_INET,&clie_addr.sin_addr,str,sizeof(str)),
					ntohs(clie_addr.sin_port));
			for(i=0;i<FD_SETSIZE;i++)
			{
				if(client[i]==-1)
				{
					client[i]=connfd;    //保存accept返回的文件描述符到client[]里
					break;
				}
			}
			if(i==FD_SETSIZE)            //达到select能监控的文件个数上限1024
			{
				fputs("too many clients!!! ,The Ser is Load",stderr);
				exit(1);
			}
			FD_SET(connfd,&allset);
			if(connfd>maxfd)
				maxfd=connfd;
			if(i>maxi)
				maxi=i;
			if(--nready==0)
				continue;
		}
		for(i=0;i<=maxi;i++)
		{
			if( (sockfd=client[i])<0)
				continue;
			if(FD_ISSET(sockfd,&rset))
			{
				if( (n=recv(sockfd,buf,sizeof(buf),0))==0 ) 
				{
					close(sockfd);
					FD_CLR(sockfd,&rset);
					client[i]=-1;
				}
				else if(n>0)
				{
					for(j=0;j<n;j++)
						buf[j]=toupper(buf[j]);
					send(sockfd,buf,n,0);
				}
				if(--nready==0)
					break;
			}
		}
	}
	close(listenfd);
	return 0;
}

//客户端
//cli.c
#include"util.h"
int main(int argc,char *argv[])
{
	char sendbuf[128];
	char recvbuf[128];
	socklen_t addrlen=sizeof(struct sockaddr);
	int cli_fd=socket(AF_INET,SOCK_STREAM,0);
	if(cli_fd==-1)
		return -1;
	struct sockaddr_in seraddr;
	seraddr.sin_family=AF_INET;
	seraddr.sin_port=htons(atoi(argv[2]));
	seraddr.sin_addr.s_addr=inet_addr(argv[1]);
	int ret=connect(cli_fd,(struct sockaddr*)&seraddr,addrlen);
	if(ret==-1)
	{
		perror("connect error!\n");
		return -1;
	}
	else
		printf("success\n");
	while(1)
	{
		printf("Cli:>");
		scanf("%s",sendbuf);
		send(cli_fd,sendbuf,strlen(sendbuf)+1,0);
		recv(cli_fd,recvbuf,128,0);
		printf("From Self Cli:%s\n",recvbuf);
	}
	close(cli_fd);
	return 0;
}