Linux console聊天室之基于libevent
结果图
客户端实现
连接完成之后,新建一个线程负责循环接收消息并显示,主线程负责循环发送消息
服务器实现
- 创建套接字句柄,地址设为可复用,并且非阻塞
[1] 可复用:evutil_make_listen_socket_reuseable
参数:套接字
[2] 非阻塞:evutil_make_socket_nonblocking
参数:套接字 - 初始化事件集:event_base
[1] struct event_base* event_base_new()
返回值:event_base* base,事件集 - 事件初始化及注册: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
作用:激活已初始化的事件,否则不会触发 - 调度:int event_base_dispatch(struct event* base)
说明:实际上调用event_base_loop函数 - 回调函数:进行accept,接受来自客户端的连接请求,并另注册事件,实现通信,即”读-转发“实现聊天室
- 回调函数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;
}