基于libevent实现的tcp服务端

编写一个基于libevent实现的tcp服务端:
1 创建socket,获得lfd--socket();

2 设置端口复用:

int opt=1;

setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));

3 绑定--bind();

4 监听--listen(); 

5 创建地基--struct event_base*base=event_base_new();

6 创建lfd对应的事件节点

struct event *ev=event_new(base,lfd,EV_READ | EV_PERSIST,conncb,(void *)base); //持续监听lfd的读事件。若有事件发生,则调用conncb回调函数。

7 将lfd对应的事件节点上地基

event_add(base,NULL);

8 进入事件循环--event_base_dispatch(base);

9 释放资源

event_base_free(base);

event_base_free(ev);

10回调函数:

void conncb(evutil_socket_t fd, short events, void *arg)

{//监听文件描述符的回调函数

struct event_base*base=(struct event_base*)arg//获得地基

//接收新的连接

int cfd=accept(lfd,NULL,NULL);

if(cfd<0)

{

}

struct event *ev=event_new(base,cfd,EV_READ | EV_PERSIST,read,(void *)ev);

}

void readcb(evutil_socket_t fd, short events, void *arg)

{

struct event *ev=(struct event *)arg//获得此通信文件描述符的事件

n=read(fd,buf,sizeof(buf));

if(n<=0)

{

close(fd);

//从base地基上删除此事件(事件从未决态变非未决态)

event_del(ev);

event_free(ev);

}

write(fd,buf,strlen(buf));

}

完整代码实现:
 

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

struct event *conev=NULL;
void readcb(evutil_socket_t fd, short event, void *arg)
{
//通信文件描述符的事件的回调函数
	char buf[128];
	int i;
	memset(buf,0x00,sizeof(buf));
	int n=read(fd,buf,sizeof(buf));
	if(n<=0)
	{
		printf("read error or client close\n");
		close(fd);//关掉文件描述符
		event_del(conev);//下树
	}
	else
	{
		for(i=0;i<n;i++)
		{
			buf[i]=toupper(buf[i]);
		}
		write(fd,buf,n);
	}
}
void conncb(evutil_socket_t fd, short event, void *arg)
{
//监听文件描述符的事件的回调函数
	struct event_base*base=(struct event_base*)arg;//强转类型,获得地基
	int cfd=accept(fd,NULL,NULL);//获取新的连接
	if(cfd>0)
	{
		conev=event_new(base,cfd,EV_READ | EV_PERSIST,readcb,NULL);//创建一个新的事件节点
		if(conev==NULL)
		{
			event_base_loopexit(base,NULL);//退出event_base_dispatch循环
		}
		event_add(conev,NULL);//上地基
	}
}
int main()
{
	int lfd=socket(AF_INET,SOCK_STREAM,0);
	if(lfd<0)
	{
		perror("socket error");
		return -1;
	}
//设置端口复用
	int opt=1;
	setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(int));
	struct sockaddr_in sev;
	bzero(&sev,sizeof(sev));
	sev.sin_family=AF_INET;
	sev.sin_port=htons(8888);
	inet_pton(AF_INET,"192.168.230.130",&sev.sin_addr.s_addr);
	int ret=bind(lfd,(struct sockaddr *)&sev,sizeof(sev));//绑定
	if(ret<0)
	{
		perror("bind error");
		return -1;
	}
	ret=listen(lfd,128);//设置监听
	if(ret<0)
	{
		perror("listen error");
		return -1;
	}
	//struct event_base *event_base_new(void);
	struct event_base*base=event_base_new();创建地基
	if(base==NULL)
	{
		printf("new error\n");
		return -1;
	}
	//struct event *event_new(struct event_base *, evutil_socket_t, short, event_callback_fn, void *);
//创建一个lfd的事件节点
	struct event *ev=event_new(base,lfd,EV_READ | EV_PERSIST,conncb,(void *)base);
	if(ev==NULL)
	{
		printf("event_new error\n");
		return -1;
	}
	//int event_add(struct event *ev, const struct timeval *timeout);
	event_add(ev,NULL);//将此事件节点上树,事件从非未决态变未决态
	//int event_base_dispatch(struct event_base *);
	event_base_dispatch(base);//进入循环,等待事件由未决态变激发态,并执行事件的回调函数
	//int event_del(struct event *);
	//void event_free(struct event *);
	event_free(ev);//释放lfd的事件节点
	event_base_free(base);//释放地基
	close(lfd);//关掉监听文件描述符
	return 0;
}

缺点:我们使用了struct event *conev=NULL;,用全局变量conev来保存通信文件描述符的事件,但是如果有多个客户端连接时,conev只保存了最后一个通信文件描述符的事件,所以当我们执行event_del(conev);语句时,只是将最后一个通信文件描述符的事件下地基,让最后一个通信文件描述符无法通信。

