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

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

poll函数应用:

服务器部分代码:

cs 复制代码
//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;
}

客户端代码部分:

cs 复制代码
//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函数部分:

代码效果:

服务器部分:

cs 复制代码
#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;
}

客户端部分:

cs 复制代码
//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;
}
相关推荐
星野川崎2065 小时前
电商多店运维:云机24小时挂机频繁掉线、账号无故风控深度原因分析及解决方案
大数据·运维·服务器·云计算·电商
Logintern095 小时前
网络通讯协议—TCP/IP协议
网络·网络协议·tcp/ip
RisunJan6 小时前
Linux命令-xauth(X11 认证授权管理)
linux·服务器·microsoft
熊IT7 小时前
什么是原生住宅 IP?如何判断一个 IP 是否真正“原生”?
服务器·网络·tcp/ip
不灭的程序员阿澄7 小时前
在飞牛 NAS 上用 Docker 运行 DeepSeek Harness
运维·docker·容器
Elastic 中国社区官方博客7 小时前
让大模型思考,让小模型执行:在 Elastic Workflows 中拆分 LLM 成本
大数据·运维·数据库·人工智能·elasticsearch·ai
爱学习的小白柏8 小时前
【AI问数技术】多Agent协同架构:查询规划/SQL生成/洞察分析/报告生成
java·网络·人工智能·windows·sql·架构·llama
艾伦_耶格宇8 小时前
【AI】-4 OpenCode Go 接入 Obsidian 完整指南
运维·开发语言·人工智能·agent·opencode
SXkehuirongsheng8 小时前
APP开发定制和模板开发哪个更实用?
大数据·运维·人工智能
fb_123459 小时前
Linux三剑客超全精讲(grep+sed+awk)零基础入门|正则+实战面试题
linux·运维·服务器