2022.11.25---IO多路复用(项目已完成--2022.11.27)

本文详细展示了服务器端使用select和poll进行IO多路复用,以及客户端与服务器的交互过程,包括文件查看、下载和上传功能。还涉及了简单的登录注册逻辑。

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

完成select的服务器和客户端

//服务器
#include <stdio.h>
#include <netinet/ip.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/time.h>
#include <string.h>
#include <unistd.h>
#define ERR(msg) do{\
	fprintf(stderr,"%d--",__LINE__);\
	perror(msg);\
}while(0)
#define SER_PORT 7777
#define SER_IP "192.168.8.205"
int ser_init();
int main(int argc, const char *argv[])
{
	char buf[128]="";
	ssize_t res=0;
	struct sockaddr_in cin;
	struct sockaddr_in cin_arr[1020];
	socklen_t addr=sizeof(cin);
	int sfd=ser_init();
	printf("服务器就绪\n");
	//初始化读集合
	fd_set rfds;
	fd_set temp_fds;
	FD_ZERO(&rfds);
	FD_ZERO(&temp_fds);
	FD_SET(0,&rfds);
	FD_SET(sfd,&rfds);
	int max_fd=sfd;
	//开始IO多路复用
	while(1)
	{
		//判断是否有文件描述符就绪
		temp_fds=rfds;
		if(select(max_fd+1,&temp_fds,NULL,NULL,NULL)<0)
		{
			ERR("select");
			return -1;
		}
		for(int i=0;i<=max_fd;i++)
		{
			if(FD_ISSET(i,&temp_fds))
			{
				if(0==i)
				{
					printf("触发键盘输入\n");
					int snd_fd;
					scanf("%d %s",&snd_fd,buf);
					while(getchar()!=10);
					if(snd_fd>sfd && FD_ISSET(snd_fd,&rfds))
					{
						if(send(snd_fd,buf,sizeof(buf),0)<0)
						{
							ERR("send");
							return -1;
						}
					}
				}
				else if(i==sfd)
				{
					printf("触发客户端连接\n");
					int nfd=accept(sfd,(struct sockaddr*)&cin,&addr);
					if(nfd<0)
					{
						ERR("accept");
						return -1;
					}
					printf("%d号客户端连接成功\n",nfd);
					cin_arr[nfd]=cin;
					FD_SET(nfd,&rfds);
					max_fd=max_fd>nfd?max_fd:nfd;
				}
				else
				{
					printf("触发客户端交互\n");
					bzero(buf,sizeof(buf));
					res=recv(i,buf,sizeof(buf),0);
					if(res<0)
					{
						ERR("recv");
						return -1;
					}
					else if(0==res)
					{
						printf("%d客户端退出\n",i);
						close(i);
						FD_CLR(i,&rfds);
						for(int j=max_fd;j>0;j--)
						{
							if(FD_ISSET(j,&rfds))
							{
								max_fd=j;
								break;
							}
						}
					}
					else
					{
						printf("[%s:%d] %s\n",inet_ntoa(cin_arr[i].sin_addr),\
								ntohs(cin_arr[i].sin_port),buf);
					}
				}
			}
		}
	}
	close(sfd);
	return 0;
}
int ser_init()
{
	//创建套接字
	int sfd=socket(AF_INET,SOCK_STREAM,0);
	if(sfd<0)
	{
		ERR("socket");
		return -1;
	}
	//设置端口允许重用
	int a=1;
	if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&a,sizeof(a))<0)
	{
		ERR("setsockopt");
		return -1;
	}
	//绑定服务器IP和端口号
	struct sockaddr_in sin={AF_INET,htons(SER_PORT),inet_addr(SER_IP)};
	if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))<0)
	{
		ERR("bind");
		return -1;
	}
	//将套接字设置为被动监听状态
	if(listen(sfd,128)<0)
	{
		ERR("listen");
		return -1;
	}
	return sfd;
}
//客户端
#include <stdio.h>
#include <netinet/ip.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/time.h>
#include <string.h>
#include <unistd.h>
#define ERR(msg) do{\
	fprintf(stderr,"%d--",__LINE__);\
	perror(msg);\
}while(0)
#define SER_PORT 7777
#define SER_IP "192.168.8.205"
int main(int argc, const char *argv[])
{
	//创建套接字
	int sfd=socket(AF_INET,SOCK_STREAM,0);
	if(sfd<0)
	{
		ERR("socket");
		return -1;
	}
	//获取服务器IP和端口号
	struct sockaddr_in sin={AF_INET,htons(SER_PORT),inet_addr(SER_IP)};
	//连接到服务器
	if(connect(sfd,(struct sockaddr*)&sin,sizeof(sin))<0)
	{
		ERR("connect");
		return -1;
	}
	printf("服务器连接成功\n");
	char buf[128]="";
	ssize_t res=0;
	//初始化读集合
	fd_set rfds;
	fd_set temp_fds;
	FD_ZERO(&rfds);
	FD_ZERO(&temp_fds);
	FD_SET(0,&rfds);
	FD_SET(sfd,&rfds);
	int max_fd=sfd;
	//开始IO多路复用
	while(1)
	{
		temp_fds=rfds;
		//判断是否有文件描述符就绪
		if(select(max_fd+1,&temp_fds,NULL,NULL,NULL)<0)
		{
			ERR("select");
			return -1;
		}
		if(FD_ISSET(0,&rfds))
		{
			printf("触发键盘输入\n");
			bzero(buf,sizeof(buf));
			fgets(buf,sizeof(buf),stdin);
			buf[strlen(buf)-1]=0;
			if(send(sfd,buf,sizeof(buf),0)<0)
			{
				ERR("send");
				return -1;
			}
			printf("发送成功\n");
		}
		if(FD_ISSET(sfd,&rfds))
		{
			printf("触发接收数据\n");
			bzero(buf,sizeof(buf));
			res=recv(sfd,buf,sizeof(buf),0);
			if(res<0)
			{
				ERR("recv");
				return -1;
			}
			else if(0==res)
			{
				printf("服务器断开连接\n");
				break;
			}
			printf("[%s:%d] %s\n",inet_ntoa(sin.sin_addr),\
					ntohs(sin.sin_port),buf);
		}
	}
	close(sfd);
	return 0;
}

完成poll的客户端

#include <stdio.h>
#include <poll.h>
#include <netinet/ip.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/time.h>
#include <string.h>
#include <unistd.h>
#define ERR(msg) do{\
	fprintf(stderr,"%d--",__LINE__);\
	perror(msg);\
}while(0)
#define SER_PORT 7777
#define SER_IP "192.168.8.205"
int main(int argc, const char *argv[])
{
	//创建套接字
	int sfd=socket(AF_INET,SOCK_STREAM,0);
	if(sfd<0)
	{
		ERR("socket");
		return -1;
	}
	//获取服务器IP和端口号
	struct sockaddr_in sin={AF_INET,htons(SER_PORT),inet_addr(SER_IP)};
	//连接到服务器
	if(connect(sfd,(struct sockaddr*)&sin,sizeof(sin))<0)
	{
		ERR("connect");
		return -1;
	}
	printf("服务器连接成功\n");
	char buf[128]="";
	ssize_t res=0;
	int pres=0;
	//初始化读集合
	struct pollfd pfd[2];
	pfd[0].fd=0;
	pfd[0].events=POLLIN;
	pfd[1].fd=sfd;
	pfd[1].events=POLLIN;
	//开始IO多路复用
	while(1)
	{
		//判断是否有文件描述符就绪
		pres=poll(pfd,2,-1);
		if(pres<0)
		{
			ERR("poll");
			return -1;
		}
		else if(pres==0)
		{
			printf("超时\n");
			break;
		}
		if(pfd[0].revents & POLLIN)
		{
			printf("触发键盘输入\n");
			bzero(buf,sizeof(buf));
			fgets(buf,sizeof(buf),stdin);
			buf[strlen(buf)-1]=0;
			if(send(sfd,buf,sizeof(buf),0)<0)
			{
				ERR("send");
				return -1;
			}
			printf("发送成功\n");
		}
		if(pfd[1].revents & POLLIN)
		{
			printf("触发接收数据\n");
			bzero(buf,sizeof(buf));
			res=recv(sfd,buf,sizeof(buf),0);
			if(res<0)
			{
				ERR("recv");
				return -1;
			}
			else if(0==res)
			{
				printf("服务器断开连接\n");
				break;
			}
			printf("[%s:%d] %s\n",inet_ntoa(sin.sin_addr),\
					ntohs(sin.sin_port),buf);
		}
	}
	close(sfd);
	return 0;
}
3. 项目:基于TCP的文件服务器(未完成)
项目需求:
1. 编写客户端和服务器
2. 客户端可以查看服务器端目录中的文件名 opendir readdir
3. 客户端可以从服务器中下载文件
4. 客户端可以上传文件给服务器
5. 多客户端同时在线
附加需求:
登录注册功能
报错信息比较少,登录时输入的是不存在的用户会直接进行注册的操作:(
//服务器
#include "inet.h"
#define SER_IP "192.168.8.205"
#define SER_PORT 7777


//服务器初始化
int ser_init();
//将目录下所有文件名放到一个文件中
int show_file(DIR *dp);
//接收客户端查看所有文件名的请求
int send_fname(int nfd);
//接收客户端下载文件的请求
int download(int nfd,char *buf);
//接收客户端上传文件的请求
int upload(int nfd,char *buf);

void *task(void *arg)//void *arg=(void *)&nfd
{
	int nfd=*(int *)arg;	
	FILE *fp=fopen("./usr.txt","a+");
	if(fp==NULL)
	{
		ERR("fopen");
		return NULL;
	}
	char name[10]="";
	char passwd[10]="";
	char fname[10]="";
	char fpasswd[10]="";
	int flag=0;

	char msg[128]="";
	msg[0]='1';
	char buf[512]="";
	ssize_t res=0;	
	//接收用户输入的账号和密码
	if(recv(nfd,name,sizeof(name),0)<0)
	{
		ERR("recv");
		return NULL;
	}
	if(recv(nfd,passwd,sizeof(passwd),0)<0)
	{
		ERR("recv");
		return NULL;
	}

	//判断该用户是否已注册
	while(1)
	{
		if(fscanf(fp,"%s %s",fname,fpasswd)==EOF)
		{
			if(errno==0)
			{
				break;
			}
			else
			{
				ERR("fscanf");
				return NULL;
			}
		}
		printf("name=%s fname=%s\n",name,fname);
		printf("passwd=%s fpasswd=%s\n",passwd,fpasswd);
		if(strcmp(fname,name)==0)//
		{
			flag=1;
			if(strcmp(fpasswd,passwd)==0)
			{
				strcpy(msg+1,"login success");
				//发送用户登陆成功的信息
				if(send(nfd,msg,sizeof(msg),0)<0)
				{
					ERR("send");
					return NULL;
				}
				while(1)
				{
					bzero(buf,sizeof(buf));
					res=recv(nfd,buf,sizeof(buf),0);
					if(res<0)
					{
						ERR("recv");
						return NULL;
					}
					else if(res==0)
					{
						printf("%d号客户端退出\n",nfd);
						break;
					}
					if(buf[0]=='1')//接收到查看文件请求
					{
						send_fname(nfd);
					}
					else if(buf[0]=='2')//接收到下载文件请求
					{
						download(nfd,buf);
					}
					else if(buf[0]=='3')//接收到上传文件请求
					{
						upload(nfd,buf);
					}
				}
			}
			else
			{
				strcpy(msg,"passwd error\nplease input again");
				//发送密码错误的信息
				if(send(nfd,msg,sizeof(msg),0)<0)
				{
					ERR("send");
					return NULL;
				}
				break;
			}
		}
	}
	if(flag==0)
	{
		strcpy(msg,"user does not exist\nplease register");
		//发送用户不存在的信息
		if(send(nfd,msg,sizeof(msg),0)<0)
		{
			ERR("send");
			return NULL;
		}
		bzero(msg,sizeof(msg));
		res=recv(nfd,msg,sizeof(msg),0);
		if(res<0)
		{
			ERR("recv");
			return NULL;
		}
		fprintf(fp,"%s\n",msg);
	}

	fclose(fp);
	close(nfd);
	pthread_exit(NULL);
}

int main(int argc, const char *argv[])
{
	int sfd=ser_init();
	printf("服务器就绪\n");
	//打开该服务器所在的目录
	DIR *dp=opendir("./");
	if(dp==NULL)
	{
		ERR("opendir");
		return -1;
	}
	show_file(dp);


	struct sockaddr_in cin;
	socklen_t addr=sizeof(cin);

	while(1)
	{
		//获取成功连接到服务器的客户端信息
		int nfd=accept(sfd,(struct sockaddr*)&cin,&addr);
		if(nfd<0)
		{
			ERR("accept");
			return -1;
		}
		printf("%d号客户端连接成功\n",nfd);

		//创建多线程
		pthread_t tid;
		pthread_create(&tid,NULL,task,(void *)&nfd);
		if(pthread_create<0)
		{
			ERR("pthread_create");
			return -1;
		}
		pthread_detach(tid);

	}
	close(sfd);
	closedir(dp);
	return 0;
}
int ser_init()
{
	//创建套接字
	int sfd=socket(AF_INET,SOCK_STREAM,0);
	if(sfd<0)
	{
		ERR("socket");
		return -1;
	}
	//设置端口允许重用
	int a=1;
	if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&a,sizeof(a))<0)
	{
		ERR("setsockopt");
		return -1;
	}
	//绑定服务器IP和端口号
	struct sockaddr_in sin={AF_INET,htons(SER_PORT),inet_addr(SER_IP)};
	if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))<0)
	{
		ERR("bind");
		return -1;
	}
	//设置套接字为被动监听状态
	if(listen(sfd,128)<0)
	{
		ERR("listen");
		return -1;
	}
	return sfd;
}
int show_file(DIR *dp)
{
	int fd=open("./show_file.txt",O_WRONLY|O_CREAT|O_TRUNC,0774);
	if(fd<0)
	{
		ERR("open");
		return -1;
	}
	char buf[512]="";
	while(1)
	{
		struct dirent *pa=readdir(dp);
		if(pa==NULL)
		{
			if(errno==0)
			{
				break;
			}
			else
			{
				ERR("readdir");
				return -1;
			}
		}
		if(*pa->d_name!='.')
		{
			int res=sprintf(buf,"%s\n",pa->d_name);
			if(write(fd,buf,res)<0)
			{
				ERR("write");
				return -1;
			}
		}
	}
	close(fd);
	return 0;
}
int send_fname(int nfd)
{
	char data[512]="";
	int fd=open("./show_file.txt",O_RDONLY);
	if(fd<0)
	{
		ERR("open");
		return -1;
	}
	if(read(fd,data,sizeof(data))<0)
	{
		ERR("read");
		return -1;
	}
	if(send(nfd,data,sizeof(data),0)<0)
	{
		ERR("send");
		return -1;
	}
	close(fd);
	return 0;
}
int download(int nfd,char *buf)
{
	char fname[20]="";
	char data[512]="";
	char msg[128]="";
	msg[0]='7';
	ssize_t read_res=0;
	ssize_t send_res=0;
	int flag=0;
	FILE *fp=fopen("./show_file.txt","r");
	if(fp==NULL)
	{
		ERR("fopen");
		return -1;
	}
	while(1)
	{
		if(fscanf(fp,"%s",fname)==EOF)
		{
			if(errno==0)
			{
				break;
			}
			else
			{
				ERR("fscanf");
				return -1;
			}
		}
		if(strcmp(buf+1,fname)==0)
		{
			flag=1;
			strcpy(msg,"filename correct");
			if(send(nfd,msg,sizeof(msg),0)<0)
			{
				ERR("send");
				return -1;
			}
			int fd=open(fname,O_RDONLY);
			if(fd<0)
			{
				ERR("open");
				return -1;
			}
			while(1)
			{
				read_res=read(fd,data,sizeof(data));
				if(read_res<0)
				{
					ERR("read");
					return -1;
				}
				send_res=send(nfd,data,read_res,0);
				if(send_res<0)
				{
					ERR("send");
					return -1;
				}
				else if(send_res<512)
				{
					break;
				}
			}
			close(fd);
			break;
		}
	}
	if(flag==0)
	{
		strcpy(msg+1,"filename error");
		if(send(nfd,msg,sizeof(msg),0)<0)
		{
			ERR("send");
			return -1;
		}
	}
	fclose(fp);
	return 0;
}
int upload(int nfd,char *buf)
{
	ssize_t res_recv=0;
	ssize_t res_wr=0;
	char data[512]="";
	int fd=open(buf+1,O_WRONLY|O_CREAT|O_TRUNC,0774);
	{
		if(fd<0)
		{
			ERR("open");
			return -1;
		}
	}
	while(1)
	{
		bzero(data,sizeof(data));
		res_recv=recv(nfd,data,sizeof(data),0);
		if(res_recv<0)
		{
			ERR("recv");
			return -1;
		}
		res_wr=write(fd,data,res_recv);
		if(res_wr<0)
		{
			ERR("write");
			return -1;
		}
		else if(res_wr<512)
		{
			break;
		}
	}
	close(fd);
	return 0;
}
//客户端
#include "inet.h"