解决方法:

创建一个结构体数组,让每一个通信文件描述符的事件有不同的内存空间。

struct f_event

{

int num

int fd;

struct event *conev;

};

改进代码:

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

struct f_event//定义一个结构体
{
int num;
	int fd;
	struct event*conev;
};
struct f_event evs[128];
void readcb(evutil_socket_t fd, short event, void *arg)
{
	struct f_event *nowev=(struct f_event*)arg;//强转类型,并用指针接收,可以修改fd的值
	char buf[128];
	int i;
	memset(buf,0x00,sizeof(buf));
	int n=read(fd,buf,sizeof(buf));
	if(n<=0)
	{

		printf("read error or client close\n");
		close(nowev->fd);
		event_del(nowev->conev);
		nowev->fd=-1;//表面该位置空闲
	}
	else
	{
		printf("[%d],n==[%d],buf==[%s]\n",nowev->num,n,buf);
		for(i=0;i<n;i++)
		{
			buf[i]=toupper(buf[i]);
		}
		write(fd,buf,n);
	}
}
void conncb(evutil_socket_t fd, short event, void *arg)
{
	int i=0;
	struct event_base*base=(struct event_base*)arg;
	int cfd=accept(fd,NULL,NULL);
	if(cfd>0)
	{
		for(i=0;i<128;i++)
		{
			if(evs[i].fd==-1)
			{
				evs[i].num=i;
				evs[i].fd=cfd;
				evs[i].conev=event_new(base,cfd,EV_READ | EV_PERSIST,readcb,(void*)&evs[i]);//直接把结构体作为参数传入
				if(evs[i].conev==NULL)
				{
					event_base_loopexit(base,NULL);
				}
				else
				{
					event_add(evs[i].conev,NULL);
					break;//超级重要,不然会一直循环,让i==128
				}
			}
		}
		if(i==128)
		{
			printf("there is no enough space\n");
			close(cfd);
		}	
	}
}
int main()
{
	int j=0;
	for(j=0;j<128;j++)
	{
		evs[j].fd=-1;//初始化,fd为-1表示空闲
	}
	int lfd=socket(AF_INET,SOCK_STREAM,0);
	if(lfd<0)
	{
		perror("socket error");
		return -1;
	}
	int opt=1;
	setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(int));
	struct sockaddr_in sev;
	bzero(&sev,sizeof(sev));
	sev.sin_family=AF_INET;
	sev.sin_port=htons(8888);
	inet_pton(AF_INET,"192.168.230.130",&sev.sin_addr.s_addr);
	int ret=bind(lfd,(struct sockaddr *)&sev,sizeof(sev));
	if(ret<0)
	{
		perror("bind error");
		return -1;
	}
	ret=listen(lfd,128);
	if(ret<0)
	{
		perror("listen error");
		return -1;
	}
	//struct event_base *event_base_new(void);
	struct event_base*base=event_base_new();
	if(base==NULL)
	{
		printf("new error\n");
		return -1;
	}
	//struct event *event_new(struct event_base *, evutil_socket_t, short, event_callback_fn, void *);
	struct event *ev=event_new(base,lfd,EV_READ | EV_PERSIST,conncb,(void *)base);
	if(ev==NULL)
	{
		printf("event_new error\n");
		return -1;
	}
	//int event_add(struct event *ev, const struct timeval *timeout);
	event_add(ev,NULL);
	//int event_base_dispatch(struct event_base *);
	event_base_dispatch(base);
	//int event_del(struct event *);
	//void event_free(struct event *);
	event_free(ev);
	event_base_free(base);
	close(lfd);
	return 0;
}

可以 看到关掉第一个通信文件描述符后,最后一个通信文件描述符也可以正常通信。

相关推荐

  1. 基于libevent使用c语言实现http服务基础框架

    2024-03-16 09:30:01       44 阅读
  2. nettyTCP服务和客户实现

    2024-03-16 09:30:01       27 阅读
  3. Python中TCP服务器与客户简易实现

    2024-03-16 09:30:01       12 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-03-16 09:30:01       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-16 09:30:01       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-16 09:30:01       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-16 09:30:01       20 阅读

热门阅读

  1. Springboot如何判断pom.xml中是否加载了某个jar依赖

    2024-03-16 09:30:01       19 阅读
  2. C#简单聊天服务器程序

    2024-03-16 09:30:01       19 阅读
  3. 什么是智能合约,如何熟悉智能合约

    2024-03-16 09:30:01       21 阅读
  4. 【gpt实践】50个提升工作效率的GPT指令

    2024-03-16 09:30:01       18 阅读