上一篇博客虽然也写了服务器端的程序,跟着别人的代码写的,没怎么搞懂,之后又看了几篇博客,把别人的代码跑了跑感受了一下,然后自己重新写了一遍,实现了客户端与服务器的通信。libevent上篇介绍了,就不重复了,下面介绍单线程的客户端与服务器端。
要实现网络通信,肯定会用到socket等函数,这几个函数应该没什么问题。libevent默认情况下是单线程的,可以配置成多线程,每个线程有一个event_base,对应一个struct event _base结构体以及一个事件管理器,调度托管给它的一系列事件。所以当一个事件发生后,先创建一个event_base,再创建一个事件,将这个事件绑定,然后添加到event_base中,启动event_base的循环,开始处理事件。大概流程如下:
1 参数解析;
2. 创建socket连接;
3. struct event_base *base = event_base_new();
创建一个event_base;
event_base_new()函数分配并且返回一个新的具有默认设置的 event_base。函数会检测环境变量,返回一个到 event_base 的指针。如果发生错误,则返回 NULL。选择各种方法时,函数会选择 OS 支持的最快方法。
4struct event* listen_event;
创建一个监听事件;
5event_new(base,listenfd,EV_READ | EV_PERSIST,accept_cb,base);
使用event_new()函数将监听事件绑定;
参数:event_base监听的对象,需要监听的事件,事件发生后的回调函数,传给回调函数的参数。libevent支持的事件及属性包括(使用bitfield实现)
EV_TIMEOUT:超时;
EV_READ:只要网络缓冲中还有数据,回调函数就会被触发;
EV_WRITE:只要塞给网络缓冲的数据被写完,回调函数就会被触发;
EV_SIGNAL:POSIX信号量;
EV_PERSIST:不指定这个属性,回调函数被触发后事件会被删除;
EV_ET:Edge-Trigger边缘触发
6event_add(listen_event,NULL);
将监听事件绑定到event_base中;
7event_base_dispatch(base);
启动循环开始处理事件;
8事件发生时的回调函数typedef void(* event_callback_fn)(evutil_socket_t sockfd, short event_type, void *arg)
传给callback_func的是一个监听的事件类型fd,以及event_new中最后一个参数。
如下是代码:
int main(int argc,char **argv)
{
int listenfd;
int ch;
int port;
struct option opt[]={
{"port",required_argument,NULL,'p'},
{"help",no_argument,NULL,'h'},
{NULL,0,NULL,0}
};
while( (ch=getopt_long(argc,argv,"p:h",opt,NULL))!=-1 )
{
switch(ch)
{
case 'p':
port=atoi(optarg);
break;
case 'h':
print_help(argv[0]);
return 0;
}
}
printf("port:%d\n",port);
if( !port )
{
print_help(argv[0]);
return 0;
}
listenfd=socket_init(NULL,port);
if( listenfd<0 )
{
printf("socket_init failure!\n");
return -1;
}
printf("socket_init successfully!\n");
/*创建一个event_base*/
struct event_base *base = event_base_new();
assert(base != NULL);//设置event_base不为空
/*创建并绑定一个event*/
struct event* listen_event;
listen_event=event_new(base,listenfd,EV_READ | EV_PERSIST,accept_cb,base);
/*添加监听事件*/
event_add(listen_event,NULL);
/*启动循环,开始处理事件*/
event_base_dispatch(base);
return 0;
}
/*回调函数accept_cb*/
void accept_cb(int fd, short events, void* arg)
{
struct sockaddr_in cliaddr;
evutil_socket_t clifd;
socklen_t len=sizeof(cliaddr);
clifd=accept(fd,(struct sockaddr*)&cliaddr,&len);
if( clifd<0 )
{
printf("accept client %d failure!\n",clifd);
close(clifd);
}
evutil_make_socket_nonblocking(clifd);//设置为非阻塞模式
printf("accept client %d successfully!\n",clifd);
struct event_base* base = (struct event_base*)arg;
/*动态创建一个event结构体,并将其作为回调参数传递给*/
struct event* cli_event = event_new(NULL, -1, 0, NULL, NULL);
event_assign(cli_event,base, clifd, EV_READ | EV_PERSIST,read_cb, (void*)cli_event);
event_add(cli_event,NULL);
}
/*accept的回调函数read_cb*/
void read_cb(int fd, short events, void* arg)
{
int rv;
char buf[1024];
struct event* ev = (struct event*)arg;
rv=read(fd,buf,sizeof(buf));
if( rv<=0 )
{
printf("read message failure!\n");
event_free(ev);
close(fd);
return ;
}
printf("read message successfully:%s\n",buf);
char reply_buf[1024] = "I have received the msg: ";
strcat(reply_buf + strlen(reply_buf), buf);
write(fd,reply_buf,sizeof(reply_buf));
}
客户端的实现与服务器端基本相似,先实现socket的连接,然后创建event_base,创建并绑定监听事件,添加监听事件,启动循环,开始处理事件。代码如下:
int main(int argc, char** argv)
{
int ch;
int sockfd;
char *server_ip=NULL;
int server_port=0;
struct option opt[]={
{"server_ip",required_argument,NULL,'i'},
{"server_port",required_argument,NULL,'p'},
{"help",no_argument,NULL,'h'},
{NULL,0,NULL,0}
};
while( (ch=getopt_long(argc,argv,"i:p:h",opt,NULL))!=-1 )
{
switch(ch)
{
case 'i':
server_ip=optarg;
break;
case 'p':
server_port=atoi(optarg);
break;
case 'h':
print_help(argv[0]);
return 0;
}
}
if(!server_port)
{
print_help(argv[0]);
return 0;
}
sockfd=socket_connect(server_ip,server_port);
if( sockfd<0 )
{
printf("connect to server failure!\n");
return -1;
}
printf("connect to server successfully!\n");
struct event_base* base = event_base_new();
struct event *sockevent = event_new(base, sockfd, EV_READ | EV_PERSIST, read_cb, NULL);
event_add(sockevent, NULL);
//监听终端输入事件
struct event* ev_input = event_new(base, STDIN_FILENO, EV_READ | EV_PERSIST, input_cb, (void*)&sockfd);
event_add(ev_input, NULL);
event_base_dispatch(base);
printf("finished \n");
return 0;
}
/*读回调函数*/
void read_cb(int fd, short events, void *arg)
{
char buf[1024];
int rv;
rv=read(fd,buf,sizeof(buf));
if( rv<=0 )
{
printf("read data from server %dfailure!\n",fd);
exit(1);
}
printf("read %d data from server:%s\n",rv,buf);
}
/*输入信息回调函数*/
void input_cb(int fd, short events, void* arg)
{
char buf[1024];
int rv;
rv=read(fd,buf,sizeof(buf));
if( rv<=0 )
{
printf("read failure!\n");
exit(1);
}
//把终端的消息发送给服务器端
int sockfd = *((int*)arg);
write(sockfd,buf,rv);
}