#define SER_IP "192.168.8.205"
#define SER_PORT 7777

//查看服务器所在目录的所有文件
int show_file(int sfd);
//下载文件
int download(int sfd);
//上传文件
int upload(int sfd);

int main(int argc, const char *argv[])
{
	//创建套接字
	int sfd=socket(AF_INET,SOCK_STREAM,0);
	if(sfd<0)
	{
		ERR("socket");
		return -1;
	}
	//连接服务器
	struct sockaddr_in sin;
	sin.sin_family=AF_INET;
	sin.sin_port=htons(SER_PORT);
	sin.sin_addr.s_addr=inet_addr(SER_IP);
	if(connect(sfd,(struct sockaddr*)&sin,sizeof(sin))<0)
	{
		ERR("connect");
		return -1;
	}
	printf("服务器连接成功\n");
	ssize_t res=0;
	char buf[128]="";

	char name[10]="";
	char passwd[10]="";

	printf("请输入用户名:");
	scanf("%s",name);
	while(getchar()!=10);
	if(send(sfd,name,sizeof(name),0)<0)
	{
		ERR("send");
		return -1;
	}
	printf("请输入密码:");
	scanf("%s",passwd);
	while(getchar()!=10);
	if(send(sfd,passwd,sizeof(passwd),0)<0)
	{
		ERR("send");
		return -1;
	}

	//接收服务器发来的信息并进行相应操作
	if(recv(sfd,buf,sizeof(buf),0)<0)
	{
		ERR("recv");
		return -1;
	}
	if(buf[0]=='1')
	{
		printf("%s\n",buf+1);

		while(1)
		{
			printf("1---查看\n");
			printf("2---下载\n");
			printf("3---上传\n");
			printf("4---退出\n");
			printf("请输入:");
			char ch=getchar();
			while(getchar()!=10);
			switch(ch)
			{
			case '1':show_file(sfd);break;
			case '2':download(sfd);break;
			case '3':upload(sfd);break;
			case '4':goto END;break;
			default:printf("输入错误\n");
			}
			printf("请输入任意字符清屏:");
			while(getchar()!=10);
			system("clear");
		}

	}
	else
	{
		printf("%s\n",buf);
		bzero(name,sizeof(name));
		bzero(passwd,sizeof(passwd));
		printf("请输入用户名:");
		scanf("%s",name);
		while(getchar()!=10);
		printf("请输入密码:");
		scanf("%s",passwd);
		while(getchar()!=10);
		int size=sprintf(buf,"%s %s",name,passwd);
		if(send(sfd,buf,size,0)<0)
		{
			ERR("send");
			return -1;
		}
		printf("注册成功\n");
	}

END:
	//关闭文件描述符
	close(sfd);
	return 0;
}
int show_file(int sfd)
{
	char buf[512]="";
	buf[0]='1';
	if(send(sfd,buf,1,0)<0)
	{
		ERR("sendto");
		return -1;
	}
	if(recv(sfd,buf,sizeof(buf),0)<0)
	{
		ERR("recv");
		return -1;
	}
	printf("%s",buf);
	return 0;
}
int download(int sfd)
{
	ssize_t res_rd=0;
	ssize_t res_wr=0;
	char msg[128]="";
	char buf[512]="";
	buf[0]='2';
	printf("请输入要下载的文件:");
	scanf("%s",buf+1);
	while(getchar()!=10);
	if(send(sfd,buf,sizeof(buf),0)<0)
	{
		ERR("send");
		return -1;
	}
	if(recv(sfd,msg,sizeof(msg),0)<0)
	{
		ERR("recv");
		return -1;
	}
	if(msg[0]!='7')
	{
		printf("%s\n",msg);
		int fd=open(buf+1,O_WRONLY|O_CREAT|O_TRUNC,0774);
		{
			if(fd<0)
			{
				ERR("open");
				return -1;
			}
		}
		while(1)
		{
			bzero(buf,sizeof(buf));
			res_rd=recv(sfd,buf,sizeof(buf),0);
			if(res_rd<0)
			{
				ERR("recv");
				return -1;
			}
			res_wr=write(fd,buf,res_rd);
			if(res_wr<0)
			{
				ERR("write");
				return -1;
			}
			else if(res_wr<512)
			{
				break;
			}
		}
		close(fd);
	}
	else
	{
		printf("%s\n",msg+1);
		printf("请重新输入文件名\n");
	}
}
int upload(int sfd)
{
	ssize_t res_rd=0;
	ssize_t res_send=0;
	char buf[512]="";
	buf[0]='3';
	printf("请输入要上传的文件:");
	scanf("%s",buf+1);
	//buf[strlen(buf)-1]=0;
	while(getchar()!=10);
	if(send(sfd,buf,sizeof(buf),0)<0)
	{
		ERR("send");
		return -1;
	}
	int fd=open(buf+1,O_RDONLY);
	{
		if(fd<0)
		{
			ERR("open");
			return -1;
		}
	}
	while(1)
	{
		bzero(buf,sizeof(buf));
		res_rd=read(fd,buf,sizeof(buf));
		if(res_rd<0)
		{
			ERR("read");
			return -1;
		}
		res_send=send(sfd,buf,res_rd,0);
		if(res_send<0)
		{
			ERR("send");
			return -1;
		}
		else if(res_send<512)
		{
			break;
		}
	}
	close(fd);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值