libco

1.libco协程客户端

场景如下:

tcp_sever:接受客户端连接,处理客户端请求,5s后回包,模拟rpc阻塞式调用服务

tcp_client:连接tcp服务器,发送请求,等待回包,这里qps! 0.2/s

libco_echocli:连接tcp服务器,起n个协程,qps! 0.2*n/s

注意:

1)这里把tcp_server的处理时间设置为了5分钟,所以libco库的读写超时时间要比这个大,这里都直接设置成10s了,在文件co_hook_sys_call.cpp

static inline rpchook_t * alloc_by_fd( int fd )

{

rpchook_t *lp = (rpchook_t*)calloc( 1,sizeof(rpchook_t) );
lp->read_timeout.tv_sec = 10;
lp->write_timeout.tv_sec = 10;

...

}

 

1.场景1:tcp_client连接

[[email protected]:~/Documents/test_tcp]$./client 
3 time:1564800489 send req!
3 time:1564800494 recv rsp!
ret:15, server response
服务器:
[[email protected]:~/Documents/test_tcp]$./server 
fd:5 client 1564800489 req:heklko
fd:5 client 1564800494 rsp:server response
5 fd normal close

2.协程:./example_echocli 127.0.0.1 8888 1000 1 

起1000个协程

top:虚拟机单核cpu占用10%_15%

qps: 1000/5,每秒并发200个

客户端:

服务器:

libco_第1张图片fu

 

2)https://blog.csdn.net/hhyjiayou/article/details/80661666 这篇文章讲会将socket设置为非阻塞

libco_第2张图片实际上这里是没设置的,阻塞形式的io在读空 写满的时候会导致进程挂起,实际这里读写的时候fd还是阻塞的,只不过设置了一个超时时间,比如说read的时候,注册了一个epoll事件监听相关io,只有io到来的时候,或者超时了,才会通知到协程来处理,如果io是非阻塞式的,直接使用系统原生的read write了,

152 int flags = 0;
153 if(flags = fcntl(fd, F_GETFL, 0) < 0)
154 {
155 perror("fcntl");
156 }
157 if(flags & O_NONBLOCK)
158 {
159 //printf("%d no block\n", fd);
160 }
161 else
162 {
163 //printf("%d block\n", fd);
164 }
执行结果:

[
况且libco微信的分析也只是说把这部分异步化了,并没有说把fd设置为非阻塞了,

tcp_sever.cpp:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MAXLEN 1024

#include
#include

using namespace std;
#define ERR_EXIT(m) \
do { \
perror(m);\
exit(EXIT_FAILURE);\
}while(0)

int listenf();
static void do_epoll(int);

int main(int argc, const char *argv[])
{
int listenfd = listenf();
do_epoll(listenfd);
close(listenfd);
return 0;
}

int listenf()
{
int listenfd = socket(AF_INET, SOCK_STREAM, 0);//准备一个socketfd
if(listenfd == -1 )
ERR_EXIT("listen");

int on = 1;
if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)//setsockopt设置端口复用
{
close(listenfd);
ERR_EXIT("setsockopt");
}

struct sockaddr_in seraddr;
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(8888);
seraddr.sin_addr.s_addr = htonl(INADDR_ANY);
socklen_t len = sizeof(seraddr);
if(bind(listenfd, (struct sockaddr*)&seraddr, len) == -1)//监听socket端口,
{
close(listenfd);
ERR_EXIT("bind");
}

if(listen(listenfd, 6) == -1)
{
close(listenfd);
ERR_EXIT("listen");
}
return listenfd;
}

