WebServer服务器(一)

这篇文章记录了从创建一个简单的C/S模型,仅支持一个客户端的服务器,到使用epoll实现可以处理多个客户端连接的服务器的开发过程。通过epoll函数,服务器能够以非阻塞的方式处理客户端的连接请求和数据传输,提升了并发性能。
摘要由CSDN通过智能技术生成

记录一下自己对服务器开发学习的过程

1、server1.0。做了一个最简单的c/s模型,仅支持一个客户端

代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>
#include<unistd.h>
#include<netinet/in.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>

int main(int argc,char* argv[])
{
	printf("服务器已启动成功!------------");
	
	if(argc<3)
	{
		printf("Usage: %s ip_address portname\n",argv[0]);
		return 0;
	}
	
	const char* ip = argv[1];
	//atoi将字符串nptr转换为int
	int port = atoi(argv[2]);

	int listenfd = socket(AF_INET,SOCK_STREAM,0);
	assert(listenfd >= 1);

	struct sockaddr_in serv_addr;
	memset(&serv_addr,0,sizeof(serv_addr));

	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(port);
	inet_pton(AF_INET,ip,&serv_addr.sin_addr);

	int ret = 0;
	ret = bind(listenfd,(struct sockaddr*)&serv_addr,sizeof(serv_addr));
	assert(ret!=-1);

	ret = listen(listenfd,10);
	assert(ret!=-1);
	
	struct sockaddr_in cli_addr;
	socklen_t cli_addrlen = sizeof(cli_addr);

	int cfd1 = accept(listenfd,(struct sockaddr*)&cli_addr,&cli_addrlen);

	//连接后的具体操作
	char buf[1024] = {0};
	int recv_size = 0;
	
	recv_size = recv(cfd1,buf,sizeof(buf),0);

	int send_size = 0;
	send_size = send(cfd1,buf,recv_size,0);

	close(cfd1);

	close(listenfd);
	printf("服务器已关闭!-------------");

	return 0;
}

运行过程:

2、server1.1。使用epoll函数实现了可以支持多个客户端的c/s模型

代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>
#include<unistd.h>
#include<netinet/in.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<sys/epoll.h>
#include<errno.h>
#include<fcntl.h>


#define MAX_EVENTS_NUMBER 10

void set_non_blocking(int fd)
{
	//fcntl函数加参数F_GETFL,获取文件状态标记
	int old_state = fcntl(fd,F_GETFL);
	//printf("old_state = [%d]\n",old_state);
	//ET模型只支持非阻塞模型
	int new_state = old_state| O_NONBLOCK;
	fcntl(fd,F_SETFL,new_state);
	//return old_state;
}


void addfd(int epollfd,int fd)
{
	//设置  监听属性
	struct epoll_event event;
    //边沿触发
	event.events = EPOLLIN|EPOLLET;
	event.data.fd = fd;
	//将文件描述符添加到监听树上
	epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&event);
	//通过函数设置阻塞方式
	set_non_blocking(fd);
}


int main(int argc,char* argv[])
{
	printf("服务器已启动成功!------------\n");
	
	if(argc<3)
	{
		printf("Usage: %s ip_address portname\n",argv[0]);
		return 0;
	}
	
	const char* ip = argv[1];
	//atoi将字符串nptr转换为int
	int port = atoi(argv[2]);

	int listenfd = socket(AF_INET,SOCK_STREAM,0);
	assert(listenfd >= 1);

	struct sockaddr_in serv_addr;
	memset(&serv_addr,0,sizeof(serv_addr));

	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(port);
	inet_pton(AF_INET,ip,&serv_addr.sin_addr);

	int ret = 0;
	ret = bind(listenfd,(struct sockaddr*)&serv_addr,sizeof(serv_addr));
	assert(ret!=-1);

	ret = listen(listenfd,10);
	assert(ret!=-1);
	
	//设置事件数组
	struct epoll_event events[MAX_EVENTS_NUMBER];
	//创建监听红黑树epollfid
	int epollfd = epoll_create(10);
	assert(epollfd != -1);
	
	//调用函数addfd,向红黑树中添加一个监听fd
	addfd(epollfd,listenfd);
	
	while(1)
	{
		//number为满足监听的事件个数
		int number = epoll_wait(epollfd,events,MAX_EVENTS_NUMBER,-1);
		//错误检查
		if(number < 0)
		{
			printf("epoll_wait failed\n");
			return -1;
		}

		//执行每个事件
		for(int i=0;i<number;i++)
		{
			struct epoll_event* event = &events[i];
			int eventfd = event->data.fd;
			
			//如果监听到的是lfd,则让lfd执行accept接收客户端信号
			if(eventfd == listenfd)
			{
				printf("监听到了lfd(有客户端进行了连接)------------\n");
				struct sockaddr_in cli_addr;
				socklen_t cli_addr_len = sizeof(cli_addr);
				int cfd = accept(listenfd,(struct sockaddr*)&cli_addr,&cli_addr_len);
				//将cfd添加到监听树上
				addfd(epollfd,cfd);
			}
			//如果当前事件的行为是读:EPOLLIN
			else if(event->events & EPOLLIN)
			{
				printf("开始进行读操作(EPOLLIN)\n");
				char buf[1024] = {0};
				while(1)
				{
					printf("当前cfd为[%d]------\n",eventfd);
					memset(buf,'\0',sizeof(buf));
					int recv_size = recv(eventfd,buf,sizeof(buf),0);
					if(recv_size<0)
					{
						if((errno==EAGAIN)||(errno ==EWOULDBLOCK))
						{
							break;
						}
						printf("cfd %d,recv msg failed\n",eventfd);
						close(eventfd);
						break;
					}
					else if(recv_size == 0)
					{
						close(eventfd);
						break;
					}
					else
					{
						printf("成功接受大小为[%d]的数据,即将发送数据",recv_size);
						send(eventfd,buf,recv_size,0);
					}
				}
			}
		}
	}

	close(listenfd);

	return 0;
}

运行过程:

 

 

参考链接:https://blog.csdn.net/qq_37500516/article/details/123486596

面试官:什么是 Reactor 和 Proactor?_reactor和proactor_小林coding的博客-CSDN博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值