約定:
端口8001
發送socket時先發送一段6字節的消息,表示接下來要發送多少字節的正文,服務器迴應同理(這個機制看上去漏洞百出,好吧,蒟蒻先這麼用着)
client隨便寫一下就好了
server(new.cpp):爲了練習libco、epoll、socket,大概採用這麼一個結構:
主協程負責初始化和eventloop,
有衆多worker協程(個數由宏定義CoNumber限定),worker接收到客戶的fd使用epoll的IO複用完成回射服務器的一套龍服務。worker協程由調度器指揮。
listen協程負責ac請求、將ac後的fd加到ac隊列(鏈表)中,在yield之前調用fd分配器queAcor將隊列中的fd分配給衆多worker,協程由計時器觸發,觸發頻率由AcTimeOut指定,單位ms。
queAcor(fd分配器函數),採用線段樹維護每個worker內部epoll中fd個數,然後將fd分配給工作壓力最小的worker,如果worker因此完成了從沒工作到有工作的轉換,則它會將它在調度隊列中激活。(本來想用二進制優化分配策略,但是想了想還是先不搞這些騷操作了)。前面的之所以用線段樹而不是堆,是因爲堆無法完成非堆頂節點數據的更新(雖然理論上可以這麼做,但是這樣就違背了堆的性質,且寫起來比較麻煩),總之,最後使用的線段樹(雖然它多使用了CoNumber*3*sizeof(int)的內存空間,我覺得還是值的)。
dispatcher協程調度器。由定時器觸發,頻率由DPTTime指定。每調度一次就將所有正在工作的worker遍歷resume一遍,如果當前worker所有客戶都已經下線,則將此worker丟到隊列鏈表後面,下次不觸發,直到它被queAcor激活。
所有協程均使用默認的128kb的棧(沒有使用share stack)
代碼沒有關閉斷言(會影響性能,不過博主沒有測過該代碼的性能)
大概就是這麼一個情況。
至於server(new.cpp)怎麼編譯,見---->>libco配置、編譯 初體驗 - 騰訊協程開源庫
OK,壓縮包版代碼下載見傳送門——>>>https://download.csdn.net/download/greybtfly/10811538
先貼短的client,由於client僅作爲測試用,所以很多地方不嚴謹,比如一些信號沒處理,甚至send都沒循環,見諒。
#include <iostream>
#include<stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include<sys/epoll.h>
using namespace std;
int myErrorOperate(char const * const error_str,int error_line,int error_exit=1)
{
perror(error_str);
printf("%d\n",error_line);
if(error_exit==1)
exit(1);
return 0;
}
int main()
{
int serverfd;
if((serverfd=socket( AF_INET, SOCK_STREAM, 0 ))<0)
myErrorOperate("socket error ",__LINE__);
struct sockaddr_in saddr;
memset ( &saddr, 0, sizeof(saddr) );
saddr.sin_family = AF_INET;
saddr.sin_port = htons( 8001 );
inet_pton(AF_INET,"127.0.0.1",&saddr.sin_addr);//"118.89.231.196"
char tmp[900];
char lenstring[10];
int ret;
memset(lenstring,0,sizeof(lenstring));
int len;
if(connect(serverfd,(struct sockaddr *)&saddr,sizeof(saddr))<0)
myErrorOperate("connect err",__LINE__);
else
{
cout<<"success"<<endl;
while(1)
{
cout<<"input----------->";
cin.getline(tmp,sizeof(tmp));
if(strcmp(tmp,"exit")==0)
break;
int msglen=strlen(tmp)+1;
sprintf(lenstring,"%d",msglen);
send(serverfd,lenstring,6,0);
cout<<"len message sended:"<<lenstring<<endl;
send(serverfd,tmp,msglen,0);
len=0;
cout<<"recving"<<endl;
while(len<6)
{
ret=recv(serverfd,&tmp[len],6-len,0);
len+=ret;
if(ret<=0)
{
myErrorOperate("recv len err",__LINE__,1);
close(serverfd);
return 0;
}
}
int messagelen=atoi(tmp);
cout<<"recv len message:"<<messagelen<<endl;
len=0;
while(len<messagelen)
{
ret=recv(serverfd,&tmp[len],messagelen-len,0);
perror("rcv.");
len+=ret;
if(ret<=0)
{
myErrorOperate("recv message err",__LINE__,1);
close(serverfd);
return 0;
}
}
printf("recv :%d bytes -->%s\n",messagelen,tmp);
cout<<"a turn complete"<<endl;
}
}
return 0;
}
服務端
#include "co_routine.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/time.h>
#include <stack>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/un.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <sys/wait.h>
#include <sys/epoll.h>
#ifdef __FreeBSD__
#include <cstring>
#include <sys/types.h>
#include <sys/wait.h>
#endif
#include<iostream>
#include<queue>
#include"co_routine.cpp"
using namespace std;
template<typename dataT>struct link_t;
template<typename dataT>struct linkNode_t;
struct task_t
{
stCoRoutine_t *co;//協程控制字
int workNumb;//epoll中的socket個數
int addNumb;//新來的數目
link_t<int> *acLink;
linkNode_t<task_t*> *dptLinkNode;//在調度器鏈表中的位置
int taskNumb;//協程號
};
template<typename dataT>
struct linkNode_t
{
struct link_t<dataT> *pLink;
struct linkNode_t *pNext;
struct linkNode_t *pPrev;
dataT data;
};
template<typename dataT>
struct link_t
{
linkNode_t<dataT> *head;
linkNode_t<dataT> *tail;
};
typedef struct dpt_t
{
link_t<task_t*>taskLink;
int coActivNumb;
}dpt_t;
#define CoNumber 128
#define EpNumber 1024 //一共CoNumber*EpNumber
//#define MinAlloc 100 //協程epoll最小處理個數(如果只有一個worker則不限制)
#define AGAINTIMES 3
#define MSGHeadLen 6
#define MSGBodyLen 1024
int const DPTTime=1; //調度器調度最小週期,可以調頻
int const AcTimeOut=10; //socket Ac 頻率
void debug(char const *p)
{
// return;
printf("%s\n",p);
return;
}
typedef int (*valueFunc_t)(void*,int);
template<int maxSize,valueFunc_t valueFunc> //,size,值計算函數,下標外部從0開始,內部從1開始
class segmentTree
{
#define lsonrt rt<<1
#define rsonrt rt<<1|1
void *ori;//原數組
int tree[maxSize<<2];
int ip;
int value(int idx)
{
return (*valueFunc)(ori,idx-1);
}
inline void pushup(int rt)
{
tree[rt]=min(tree[rt<<1],tree[rt<<1|1]);
}
public:
bool empty()
{
return !ori;
}
void build(int l,int r,int rt)
{
if(l==r)
{
tree[rt]=value(++ip);
return;
}
build(l,(l+r)>>1,rt<<1);
build(((l+r)>>1)+1,r,rt<<1|1);
pushup(rt);
}
segmentTree()
{
ori=NULL;
ip=0;
}
bool setOri(void *ori) //注意,ori數組下標從0開始。
{
if(ori==NULL)
{
debug("segment tree.ori==NULL");
exit(1);
}
if(this->ori)
return false;
this->ori=ori;
ip=0;
build(1,maxSize,1);
return true;
}
int queryMin()//返回最小值
{
if(ori==0)
{
cout<<"segmentTree.oir==NULL"<<endl;
exit(1);
}
return tree[1];
}
int queryIdx()//返回最小值的下標
{
if(ori==NULL)
{
cout<<"segmentTree.oir==NULL"<<endl;
exit(1);
}
int rt=1;
int l=1,r=maxSize;
while(l<r)
{
if(tree[lsonrt]<tree[rsonrt])
{
rt=lsonrt;
r=(l+r)>>1;
}
else
{
rt=rsonrt;
l=((l+r)>>1)+1;
}
}
return l-1;
}
void update(int idx)
{
idx++;
int rt=1;
int l=1,r=maxSize;
int mid;
while(l<r) //先找到idx的rt
{
mid=(l+r)>>1;
if(idx<=mid)
{
rt=lsonrt;
r=(l+r)>>1;
}
else
{
rt=rsonrt;
l=((l+r)>>1)+1;
}
}
//再遞歸地修改
tree[rt]=value(l);
while(rt)
{
rt>>=1;
pushup(rt);
}
}
};
int valueFunc(void *arr,int idx)
{
return ((task_t*)arr)[idx].addNumb+((task_t*)arr)[idx].workNumb;
}
segmentTree<CoNumber,valueFunc>coWoker;
link_t<int> acQue;
task_t _coWoker[CoNumber]={0};//----------------------------------全局變量-----------------
dpt_t dpt;
template<typename T>
void freeLinkNode(T* p)
{
free(p);
return;
}
template<typename T>
T* mallocLinkNode()
{
return (T*)malloc(sizeof(T));
}
template<typename T>
T* callocLinkNode(int n)
{
return (T*)calloc(n,sizeof(T));
}
int myErrorOperate(char const * const error_str,int error_line,int error_exit=1)
{
perror(error_str);
printf("%d\n",error_line);
if(error_exit==1)
exit(1);
return 0;
}
template<typename TLink,typename TNode>
void AddHead(TLink * apLink,TNode *ap)
{
if(apLink==NULL || ap==NULL)
{
myErrorOperate("AddHead.apLink==NULL || ap==NULL",__LINE__,1);
}
if(apLink->head)
{
apLink->head->pPrev=(TNode*)ap;
ap->pPrev=NULL;
ap->pNext=apLink->head;
apLink->head=ap;
}
else
{
apLink->head=apLink->tail=ap;
ap->pNext=ap->pPrev=NULL;
}
ap->pLink=apLink;
}
char* mallocMSGBodyMem()
{
return (char*)malloc(MSGBodyLen);
}
void freeMSGBodyMem(char* p)
{
free(p);
}
int co_accept(int fd, struct sockaddr *addr, socklen_t *len );
void queAcor() //將acQue成員分配給worker們
{
if(acQue.tail==NULL || coWoker.empty())
{
return;
}
int lst;//空間
linkNode_t<int> *ptr;
int i=0;
for(;acQue.head;)
{
if(coWoker.empty())
break;
int oldIdx=coWoker.queryIdx();
task_t *old=&_coWoker[oldIdx];
if(old->addNumb+old->workNumb==0)
{
dpt.coActivNumb++;
}
else if(old->addNumb+old->workNumb==EpNumber)
{
myErrorOperate("Acor:too many users err.Refuse connect request.",__LINE__,0);
ptr=acQue.head;
RemoveFromLink<linkNode_t<int>,link_t<int>>(ptr);
close(ptr->data); //拒絕請求
freeLinkNode(ptr);
/* cout<<"0號協程:" <<_coWoker[0].addNumb+_coWoker[0].workNumb<<endl;
cout<<"1號協程:" <<_coWoker[1].addNumb+_coWoker[1].workNumb<<endl;
cout<<"超限協程:" <<old->addNumb+old->workNumb<<endl;
cout<<"協程號"<<old->taskNumb<<endl;
cout<<"oldIdx:"<<oldIdx<<endl;
cout<<"debug ends"<<endl;*/
break;
}
task_t &top=*(old);
lst=EpNumber - top.workNumb - top.addNumb;
if(lst>0)
{
ptr=acQue.tail;
for(i=0;i<lst && ptr!=NULL;i++,ptr=acQue.tail)
{
RemoveFromLink<linkNode_t<int>,link_t<int>>(ptr);
AddTail(top.acLink,ptr);
top.addNumb++;
}
}
linkNode_t<task_t*> *tmp=top.dptLinkNode;
//在task中加入socket之後,將它在dpt的位置移動到頭部(主要是當它在coLstIdx之後的時候,把它移到前面活躍起來。)
RemoveFromLink<linkNode_t<task_t*>,link_t<task_t*>>(tmp);
AddHead(&dpt.taskLink,tmp);
coWoker.update(oldIdx);
}
}
static void* dispatcher(void* arg)//協程調度器
{
static int ActiveCnt=-1;
task_t *taskQue=(task_t*)arg;
link_t<task_t*> &link=dpt.taskLink;
//init
cout<<"dpt.init"<<endl;
for(int i=0;i<CoNumber;i++){
linkNode_t<task_t*> *nodeTmp=mallocLinkNode<linkNode_t<task_t*>>();
nodeTmp->pLink=NULL;
nodeTmp->data=&taskQue[i];
nodeTmp->data->dptLinkNode=nodeTmp;
AddTail(&link,nodeTmp);
}
cout<<"dpt.init.end"<<endl;
linkNode_t<task_t*> *nodeTmp=NULL;
for(;;)
{
linkNode_t<task_t*> *ptr=link.head;
if(ptr!=NULL)
{
int tmp=dpt.coActivNumb;
// cout<<"工作協程號:";
for(int i=0;ptr && i<tmp;i++)
{
if(ptr->data->workNumb+ptr->data->addNumb)//如果這個協程有工作
{
co_resume(ptr->data->co);
// cout<<ptr->data->taskNumb<<' ';
ptr=ptr->pNext;
}
else//沒有工作就把它取出,然後加到後邊
{
// cout<<"沒有工作:"<<ptr->data->taskNumb<<endl;
// dpt.coActivNumb--;
nodeTmp=ptr->pNext;
RemoveFromLink<linkNode_t<task_t*>,link_t<task_t*>>(ptr);
AddTail(&dpt.taskLink,ptr);
if(ptr->pNext==nodeTmp && nodeTmp)
{
// cout<<nodeTmp<<endl;
myErrorOperate("bug****",__LINE__,1);
}
ptr=nodeTmp;
}
}
// cout<<endl;
}
if(ActiveCnt!=dpt.coActivNumb)
{
ActiveCnt=dpt.coActivNumb;
printf("dispatcher:Active worker numb : %d\n",dpt.coActivNumb);
}
struct pollfd pf={0};//不關心epoll時間,只關心時間輪超時事件。
co_poll(co_get_epoll_ct(),&pf,1,DPTTime);//1ms執行一次
}
myErrorOperate("dpt:exit err",__LINE__,1);
}
static void* mcoListen(void *arg_co)
{
co_enable_hook_sys();
int lsEpFd;
lsEpFd=co_epoll_create(128);
if(lsEpFd<0)
{
myErrorOperate("create listen_epfd err",__LINE__);
}
int lsSocketFd;
if((lsSocketFd=socket(AF_INET,SOCK_STREAM,0))<0)
{
close(lsEpFd);
myErrorOperate("create listen_socket fd err.",__LINE__);
}
//set socket opt
int ret,val=1;
ret=setsockopt(lsSocketFd,SOL_SOCKET,SO_REUSEADDR,(void*)&val,sizeof(val));
//reuse addr
if(ret<0)
{
myErrorOperate("set SO_REUSEADDR err.",__LINE__,0);
}
struct sockaddr_in saddr;
memset(&saddr,0,sizeof(sockaddr_in));
saddr.sin_family=AF_INET;
saddr.sin_addr.s_addr=INADDR_ANY;
saddr.sin_port=htons(8001);
ret=bind(lsSocketFd,(struct sockaddr*)&saddr,sizeof(struct sockaddr_in));
if(ret<0)
{
myErrorOperate("lsten socket bind err.",__LINE__,1);
}
ret=listen(lsSocketFd,1024);//backlog
if(ret<0)
{
myErrorOperate("listen err.",__LINE__,1);
}
socklen_t saddrLen;
ret=-1;
for(;;)
{
saddrLen=sizeof(saddr);
ret=co_accept(lsSocketFd,(struct sockaddr*)&saddr,&saddrLen);
if(ret<0)//每次poll超時後都需要重新加入。
{
// cout<<"acor"<<endl;
queAcor();//將ac列表中的socket分配給coWoker
// cout<<"endAcor"<<endl;
struct pollfd pf={0};//不關心epoll時間,只關心時間輪超時事件。
pf.fd=lsSocketFd;
pf.events=(EPOLLERR|EPOLLHUP|EPOLLIN);
co_poll(co_get_epoll_ct(),&pf,1,AcTimeOut);//yield 同時關心epoll事件,和1000ms的超時事件
}
else if(ret>=0)
{
linkNode_t<int> *t=mallocLinkNode<linkNode_t<int>>();
t->pLink=NULL;
if(t==NULL)
{
close(ret);//若超出了處理範圍則close
myErrorOperate("socket acQue has too many node.",__LINE__,0);
}
t->data=ret;//fd
AddTail(&acQue,t);
}
}
}
void * echoFunc(void *args)
{
co_enable_hook_sys();
for(;;)
{
cout<<"time 0.5"<<endl;
struct pollfd pf={0};//不關心epoll時間,只關心時間輪超時事件。
co_poll(co_get_epoll_ct(),&pf,1,500);//500ms打印一次
}
}
void workerAddNew(task_t &taskSelf,int epFd)
{
int ret;
linkNode_t<int> *ptr=taskSelf.acLink->tail;
struct epoll_event *ep;
for(int i=0;i<taskSelf.addNumb;i++)
{
ep=mallocLinkNode<struct epoll_event>();
ep->events=(EPOLLERR | EPOLLIN | EPOLLHUP);
ep->data.ptr=(void*)ptr;
ret=epoll_ctl(epFd,EPOLL_CTL_ADD,ptr->data,ep);
if(ret<0)
{
myErrorOperate("worker:epoll_ctl err.",__LINE__,0);
co_yield_ct();
i--;
freeLinkNode(ep);
continue;
}
ptr=ptr->pPrev;
}
taskSelf.workNumb+=taskSelf.addNumb;
taskSelf.addNumb=0;
}
int sendToSocket(int fd,char* buff,int len,bool co=1)
{
int idx=0;
int ret;
int again=AGAINTIMES+1;
errno=0;
while(len>idx && again)
{
ret=send(fd,buff+idx,len-idx,0);
if(ret<=0)
{
if(errno==EINTR || errno==EAGAIN)
{
if(errno==EAGAIN)
{
again--;
}
if(co)
{
co_yield_ct();
}
continue;
}
return ret;
}
idx+=ret;
}
return len;
}
int recvFromSocket(int fd,char*buff,int len,int line,bool co=1)
{
memset(buff , 0 , len);
errno=0;
int idx=0;
int ret;
int again=AGAINTIMES+1;
while(len>idx && again)
{
ret=recv(fd,buff+idx,len-idx,MSG_WAITALL);
// perror("rcv form socket:");
if(ret==0)
{
}
if(ret<=0)
{
// cout<<"ret=="<<ret<<endl;
// perror("rcv.");
if(errno==EINTR || errno==EAGAIN)
{
// cout<<"rcv err"<<errno<<endl;
if(errno==EAGAIN)
{
again--;
}
if(co)
{
co_yield_ct();
}
continue;
}
return ret;
}
idx+=ret;
}
if(idx<len || again==0)
{
return -1;
}
return len;
}
bool checkSocketConnect(task_t &taskSelf,linkNode_t<int>*ptr,int ret,int line)
{
if(ret<=0)
{
if(ret==0)
{
debug("client closed");
}
else
{
debug("client err");
}
RemoveFromLink<linkNode_t<int>,link_t<int>>(ptr);
close(ptr->data);
freeLinkNode(ptr);
taskSelf.workNumb--;
coWoker.update(taskSelf.taskNumb);
return 1;
}
return 0;
}
void *wokerRoutine(void *_coWoker) //系統默認協程棧大小爲128KB
{
co_enable_hook_sys();
link_t<int> *linkTmp = mallocLinkNode<link_t<int>>();
memset(linkTmp,0,sizeof(link_t<int>));
static struct epoll_event evtOut[EpNumber];//因爲每次運行時協程都會重新取出epoll中active事件,所以此緩衝區可以共享。
task_t &taskSelf=*(task_t*)((task_t*)_coWoker);
//接受數據,發送數據。
int epFd=-1;
int ret;
for(;;)
{
if(taskSelf.workNumb+taskSelf.addNumb==0)
{
co_yield_ct();
continue;
}
if(epFd==-1)
{
epFd=epoll_create(100);
if(epFd<0)
{
myErrorOperate("WokerEp epoll_create err.With CoNumber=",__LINE__,0);
co_yield_ct();
continue;
}
}
//將新的加入
workerAddNew(taskSelf,epFd);
//處理事件
int activeNumb;
errno=0;
activeNumb=epoll_wait(epFd,evtOut,taskSelf.workNumb,0);//這裏的延時時間寫0不知道有沒有問題????????????????????????????????、
if(activeNumb<0)
{
myErrorOperate("epoll_wait err",__LINE__,0);
co_yield_ct();
continue;
}
char msgHead[MSGHeadLen+1];//如果是static可能會非協程安全。
msgHead[MSGHeadLen]=0;//結束符
for(int i=0;i<activeNumb;i++)
{
linkNode_t<int> * ptr=(linkNode_t<int> *)evtOut[i].data.ptr;//ptr是workLink中的fd對應節點。該節點data域爲fd
int sFd=ptr->data;
int evt=evtOut[i].events;
if(evt&EPOLLERR || evt&EPOLLHUP )
{
if(evt&EPOLLERR)
{
myErrorOperate("user socket err.user exit",__LINE__,0);
}
else
{
printf("user exit\n");
}
checkSocketConnect(taskSelf,ptr,0,__LINE__);
continue;
}
else if(evt&EPOLLIN)
{
int len;
errno=0;
ret=recvFromSocket(sFd,msgHead,MSGHeadLen,__LINE__);//讀長度信息
if(checkSocketConnect(taskSelf,ptr,ret,__LINE__))
continue;
ret=sendToSocket(sFd,msgHead,MSGHeadLen);//發送長度信息
if(checkSocketConnect(taskSelf,ptr,ret,__LINE__))
{
continue;
}
int msgLen=atoi(msgHead);
char *msgBody=mallocMSGBodyMem();
len=0;
int sdLen;
bool cls=0;
while(len<msgLen)
{
errno=0;
ret=recvFromSocket(sFd,msgBody,min(MSGBodyLen,msgLen-len),__LINE__);//接受一段body信息
len+=ret;
if(checkSocketConnect(taskSelf,ptr,ret,__LINE__))
{
cls=1;
break;
}
sdLen=ret;
ret=sendToSocket(sFd,msgBody,sdLen); //將這一段body發送出去
if(checkSocketConnect(taskSelf,ptr,ret,__LINE__))
{
cls=1;
break;
}
}
if(cls)
continue;
}
else
{
myErrorOperate("unkown err",__LINE__,0);
}
}
if(taskSelf.addNumb+taskSelf.workNumb==0)
{
dpt.coActivNumb--;
}
co_yield_ct();
}
return NULL;
}
void main_init()
{
acQue.head=acQue.tail=NULL;
struct sigaction sa; //這三行:設置向已關閉socket發送信息時默認行爲。防止向已關閉socket發送數據導致服務器崩潰
sa.sa_handler = SIG_IGN;
sigaction( SIGPIPE, &sa, 0 );
}
int main() {
cout<<"服務器初始化..."<<endl;
main_init();
task_t coLs,coDpt;
cout<<"創建調度..."<<endl;
co_create(&(coDpt.co),NULL,dispatcher,(void*)_coWoker);//init _coWoker.dpt
link_t<int> *acLinkMem=callocLinkNode<link_t<int>>(CoNumber);
cout<<"協程池初始化..."<<endl;
for(int i=0;i<CoNumber;i++)
{
_coWoker[i].taskNumb=i;//引用線段樹,協程調度輸出標識
_coWoker[i].acLink=acLinkMem+i;
co_create(&(_coWoker[i].co),NULL,wokerRoutine,&_coWoker[i]);
cout<<i<<endl;
co_resume(_coWoker[i].co);
}
cout<<"segment tree init"<<endl;
coWoker.setOri((void*)&_coWoker);
cout<<"創建listen..."<<endl;
co_create(&(coLs.co),NULL,mcoListen,&coLs);
cout<<"啓動listen..."<<endl;
co_resume(coLs.co);//啓動ac
cout<<"啓動調度..."<<endl;
co_resume(coDpt.co);
cout<<"服務器初始化完畢,啓動服務."<<endl;
co_eventloop(co_get_epoll_ct(),0,0);
return 0;
}