libevent的两个服务端、客户端示例(C语言)
目录
示例1
server.c
client.c
makefile
示例2:使用BufferEvent
server.c
client.c
makefile
关于libevent的介绍,请参见《libevent:信号、超时、回调》
示例1
代码实现了一个简单的echo server,server启动后,client端启动并连接,在cmd中输入文字,server端收到后,将文字再返回给client。
server.c
/**
You need libevent2 to compile this piece of code
Please see: http://libevent.org/
Cmd to compile this piece of code: You need libevent2 to compile this piece of code
Please see: http://libevent.org/
Cmd to compile this piece of code: gcc client.c -levent -o client.out
gcc server.c -levent -o server.out
**/
#include<stdio.h>
#include<string.h>
#include<errno.h> #include<unistd.h>
#include<event.h>void accept_cb(int fd, short events, void* arg);
void socket_read_cb(int fd, short events, void* arg);int tcp_server_init(int port, int listen_num);int main(int argc, char const *argv[])
{/* code */int listener = tcp_server_init(9999, 10);if (listener == -1){perror("tcp_server_init error");return -1;}struct event_base* base = event_base_new();// 监听客户端请求链接事件struct event* ev_listen = event_new(base, listener, 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)
{evutil_socket_t sockfd;struct sockaddr_in client;socklen_t len = sizeof(client);sockfd = accept(fd, (struct sockaddr*)&client, &len);evutil_make_socket_nonblocking(sockfd);printf("accept a client %d\n", sockfd);struct event_base* base = (struct event_base*)arg;//动态创建一个event结构体,并将其作为回调参数传递给struct event* ev = event_new(NULL, -1, 0, NULL, NULL);event_assign(ev, base, sockfd, EV_READ | EV_PERSIST, socket_read_cb, (void*)ev);event_add(ev, NULL);
}void socket_read_cb(int fd, short events, void* arg)
{char msg[4096];struct event* ev = (struct event*)arg;int len = read(fd, msg, sizeof(msg) - 1);if(len <= 0){printf("some error happen when read\n");event_free(ev);close(fd);return;}msg[len] = '\0';printf("recv the client msg : %s\n", msg);char reply_msg[4096] = "I have received the msg: ";strcat(reply_msg + strlen(reply_msg), msg);write(fd, reply_msg, strlen(reply_msg));
}typedef struct sockaddr SA;
int tcp_server_init(int port, int listen_num)
{ int errno_save; evutil_socket_t listener; listener = socket(AF_INET, SOCK_STREAM, 0); if( listener == -1 ) return -1; //允许多次绑定同一个地址。要用在socket和bind之间 evutil_make_listen_socket_reuseable(listener); struct sockaddr_in sin; sin.sin_family = AF_INET; sin.sin_addr.s_addr = 0; sin.sin_port = htons(port); if( bind(listener, (SA*)&sin, sizeof(sin)) < 0 ) goto error; if( listen(listener, listen_num) < 0) goto error; //跨平台统一接口,将套接字设置为非阻塞状态 evutil_make_socket_nonblocking(listener); return listener; error: errno_save = errno; evutil_closesocket(listener); errno = errno_save; return -1;
}
client.c
/**
You need libevent2 to compile this piece of code
Please see: http://libevent.org/
Cmd to compile this piece of code: gcc client.c -levent -o client.out
gcc server.c -levent -o server.out**/
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<errno.h>
#include<unistd.h> #include<stdio.h>
#include<string.h>
#include<stdlib.h> #include<event.h>
#include<event2/util.h> int tcp_connect_server(const char* server_ip, int port);
void cmd_msg_cb(int fd, short events, void* arg);
void socket_read_cb(int fd, short events, void *arg); int main(int argc, char** argv)
{ if( argc < 3 ) { printf("please input 2 parameter\n"); return -1; } //两个参数依次是服务器端的IP地址、端口号 int sockfd = tcp_connect_server(argv[1], atoi(argv[2])); if( sockfd == -1) { perror("tcp_connect error "); return -1; } printf("connect to server successful\n"); struct event_base* base = event_base_new(); struct event *ev_sockfd = event_new(base, sockfd, EV_READ | EV_PERSIST, socket_read_cb, NULL); event_add(ev_sockfd, NULL); //监听终端输入事件 struct event* ev_cmd = event_new(base, STDIN_FILENO, EV_READ | EV_PERSIST, cmd_msg_cb, (void*)&sockfd); event_add(ev_cmd, NULL); event_base_dispatch(base); //调度printf("finished \n"); return 0;
} void cmd_msg_cb(int fd, short events, void* arg)
{ char msg[1024]; int ret = read(fd, msg, sizeof(msg)); if( ret <= 0 ) { perror("read fail "); exit(1); } int sockfd = *((int*)arg); //把终端的消息发送给服务器端 //为了简单起见,不考虑写一半数据的情况 write(sockfd, msg, ret);
} void socket_read_cb(int fd, short events, void *arg)
{ char msg[1024]; //为了简单起见,不考虑读一半数据的情况 int len = read(fd, msg, sizeof(msg)-1); if( len <= 0 ) { perror("read fail "); exit(1); } msg[len] = '\0'; printf("recv %s from server\n", msg);
} typedef struct sockaddr SA;
int tcp_connect_server(const char* server_ip, int port)
{ int sockfd, status, save_errno; struct sockaddr_in server_addr; memset(&server_addr, 0, sizeof(server_addr) ); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(port); status = inet_aton(server_ip, &server_addr.sin_addr); if( status == 0 ) //the server_ip is not valid value { errno = EINVAL; return -1; } sockfd = socket(PF_INET, SOCK_STREAM, 0); if( sockfd == -1 ) return sockfd; status = connect(sockfd, (SA*)&server_addr, sizeof(server_addr) ); if( status == -1 ) { save_errno = errno; close(sockfd); errno = save_errno; //the close may be error return -1; } evutil_make_socket_nonblocking(sockfd); return sockfd;
}
makefile
all:gcc client.c -levent -o client.outgcc server.c -levent -o server.out
示例2:使用BufferEvent
在上面的代码中,client的cmd中有信息输入时,client直接将数据写入到fd中,server中收到信息后,也是直接将信息写入到fd中,因为fd是非阻塞的,所以不能保证正确。那么需要一个自己管理的缓存来管理自己的数据。那么步骤将稍微有些变化,如下所示:
- 设置scokfd为nonblocking;
- 使用bufferevent_socket_new创建一个struct bufferevent* bev,关联上面的sockfd,并托管给event_base;
- 使用bufferevent_setcb(bev, read_cb, write_cb, error_cb, (void*)arg);
- 使用buffevent_enable(bev, EV_READ|EV_WRITE|EV_PERSIST)来启动read/write事件
server.c
/**
You need libevent2 to compile this piece of code
Please see: http://libevent.org/
Cmd to compile this piece of code: gcc client.c -levent -o client.out
gcc server.c -levent -o server.out**/#include<stdio.h>
#include<string.h>
#include<errno.h>#include<event.h>
#include<event2/bufferevent.h>void accept_cb(int fd, short events, void* arg);
void socket_read_cb(struct bufferevent* bev, void* arg);
void event_cb(struct bufferevent *bev, short event, void *arg);
int tcp_server_init(int port, int listen_num);int main(int argc, char** argv)
{int listener = tcp_server_init(9999, 10);if( listener == -1 ){perror(" tcp_server_init error ");return -1;}struct event_base* base = event_base_new();//添加监听客户端请求连接事件struct event* ev_listen = event_new(base, listener, EV_READ | EV_PERSIST,accept_cb, base);event_add(ev_listen, NULL);event_base_dispatch(base);event_base_free(base);return 0;
}void accept_cb(int fd, short events, void* arg)
{evutil_socket_t sockfd;struct sockaddr_in client;socklen_t len = sizeof(client);sockfd = accept(fd, (struct sockaddr*)&client, &len );evutil_make_socket_nonblocking(sockfd);printf("accept a client %d\n", sockfd);struct event_base* base = (struct event_base*)arg;struct bufferevent* bev = bufferevent_socket_new(base, sockfd, BEV_OPT_CLOSE_ON_FREE);bufferevent_setcb(bev, socket_read_cb, NULL, event_cb, arg);bufferevent_enable(bev, EV_READ | EV_PERSIST);
}void socket_read_cb(struct bufferevent* bev, void* arg)
{char msg[4096];size_t len = bufferevent_read(bev, msg, sizeof(msg));msg[len] = '\0';printf("recv the client msg: %s", msg);char reply_msg[4096] = "I have recvieced the msg: ";strcat(reply_msg + strlen(reply_msg), msg);bufferevent_write(bev, reply_msg, strlen(reply_msg));
}void event_cb(struct bufferevent *bev, short event, void *arg)
{if (event & BEV_EVENT_EOF)printf("connection closed\n");else if (event & BEV_EVENT_ERROR)printf("some other error\n");//这将自动close套接字和free读写缓冲区bufferevent_free(bev);
}typedef struct sockaddr SA;
int tcp_server_init(int port, int listen_num)
{int errno_save;evutil_socket_t listener;listener = socket(AF_INET, SOCK_STREAM, 0);if( listener == -1 )return -1;//允许多次绑定同一个地址。要用在socket和bind之间evutil_make_listen_socket_reuseable(listener);struct sockaddr_in sin;sin.sin_family = AF_INET;sin.sin_addr.s_addr = 0;sin.sin_port = htons(port);if( bind(listener, (SA*)&sin, sizeof(sin)) < 0 )goto error;if( listen(listener, listen_num) < 0)goto error;//跨平台统一接口,将套接字设置为非阻塞状态evutil_make_socket_nonblocking(listener);return listener;error:errno_save = errno;evutil_closesocket(listener);errno = errno_save;return -1;
}
client.c
/**
You need libevent2 to compile this piece of code
Please see: http://libevent.org/
Cmd to compile this piece of code: gcc client.c -levent -o client.out
gcc server.c -levent -o server.out**/
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<errno.h>
#include<unistd.h> #include<stdio.h>
#include<string.h>
#include<stdlib.h> #include<event.h>
#include<event2/bufferevent.h>
#include<event2/buffer.h>
#include<event2/util.h> int tcp_connect_server(const char* server_ip, int port);
void cmd_msg_cb(int fd, short events, void* arg);
void server_msg_cb(struct bufferevent* bev, void* arg);
void event_cb(struct bufferevent *bev, short event, void *arg); int main(int argc, char** argv)
{ if( argc < 3 ) { printf("please input 2 parameter\n"); return -1; } //两个参数依次是服务器端的IP地址、端口号 int sockfd = tcp_connect_server(argv[1], atoi(argv[2])); if( sockfd == -1) { perror("tcp_connect error "); return -1; } printf("connect to server successful\n"); struct event_base* base = event_base_new(); struct bufferevent* bev = bufferevent_socket_new(base, sockfd, BEV_OPT_CLOSE_ON_FREE); //监听终端输入事件 struct event* ev_cmd = event_new(base, STDIN_FILENO, EV_READ | EV_PERSIST, cmd_msg_cb, (void*)bev); event_add(ev_cmd, NULL); //当socket关闭时会用到回调参数 bufferevent_setcb(bev, server_msg_cb, NULL, event_cb, (void*)ev_cmd); bufferevent_enable(bev, EV_READ | EV_PERSIST); event_base_dispatch(base); printf("finished \n"); return 0;
} void cmd_msg_cb(int fd, short events, void* arg)
{ char msg[1024]; int ret = read(fd, msg, sizeof(msg)); if( ret < 0 ) { perror("read fail "); exit(1); } struct bufferevent* bev = (struct bufferevent*)arg; //把终端的消息发送给服务器端 bufferevent_write(bev, msg, ret);
} void server_msg_cb(struct bufferevent* bev, void* arg)
{ char msg[1024]; size_t len = bufferevent_read(bev, msg, sizeof(msg)); msg[len] = '\0'; printf("recv %s from server\n", msg);
} void event_cb(struct bufferevent *bev, short event, void *arg)
{ if (event & BEV_EVENT_EOF) printf("connection closed\n"); else if (event & BEV_EVENT_ERROR) printf("some other error\n"); //这将自动close套接字和free读写缓冲区 bufferevent_free(bev); struct event *ev = (struct event*)arg; //因为socket已经没有,所以这个event也没有存在的必要了 event_free(ev);
} typedef struct sockaddr SA;
int tcp_connect_server(const char* server_ip, int port)
{ int sockfd, status, save_errno; struct sockaddr_in server_addr; memset(&server_addr, 0, sizeof(server_addr) ); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(port); status = inet_aton(server_ip, &server_addr.sin_addr); if( status == 0 ) //the server_ip is not valid value { errno = EINVAL; return -1; } sockfd = socket(PF_INET, SOCK_STREAM, 0); if( sockfd == -1 ) return sockfd; status = connect(sockfd, (SA*)&server_addr, sizeof(server_addr) ); if( status == -1 ) { save_errno = errno; close(sockfd); errno = save_errno; //the close may be error return -1; } evutil_make_socket_nonblocking(sockfd); return sockfd;
}
makefile
all:gcc client.c -levent -o client.outgcc server.c -levent -o server.out
libevent的两个服务端、客户端示例(C语言)相关推荐
- java mina tcp_Mina TCP服务端客户端 示例
服务端代码:package com.xd.nms.example; import java.io.IOException; import java.net.InetSocketAddress; imp ...
- TCP/IP网络编程之基于TCP的服务端/客户端(二)
回声客户端问题 上一章TCP/IP网络编程之基于TCP的服务端/客户端(一)中,我们解释了回声客户端所存在的问题,那么单单是客户端的问题,服务端没有任何问题?是的,服务端没有问题,现在先让我们回顾下服 ...
- 一个简单的完成端口(服务端/客户端)类
一个简单的完成端口(服务端/客户端)类 作者:spinoza 翻译:麦子芽儿, POWERCPP(后面部分内容) 下载源代码 原文网址:http://www.codeproject.com/KB/IP ...
- react服务端/客户端,同构代码心得
FKP-REST是一套全栈javascript框架 react服务端/客户端,同构代码心得 作者:webkixi react服务端/客户端,同构代码心得 服务端,客户端同构一套代码,大前端的梦想,为了 ...
- restful服务端客户端_测试RESTful服务的客户端
restful服务端客户端 开发使用RESTful Web API的应用程序可能意味着开发服务器和客户端. 为服务器端编写集成测试可以像使用Arquillian启动服务器一样容易,并且可以通过REST ...
- netty java_GitHub - leihuazhe/Java11-Netty-Demo: 基于Java11 构建的 netty 服务端客户端 模块化例子...
Java11-Netty-Demo Java11 基于maven构建的简单的服务端客户端分离模块调用的例子 Java 11 从 Java9 开始引入了模块化的概念.使用Java11 也需要以模块化的方 ...
- TCP/IP网络编程之基于TCP的服务端/客户端(一)
TCP/IP网络编程之基于TCP的服务端/客户端(一) 理解TCP和UDP 根据数据传输方式的不同,基于网络协议的套接字一般分为TCP套接字和UDP套接字.因为TCP套接字是面向连接的,因此又称为基于 ...
- 网络云盘项目——HTTP接口介绍、功能介绍、服务端/客户端代码解析
一.本文目的 本项目分为6篇博客文章完成: 1.项目总体介绍:https://blog.csdn.net/qq_41453285/article/details/107871393. 2.Redis部 ...
- Python Django断点下载(服务端/客户端)
断点下载 背景 断点续传/断点下载一直是每个系统最实用的功能,最近公司在复杂的网络环境(国外vps)下载东西遇到问题,有些文件下载的时候很慢,并且可能会下不下来,这种情况对一个系统的稳定性构成 ...
最新文章
- 2007年你必须学习的10项.NET技术
- 终极解决方式——找到占用U盘的程序
- Windows下的ping
- datastage 函数_DataStage_Transformer常用函数
- CVPR 2020|打脸SOTA!不能忍,谷歌发起图像匹配挑战赛
- php71+yum源+epel,搭建CentOS在线yum源镜像服务器
- Python变量的作用范围
- 蓝桥杯 ADV-65 算法提高 格子位置
- ul阻燃标准有几个等级_UL阻燃等级介绍说明
- PHP语言基本数据类型
- 1436 旅行终点站
- 七、手把手教你搭建SpringCloudAlibaba之Sentinel实现流量控制
- centos虚拟机上网慢的问题
- 《搞不定人,你如何带团队?》读书记录
- rtb中的win_广告:RTB
- Openvino 模型文件部署推理
- c语言摄氏度字符,c语言摄氏度与华氏温度如何转换
- 输入年月日,获得下个月的同一天,如果该天不存在,则顺延一天
- 小议并实战go包------顺便说说go中的GOROOT,GOPATH和src,pkg,bin
- spring tool suit设置屏幕护眼保护色
热门文章
- bzoj 2761: [JLOI2011]不重复数字【hash】
- Spring第七弹—依赖注入之注解方式注入及编码解析@Resource原理
- python操作各种excel库
- 桶排序Bucket sort(转)
- LoadRunner 使用虚拟IP测试流程
- Struts2的输入验证(三)-短路验证与非字段验证
- jquery.ui.sortable 笔记
- java c static,java-是否可以禁用静态最终变量的javac内联?
- 举例说明操作系统在计算机系统中的重要地位,第一二三章作业参考答案
- 斜视术后融合训练方法_做斜视手术两年后又复发了怎么办?