void do_epoll(int fd)
{
char recvbuf[MAXLEN] = {0};
int epollfd = epoll_create(2048);//设置的最大连接数
if(epollfd == -1)
ERR_EXIT("epoll_create");

struct epoll_event ev;
ev.data.fd = fd;
ev.events = EPOLLIN;
if(epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1)//加入轮询
ERR_EXIT("epoll_ctl_add");

struct epoll_event events[2048];//数组在epoll_wait返回结果的时候使用
int ret;
int i;//在下面while的for循环中遍历使用
int rfd;
int clientfd;
int nread;
std::map map_req;
while(1)
{
ret = epoll_wait(epollfd, events, 2048, 500);
if(ret == -1)
ERR_EXIT("epoll_wait");

for(i = 0; i < ret; ++i )
{
rfd = events[i].data.fd;
if(rfd == fd)
{
if((clientfd = accept(fd, NULL, NULL)) == -1)
ERR_EXIT("accept");
ev.data.fd = clientfd;
ev.events = EPOLLIN;
if(epoll_ctl(epollfd, EPOLL_CTL_ADD, clientfd, &ev) == -1)
ERR_EXIT("epoll_ctl");
}else
{
int nread = read(rfd, recvbuf, MAXLEN);
if(nread == -1)
{
if(errno == EINTR || errno == EAGAIN)
continue;
else
{
close(rfd);
printf("%d fd unnormal close\n", rfd);
}
//ERR_EXIT("read");
}else if( nread == 0)//客户端退出,从epoll轮询中删除
{
printf("%d fd normal close\n", rfd);
ev.data.fd = rfd;
ev.events = EPOLLIN;
if(epoll_ctl(epollfd, EPOLL_CTL_DEL, rfd, &ev) == -1)
ERR_EXIT("epoll_ctl");
close(rfd);
}else
{
printf("fd:%lu client %lu req:%s\n", rfd, time(NULL),recvbuf);
map_req[rfd] = time(NULL);
//sleep(5);
//if(write(rfd, recvbuf, strlen(recvbuf)) == -1)
// ERR_EXIT("write");
//printf("client %lu rsp:%s\n", time(NULL),recvbuf);

memset(recvbuf, 0, MAXLEN);
}
}
}

int iTime = time(NULL);
for(map::iterator itr = map_req.begin();
itr != map_req.end();
)
{
if(iTime - itr->second >=5)
{
write(itr->first, "server response", 15);
printf("fd:%d client %lu rsp:%s\n", itr->first, time(NULL),"server response");
map_req.erase(itr++);
}
else
{
itr++;
}
}
}
close(epollfd);
}
 

tcp_client.c:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#define ERR_EXIT(m) \
do { \
perror(m);\
exit(EXIT_FAILURE);\
}while(0)

#define MAXLINE 1024
static void do_client(int fd)
{
char recvbuf[MAXLINE + 1] = {0};
char sendbuf[MAXLINE + 1] = {0};

fd_set reade, ready;
FD_ZERO(&reade);
int fd_stdin = fileno(stdin);
FD_SET(fd_stdin, &reade);
FD_SET(fd, &reade);
int fd_max = (fd_stdin > fd) ? fd_stdin : fd;

int ret;
while(1)
{
ready = reade;
ret = select( fd_max+1, &ready, NULL, NULL, NULL);//轮询
if(ret == -1)
{
if(errno == EINTR)
continue;
ERR_EXIT("select");
}else if(ret == 0)
{
continue;
}

if(FD_ISSET(fd_stdin, &ready))
{
if(fgets(sendbuf, sizeof(sendbuf), stdin) == NULL)
{
close(fd);
break;
}else
{
if( -1 == write(fd, sendbuf, strlen(sendbuf)))
printf("write\n");
}
}


if(FD_ISSET(fd, &ready))
{
int nread = read(fd, recvbuf, MAXLINE);
if(nread < 0)
ERR_EXIT("read");
if(nread == 0)//如果没接收到消息,打印关闭描述符,退出循环
{
fprintf(stdout, "fd close\n");
break;
}
fprintf(stdout, "receive:%s", recvbuf);
}
memset(recvbuf, 0, sizeof recvbuf);
memset(sendbuf, 0, sizeof sendbuf);
}
}
void handle(int signum)
{
printf("sigpipe\n");
}

int main(int argc, const char *argv[])
{
signal(SIGPIPE, SIG_IGN);
int fd = socket(AF_INET, SOCK_STREAM, 0);
if(fd < 0)
ERR_EXIT("socket");

struct sockaddr_in cliaddr;
cliaddr.sin_family = AF_INET;
cliaddr.sin_port = htons(8888);
cliaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
socklen_t len = sizeof cliaddr;

int ret ;
if((ret = connect(fd, (struct sockaddr*)&cliaddr, len)) == -1)
{
close(fd);
ERR_EXIT("connect");
}
struct linger so_linger;
so_linger.l_onoff = 1;
so_linger.l_linger=1;
//设置延迟关闭
setsockopt(fd, SOL_SOCKET, SO_LINGER, &so_linger, sizeof(so_linger));
// do_client(fd);
write(fd, "heklko", 7);
printf("%d time:%lu send req!\n", fd, time(NULL));
char str[1024] = {0};
ret = read(fd, str, 1024);
if(ret)
{
printf("%d time:%lu recv rsp!\n", fd, time(NULL));
printf("ret:%d, %s\n", ret, str);
}
close(fd);
return 0;
}
 

example_echocli.cpp 用Libco里面的demo即可,注意测试时要把Libco的读写超时设置得比服务器处理时间大,不然一直返回errno 11,

 

 

 

你可能感兴趣的:(c)