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连接
[syswj@192.168.1.54:~/Documents/test_tcp]$./client
3 time:1564800489 send req!
3 time:1564800494 recv rsp!
ret:15, server response
服务器:
[syswj@192.168.1.54:~/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个
客户端:
服务器:
fu
2)https://blog.csdn.net/hhyjiayou/article/details/80661666 这篇文章讲会将socket设置为非阻塞
实际上这里是没设置的,阻塞形式的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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/epoll.h>
#define MAXLEN 1024
#include <iostream>
#include <map>
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<int, int> 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<int, int>::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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <signal.h>
#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,