客户端:
#include<event2/event-config.h>
#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);
//回调函数:参数是:fd,event,arg
event_add(ev_cmd,NULL); //添加事件
event_base_dispatch(base); //进入循环,等待就绪事件并执行事件处理
printf("finished\n");
return 0;
}
void cmd_msg_cb(int fd, short events, void* arg) { // fd:STDIN_FILENO,event:EV_READ | EV_PERSIST,arg:sockfd;
//该回调函数作用是从标准输入读到msg,再把msg的数据发送给sockfd(服务器端)
char msg[1024];
int ret = read(fd,msg,sizeof(msg)); //ret是读取的长度
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) { //fd:sockfd,event:EV_READ | EV_PERSIST,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 fron 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); //将IP地址转换为网络字节序整数
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;
}
gcc -o primary_client primary_client.c -levent
服务器端:
#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** 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);//将base参数传给回调函数
event_add(ev_listen, NULL);
event_base_dispatch(base);
return 0;
}
void accept_cb(int fd, short events, void* arg) //fd:listener , events: EV_READ | EV_PERSIST, arg:base;
{
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; //arg传入的实参就是base,但是是void*,所以强制转换回来
//仅仅是为了动态创建一个event结构体
struct event *ev = event_new(NULL, -1, 0, NULL, NULL);
//将动态创建的结构体作为event的回调参数
event_assign(ev, base, sockfd, EV_READ | EV_PERSIST, socket_read_cb, (void*)ev); //监听读事件
//event_assign等价于:struct event* ev = event_new(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) { //fd:sockfd, events:EV_READ | EV_PERSIST, arg:ev,监听读事件的回调函数
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",msg);
char reply_msg[4096] = "I have recvieced the msg:";
strcat(reply_msg + strlen(reply_msg),msg); //将msg字符串复制到reply_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;
}
gcc -o primary_server.c primary_server -levent
注意:
编译时要在编译语句后面加上 -levent
表示要链接event静态函数库,如果没有这一句,那么整个链接将会出错,类似与下面的结局:
main.cpp:(.text+0x2c):对‘evutil_make_listen_socket_reuseable’未定义的引用
main.cpp:(.text+0xec):对‘evutil_make_socket_nonblocking’未定义的引用
main.cpp:(.text+0xf1):对‘event_base_new’未定义的引用
main.cpp:(.text+0x13f):对‘event_new’未定义的引用
main.cpp:(.text+0x154):对‘event_add’未定义的引用
main.cpp:(.text+0x160):对‘event_base_dispatch’未定义的引用
main.cpp:(.text+0x16c):对‘event_base_free’未定义的引用
/tmp/ccFk4bSL.o:在函数‘do_accept(int, short, void*)’中:
main.cpp:(.text+0x22e):对‘bufferevent_socket_new’未定义的引用
main.cpp:(.text+0x254):对‘bufferevent_setcb’未定义的引用
main.cpp:(.text+0x265):对‘bufferevent_enable’未定义的引用
/tmp/ccFk4bSL.o:在函数‘read_cb(bufferevent*, void*)’中:
main.cpp:(.text+0x28f):对‘bufferevent_getfd’未定义的引用
main.cpp:(.text+0x2f5):对‘bufferevent_write’未定义的引用
main.cpp:(.text+0x313):对‘bufferevent_read’未定义的引用
/tmp/ccFk4bSL.o:在函数‘error_cb(bufferevent*, short, void*)’中:
main.cpp:(.text+0x357):对‘bufferevent_getfd’未定义的引用
main.cpp:(.text+0x408):对‘bufferevent_free’未定义的引用
运行结果:
客户端:
服务器: