Bootstrap

网络编程并发服务器的应用

作业2:完成局域网CS模型,局域网内一个服务器,多个客户端连接一个服务器,完成局域网聊天(select函数,poll函数,完成TCP并发服务器)。

poll函数应用:

服务器部分代码:

//TCP服务器
#include<myhead.h> 
#include<poll.h>
#define IP "192.168.60.68"
#define PORT 1028
#define BACKLOG 20
int main(int argc, const char *argv[])
{
	//AF_INET:IPv4通信
	int oldfd=socket(AF_INET,SOCK_STREAM,0);
	if(oldfd==-1)
	{
		perror("socket");
		return -1;
	}
	int n=1;
	int res=setsockopt(oldfd,SOL_SOCKET,SO_REUSEADDR,&n,sizeof(n));
	if(res==0)
	{
		printf("端口号快速复用成功\n");
	}
	//2、绑定IP和端口号
	struct sockaddr_in server={
	.sin_family=AF_INET,//IPv4
	.sin_port=htons(PORT),//端口号转为网络字节序
	.sin_addr.s_addr=inet_addr(IP),//IP地址
	};
	
	if(bind(oldfd,(struct sockaddr *)&server,sizeof(server))==-1)
	{
		perror("bind");
		return -1;
	}
	//3、监听
	if(listen(oldfd,BACKLOG)==-1)
	{
		perror("listen");
		return -1;
	}
	//4、接受客户端连接请求创建新的描述符用于通信
	//1-1设置结构体数组检测描述符发生事件
	struct pollfd fds[100];
	fds[0].fd=oldfd;//旧的套接字描述符检测数组中
	fds[0].events=POLLIN;
	int i;
	struct sockaddr_in client;
	socklen_t client_len=sizeof(client);
	int newfd;
	int count=1;
	while(1)
	{
		int res=poll(fds,100,-1);
		if(res==-1)
		{
			perror("poll");
		}
		if(res==0)
		{
			printf("超时\n");
		}
		if(fds[0].revents==POLLIN)
		{
			newfd=accept(oldfd,(struct sockaddr *)&client,&client_len);
			if(newfd==-1)
			{
				perror("accept");
				return -1;
			}
				printf("%s发来连接请求\n",inet_ntoa(client.sin_addr));
			fds[count].fd=newfd;
			fds[count++].events=POLLIN;
		}
		for(i=1;i<100;i++)
		{
			if(fds[i].fd>0&&fds[i].revents==POLLIN)
			{
				char buff[1024];
				memset(buff,0,sizeof(buff));
				int len=recv(fds[i].fd,buff,sizeof(buff),0);
				if(len==0)
				{
					printf("客户端下线\n");
					close(fds[i].fd);
					break;
				}
				printf("%s\n",buff);
				strcat(buff,"今天是周一");
				send(fds[i].fd,buff,sizeof(buff),0);
			}
		}
	}
	close(oldfd);
	return 0;
}

客户端代码部分:

//TCP客户端
#include<myhead.h> 
#define  IP "192.168.60.68"
#define PORT  1028
int main(int argc, const char *argv[])
{
	//1、创建套接字
	int oldfd=socket(AF_INET,SOCK_STREAM,0);
	if(oldfd==-1)
	{
		perror("socket");
		return -1;
	}

	struct sockaddr_in server={
	.sin_family=AF_INET,
	.sin_port = htons(PORT),
	.sin_addr.s_addr =inet_addr(IP),
	};

	if(connect(oldfd,(struct sockaddr *)&server,sizeof(server))==-1)
	{
		perror("connect");
		return -1;
	}

	char buff[1024];
	while(1)
	{
		fgets(buff,sizeof(buff),stdin);
		send(oldfd,buff,sizeof(buff),0);
		int len=recv(oldfd,buff,sizeof(buff),0);
		if(len==0)
		{
			perror("服务意外退出\n");
			break;
		}
		printf("接受服务器消息:%s\n",buff);
	}
	close(oldfd);
	return 0;
}

效果演示:

selsect函数部分:

代码效果:

服务器部分:

#include <myhead.h>
#define IP "192.168.60.68"
#define PORT 1028
#define BACKLOG 20

int main(int argc, const char *argv[])
{
	//AF_INET:IPV4通信
	//SOCK_STREAM:TCP通信协议
	int oldfd = socket(AF_INET,SOCK_STREAM,0);//1、创建套接字
	if(oldfd==-1)
	{
		perror("socket");
		return -1;
	}
	
	//2、绑定IP和端口号
	struct sockaddr_in server = {
	.sin_family = AF_INET,//IPV4
	.sin_port = htons(PORT),//端口号转为网络字节序
	.sin_addr.s_addr = inet_addr(IP),//IP地址
	};
	if(bind(oldfd,(struct sockaddr *)&server,sizeof(server))==-1)
	{
		perror("bind");
		return -1;
	}
	
	//3、监听
	if(listen(oldfd,BACKLOG)==-1)
	{
		perror("listen");
		return -1;
	}
	//4、接受客户端连接请求创建新的描述符用于通信
	struct sockaddr_in client;//接收客户端信息的结构体
	socklen_t client_len = sizeof(client);//计算出结构体大小
	//1-1定义容器放入描述符
	fd_set readfds,temp;//定义两个集合存放描述符
	FD_ZERO(&readfds);//清空集合内的内容
	FD_SET(0,&readfds);
	FD_SET(oldfd,&readfds);//描述符放入集合
	int maxfd = oldfd;//保留最大的描述符

	int newfd;
	
	while(1)//循环连入多个客户端
	{
		
		//1-2监视所有的描述符
		temp = readfds;
		int res = select(maxfd+1,&temp,NULL,NULL,NULL);//永久阻塞
		if(res==-1)
		{
			perror("select");
			return -1;
		}
		if(res==0)
		{
			printf("超时\n");
			continue;
		}
		//程序执行到此,说明某些描述符解除了阻塞	
		//1-3循环检测是谁发生了IO操作 
		int i;
		for(i = 0;i<=maxfd;i++)
		{
			if(!FD_ISSET(i,&temp))//如果描述符不在容器中,继续下次循环查找
			{
				continue;
			}
			if(i==oldfd)//旧的描述符解除的阻塞
			{
				newfd = accept(i,(struct sockaddr *)&client,&client_len);
				FD_SET(newfd,&readfds);//产生新描述符代表新客户端,需要放入集合
				maxfd = newfd>maxfd?newfd:maxfd;//更新最大的描述符
				if(newfd==-1)
				{
					perror("accept");
					return -1;
				}
				printf("%s发来连接请求\n",inet_ntoa(client.sin_addr));
			}	
			else//0号描述符解除的阻塞,进行通话
			{
				//5、循环收发信息
				char buff[1024];
				memset(buff,0,sizeof(buff));
				int len = recv(i,buff,sizeof(buff),0);//0:阻塞发送 MSG_DONTWAIT:非阻塞
				if(len==0)
				{
					close(i);//关闭新的描述符
					FD_CLR(i,&readfds);//移除退出的客户端描述符
					if(maxfd==i)//退出后,描述符个数减一
					{
						maxfd--;
					}
					printf("客户端下线\n");
					break;
				}
				printf("%s\n",buff);
				fgets(buff,sizeof(buff),stdin);
				send(i,buff,sizeof(buff),0);
			}
		}
	}
	close(oldfd);
	return 0;
}

客户端部分:

//TCP客户端
#include<myhead.h> 
#define  IP "192.168.60.68"
#define PORT  1028
int main(int argc, const char *argv[])
{
	//1、创建套接字
	int oldfd=socket(AF_INET,SOCK_STREAM,0);
	if(oldfd==-1)
	{
		perror("socket");
		return -1;
	}

	struct sockaddr_in server={
	.sin_family=AF_INET,
	.sin_port = htons(PORT),
	.sin_addr.s_addr =inet_addr(IP),
	};

	if(connect(oldfd,(struct sockaddr *)&server,sizeof(server))==-1)
	{
		perror("connect");
		return -1;
	}

	char buff[1024];
	while(1)
	{
		fgets(buff,sizeof(buff),stdin);
		send(oldfd,buff,sizeof(buff),0);
		int len=recv(oldfd,buff,sizeof(buff),0);
		if(len==0)
		{
			perror("服务意外退出\n");
			break;
		}
		printf("接受服务器消息:%s\n",buff);
	}
	close(oldfd);
	return 0;
}

;