Linux console聊天室之基于libevent

Linux console聊天室之基于libevent

结果图

客户端实现

连接完成之后,新建一个线程负责循环接收消息并显示,主线程负责循环发送消息


服务器实现

  1. 创建套接字句柄,地址设为可复用,并且非阻塞
    [1] 可复用:evutil_make_listen_socket_reuseable
    参数:套接字
    [2] 非阻塞:evutil_make_socket_nonblocking
    参数:套接字
  2. 初始化事件集:event_base
    [1] struct event_base* event_base_new()
    返回值:event_base* base,事件集
  3. 事件初始化及注册:event
    [1] struct event* event_new(struct event_base base, evutil_socket_t fd, short events,
    event_callback-fn cb, void
    arg)
    参数:事件集base
    套接字fd
    事件类型及属性:EV_TIMEOUT、EV_READ、EV_WRITE、EV_SIGNAL、EV_PERSIST、EV_ET
    回调函数:
    回调函数的参数:
    返回值:event* ev,已初始化的事件
    [2] void event_add(struct event* ev, const struct timeval* v)
    参数:已初始化的事件ev
    时间tv,可设为NULL
    作用:激活已初始化的事件,否则不会触发
  4. 调度:int event_base_dispatch(struct event* base)
    说明:实际上调用event_base_loop函数
  5. 回调函数:进行accept,接受来自客户端的连接请求,并另注册事件,实现通信,即”读-转发“实现聊天室
  6. 回调函数2:read,然后 for i:已连接客户端,非自身 转发
EV_TIMEOUT:定时事件,时间到了后激活执行
EV_READ:可读事件,可读时就绪
EV_WRITE:可写事件,可写时就绪
EV_SIGNAL:信号事件
EV_PERSIST:永久事件
EV_ET:边沿触发

代码

//服务器
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>

#include <string.h>//bzero 
#include <unistd.h>//read  write
#include <stdlib.h>

#include <event2/event.h>
#include <event2/event_struct.h>
#include <event2/bufferevent.h>
#include <event2/buffer.h>

int Socket(int );

//-----------------------------
void accept_cb(int fd,short events,void* arg);
void socket_read(int fd,short events,void* arg);
//-----------------------------

int fds[1024]={0};

int main(int argc,char** argv)
{
	int port=8888;
	if(argc==2) port=atoi(argv[1]);
	
	int s=Socket(port);
    

    struct event_base* base=event_base_new();
    struct event* ev_listen=event_new(base,s,EV_READ|EV_PERSIST,accept_cb,base);
    event_add(ev_listen,NULL);
    event_base_dispatch(base);
    
    return 0;
}


void accept_cb(int fd,short events,void* arg){
    struct sockaddr_in client;
    socklen_t len=sizeof(client);

    evutil_socket_t sockfd=::accept(fd,(struct sockaddr*)&client,&len);
    
    fds[sockfd]=1;
    std::cout<<sockfd<<" connect server \n";
    evutil_make_socket_nonblocking(sockfd);

    struct event_base *base=(event_base*)arg;
    struct event* ev=event_new(NULL,-1,0,NULL,NULL);
    event_assign(ev,base,sockfd,EV_READ|EV_PERSIST,socket_read,(void*)ev);
    event_add(ev,NULL);
}

void socket_read(int fd,short events,void* arg){
    char buf[1024];
    
    struct event* ev=(struct event*)arg;

    int len=read(fd,buf,sizeof(buf)-1);
    if(len<0){
        std::cout<<"Some error happen when server reads \n";
        event_free(ev);
        close(fd);
        fds[fd]=0;
        return;
    }
    else if(len==0){
        std::cout<<"Disconnect \n";
        event_free(ev);
        close(fd);
        fds[fd]=0;
        return;
    }
    buf[len]='\0';
    char newbuf[1024];
    strcpy(newbuf,std::to_string(fd).c_str());
    strcat(newbuf,": ");
    strcat(newbuf,buf);
    std::cout<<newbuf<<"\n\n";

    for(int i=0;i<1000;++i){
        if(fds[i]==1 && i!=fd){
            send(i,newbuf,strlen(newbuf),0);
        } 
    }
}




int Socket(int port){
    evutil_socket_t s;
    
    s=socket(AF_INET,SOCK_STREAM,0);
    
    evutil_make_listen_socket_reuseable(s);

    struct sockaddr_in addr;
    bzero(&addr,sizeof(addr));
    addr.sin_family=AF_INET;
    addr.sin_port=htons(port);
    addr.sin_addr.s_addr=htonl(INADDR_ANY);

    bind(s,(struct sockaddr*)&addr,sizeof(addr));
    listen(s,5);

    evutil_make_socket_nonblocking(s);

    return s;
}
//客户端
#include <iostream>

#include <unistd.h>
#include <stdlib.h>

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <strings.h>
#include <string.h>
#include <signal.h>

#include <fcntl.h>
#include <errno.h>
#include <stdio.h>

#include <thread>
using namespace std;

const unsigned short port=8888;

void sig_process(int signo){
    cout<<"\nCatch a exit signal:"<<signo<<"\n\n";
    exit(0);
}
/*
void sig_pipe(int signo){
    printf("Catch a SIGPIPE signal :%d \n",signo);
}
*/

int Socket(){
    int s=socket(AF_INET,SOCK_STREAM,0);
    fcntl(s,F_SETFL,O_NONBLOCK);
    return s;
}

void Init_addr(struct sockaddr_in& addr){
    addr.sin_family=AF_INET;
    addr.sin_port=htons(port);
    addr.sin_addr.s_addr=htonl(INADDR_ANY);
    inet_pton(AF_INET,"127.0.0.1",&addr.sin_addr);
}


void Recv(int s){
    char buf[1024];
    ssize_t sz;
    while(1){
    	sz=recv(s,buf,sizeof(buf),0);
        if(sz<=0) continue;
        buf[sz]='\0';
        cout<<buf<<"\n";
    }
    cout<<"Recv cp \n";
}

void Chat(int s){
    thread t(Recv,s);
    t.detach();
    
    char buf[1024];
    int len;
    while(1){
        cin.getline(buf,sizeof(buf));

        len=send(s,buf,strlen(buf),0);
        if(len==-1){
            cout<<"Error \n";
            break;
        }
    }
}

void Connect(int s,struct sockaddr_in& addr){
cout<<"Ready to connect\n";
    connect(s,(struct sockaddr*)&addr,sizeof(addr));
cout<<"Connect sc \n";
    Chat(s);
}

int main()
{
    signal(SIGINT,sig_process);
    int s=Socket();
    struct sockaddr_in addr;
    Init_addr(addr);
    Connect(s,addr);
    close(s);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值