socket api- c/s模式:全双工 ;IO模式:同步阻塞,epoll,多路复用。

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了socket api- c/s模式:全双工 ;IO模式:同步阻塞,epoll,多路复用。相关的知识,希望对你有一定的参考价值。

server:

 

 

知识点:

1)

// epoll_event 结构 events 域取值 {{{
#define EPOLLIN 0x001 // 连接上有数据可读,包括 tcp 连接关闭时收到 FIN 包
#define EPOLLPRI 0x002 // 连接上有紧急数据可读
#define EPOLLOUT 0x004 // 连接可写
#define EPOLLRDNORM 0x040 // 普通数据可读
#define EPOLLRDBAND 0x080 // 优先级数据可读
#define EPOLLWRNORM 0x100 // 普通数据可写
#define EPOLLWRBAND 0x200 // 优先级数据可写
#define EPOLLMSG 0x400 // 消息队列数据
#define EPOLLERR 0x008 // 连接上发生错误
#define EPOLLHUP 0x010 // 连接被挂起
#define EPOLLRDHUP 0x2000 // 连接远端已经关闭或半关闭
// }}}

 

2)

//个人感觉epoll应该是硬件的发展,导致多使用了一个事件数组,每次来清空,并插入有效事件.
//而不必像原来那样,直接修改输入参数.来节省内存.这样带来编程的简便性.
//原来需要每次重新设置需要触发的fd集合,因为会被改写.而现在用一个文件描述符代替,且内核不改动.
//原来需要每次遍历触发的fd集合,并查看其中的数据来确定是否触发
//而现在新加了一个已触发事件集合,内存换效率吧.还有mmap技术.

//网上查看了下,精华就这一段话了.
//如此,一颗红黑树,一张准备就绪句柄链表,少量的内核cache,就帮我们解决了大并发下的socket处理问题。
//执行epoll_create时,创建了红黑树和就绪链表,
//执行epoll_ctl时,如果增加socket句柄,则检查在红黑树中是否存在,存在立即返回,不存在则添加到树干上,
//然后向内核注册回调函数,用于当中断事件来临时向准备就绪链表中插入数据。
//执行epoll_wait时立刻返回准备就绪链表里的数据即可。

//自己分析下:
//一个epoll文件描述符,存储注册的事件,依据epoll中事件的索引.内核建立一个红黑树.
//红黑树保证大量socket下.中断发生时,内核得到FD,需要查看是否在我们epoll文件描述符中注册的事件上.,
//红黑树是效率更好的平衡查找树,log2的效率,最多大概20次的查询,就可以在百万的数据中找到fd.
//如果确实是我们注册的事件,调用callback.把中断事件的信息放入到链表,再把整个链表复制给我们的struct epoll_event events[n]?
//之后内核再自己清空链表?
//所以基础知识很重要,大概了解红黑树和中断,就很容易理解epoll的机制和大概推算其效率.
3)
//关于et和lt,
//个人感觉应该说非阻塞的io不适合et,因为et只会触发一次.后续数据还没到的话,非阻塞,就会错过数据(如果之后再也没有触发过).这样的因果说法才更清晰.
//所以非阻塞适合用lt,而阻塞的用lt和et应该都行.


遗留问题:
1)stdin 用epollrdnormon,会出现一运行就触发事件.要详细了解下参数.
2)要测试下 events是否内核会自动清空.打断点就可以测试.
#include <iostream>
#include <sys/socket.h>//{socket_type.h}:socket,AF_INET,SOCK_STREAM,PF_INET
#include <netdb.h>//{<netinet/in.h>}:IPPROTO_TCP
#include <sys/errno.h>
#include <string.h>
#include <stdio.h>//perror
#include <fcntl.h>
#include <unistd.h>//close.
#include <time.h>
#include <arpa/inet.h>//INET_PTON
#include <chrono>
#include <vector>
#include <algorithm>
#include <poll.h>
#include <sys/epoll.h>

//EAGAIN、EWOULDBLOCK:请再试,说明无数据.
//EINTR 指操作被中断唤醒,需要重新读/写

using namespace std;

const int MAXLINE=1024*4;
const int MAXFD=100;

typedef struct sockaddr_in SA;
string g_cmd;

void Accpetthread(int serverFD);
void threadProcess(int serverTempFD);
int main()
{
    //socket->addr->bind->listen->accept(read ,write)
    int serverFD;
    int intflag;
    g_cmd="";
    SA serverAddr;
    bzero(&serverAddr,sizeof(serverAddr));
    serverFD=socket(AF_INET,SOCK_STREAM,IPPROTO_IP);
    if(serverFD==-1)
    {
        perror("create()");
        return -1;
    }

    //serverAddr.sin_addr.s_addr=htonl(INADDR_ANY);
    inet_pton(AF_INET,"127.0.0.1",&serverAddr.sin_addr);
    serverAddr.sin_family=AF_INET;
    serverAddr.sin_port=htons(3021);
    //serverAddr.sin_zero??

    intflag=bind(serverFD,(sockaddr*)&serverAddr,sizeof(sockaddr));
    if(intflag==-1)
    {
        perror("bind()");
        return -1;
    }

    listen(serverFD,10);//max queue?

    string cmd;
    cout<<"exist:input 88"<<endl;

    //
    int epollFD= epoll_create1(0);
    if(epollFD==-1)
    {
        perror("epoll_create()");
        return -1;
    }

    struct epoll_event ev, ev2;//临时事件变量,EPOLL_CTL_ADD后,事件会被复制到到epollFD里
    struct epoll_event events[100];//有棵红黑树记录数组的索引?按FD大小做key?

    ev.data.fd=serverFD;
    ev.events=EPOLLIN;//还是默认lt好.
    int epoll_ctrflag=epoll_ctl(epollFD,EPOLL_CTL_ADD,serverFD,&ev);
    if(epoll_ctrflag==-1)
    {
        perror("epoll_ctl()");
        return -1;
    }

    ev2.data.fd=STDIN_FILENO;
    ev2.events=EPOLLIN;
    epoll_ctrflag=epoll_ctl(epollFD,EPOLL_CTL_ADD,STDIN_FILENO,&ev2);
    if(epoll_ctrflag==-1)
    {
        perror("epoll_ctl()");
        return -1;
    }

    for(;;)
    {
        int epoll_wait_flag=epoll_wait(epollFD,events,100,-1);
        cout<<"epoll wait:"<<epoll_wait_flag<<endl;

        if(epoll_wait_flag==-1)
        {
            perror("wait()");
            continue;
        }

        for(int i=0;i<epoll_wait_flag;++i)
        {
            if(events[i].data.fd==STDIN_FILENO)
            {
                string cmd;
                cin>>cmd;//这个是阻塞操作.
//                char buffcmd[MAXLINE];
//                int cmdlen=read(STDIN_FILENO,buffcmd,MAXLINE-1);
//                buffcmd[cmdlen]=0;
//                cmd=string(buffcmd);
                if(cmd=="88")
                {
                    close(epollFD);
                    close(serverFD);
                    return -1;
                }
                else
                {
                    cout<<"exist:input 88"<<endl;
                }
            }
            else if(events[i].data.fd==serverFD)
            {
                //accpet
                cout<<"accpet trige"<<endl;
                int tempServerFD=accept(serverFD,0,0);
                cout<<tempServerFD<<endl;
                if(tempServerFD==-1)
                {
                    perror("accpet");
                    continue;
                }

                ev.data.fd=tempServerFD;
                ev.events=EPOLLIN;//这里又和poll不一样,EPOLLIN,通常用作数据可读和fin信号.

                epoll_ctrflag=epoll_ctl(epollFD,EPOLL_CTL_ADD,tempServerFD,&ev);
                if(epoll_ctrflag==-1)
                {
                    perror("epoll_ctl()");
                    continue;
                }

            }
            else
            {
                char readbuf[MAXLINE];
                bzero(readbuf,MAXLINE);
                //int sizeread= read(pollfdArray[i].fd,readbuf,MAXLINE-1);
                int sizeread= recvfrom(events[i].data.fd,readbuf,MAXLINE-1,0,0,0);
                if(sizeread==-1)//-1到底是个什么状态?是彻底不能连接还是可以重试?
                {
                    perror("read()");
                    close(events[i].data.fd);
                    ev.data.fd=events[i].data.fd;
                    ev.events=EPOLLWRNORM;

                    epoll_ctl(epollFD,EPOLL_CTL_DEL,events[i].data.fd,&ev);
                    continue;
                }
                else if(sizeread==0)
                {
                    close(events[i].data.fd);
                    ev.data.fd=events[i].data.fd;
                    ev.events=EPOLLWRNORM;

                    epoll_ctl(epollFD,EPOLL_CTL_DEL,events[i].data.fd,&ev);
                    continue;
                }
                else
                {
                    readbuf[sizeread]=\0;//以免溢出,插入结束符号.
                    char writebuff[MAXLINE+10];
                    //snprintf如果格式化后的字符串长度 >= size,则只将其中的(size-1)个字符复制到str中,并给其后添加一个字符串结束符(‘\0‘)
                    snprintf(writebuff,MAXLINE+10-1,"%s:%d\n",readbuf,strlen(readbuf));
                    cout<<writebuff<<flush;
                    write(events[i].data.fd,writebuff,strlen(writebuff));
                }
            }
        }
    }



//    struct pollfd pollfdArray[MAXFD];
//    pollfdArray[0].fd=STDIN_FILENO;
//    pollfdArray[0].events=POLLIN;//标准输入数据不是普通数据......
//    pollfdArray[0].revents=0;
//
//    pollfdArray[1].fd=serverFD;
//    pollfdArray[1].events=POLLRDNORM;//标准输入数据不是普通数据......
//    pollfdArray[1].revents=0;
//
//    int fdMAXIndex=1;
//
//    for(int i=2;i<MAXFD;++i)
//    {
//        pollfdArray[i].fd=-1;
//    }
//
//    while(true)
//    {
//        int pollStatus= poll(pollfdArray,fdMAXIndex+1,-1);
//        if(pollStatus<0)
//        {
//            perror("poll()");
//        }
//        else if(pollStatus==0)
//        {
//            cout<<"no events. why program be here?"<<endl;
//        }
//        else
//        {
//            if(pollfdArray[1].revents==POLLRDNORM)
//            {
//                pollfdArray[1].revents=0;
//                int tempServerFD=accept(pollfdArray[1].fd,0,0);//-1或非负整数
//
//                struct timeval mytime;
//                mytime.tv_sec=10;
//                mytime.tv_usec=0;
//
//
//                setsockopt(tempServerFD,SOL_SOCKET,SO_RCVTIMEO,&mytime,sizeof(mytime));
//
//
//                if(tempServerFD==-1)
//                {
//                    perror("accept()");
//                }
//                for(int i=2;i<MAXFD;++i)
//                {
//                    if(pollfdArray[i].fd==-1)
//                    {
//                        pollfdArray[i].fd=tempServerFD;
//                        pollfdArray[i].events=POLLRDNORM;
//
//                        fdMAXIndex=fdMAXIndex>i?fdMAXIndex:i;
//                        break;//
//                    }
//
//                }
//                if(--pollStatus<=0)
//                {
//                    continue;
//                }
//            }
//
//
//            for(int i=2;i<=fdMAXIndex&&pollStatus>0  ;++i)
//            {
//                if(pollfdArray[i].fd!=-1)
//                {
//                    if(pollfdArray[i].revents==POLLRDNORM)
//                    {
//                        char readbuf[MAXLINE];
//                        bzero(readbuf,MAXLINE);
//                        //int sizeread= read(pollfdArray[i].fd,readbuf,MAXLINE-1);
//                        int sizeread= recvfrom(pollfdArray[i].fd,readbuf,MAXLINE-1,0,0,0);
//                        if(sizeread==-1)//-1到底是个什么状态?是彻底不能连接还是可以重试?
//                        {
//                            perror("read()");
//                            close(pollfdArray[i].fd);
//                            pollfdArray[i].fd=-1;
//                        }
//                        else if(sizeread==0)
//                        {
//                            close(pollfdArray[i].fd);
//                            pollfdArray[i].fd=-1;
//                        }
//                        else
//                        {
//                            readbuf[sizeread]=‘\0‘;//以免溢出,插入结束符号.
//                            char writebuff[MAXLINE+10];
//                            //snprintf如果格式化后的字符串长度 >= size,则只将其中的(size-1)个字符复制到str中,并给其后添加一个字符串结束符(‘\0‘)
//                            snprintf(writebuff,MAXLINE+10-1,"%s:%d\n",readbuf,strlen(readbuf));
//                            cout<<writebuff<<flush;
//                            write(pollfdArray[i].fd,writebuff,strlen(writebuff));
//                        }
//                        --pollStatus;
//                    }
//
//                    if(pollfdArray[i].revents==POLLERR)//overtime?
//                    {
//                        cout<<"error"<<endl;
//                    }
//                }
//            }
//
//
//            if(pollfdArray[0].revents==POLLIN)
//            {
//                pollfdArray[0].revents=0;//需要重设返回值不?
//
//                cin>>cmd;
//                if(cmd=="88")
//                {
//                    //close all sockets.
//                    close(serverFD);
//                    break;
//                }
//            }
//
//        }
//    }
    return 0;
}

 


















以上是关于socket api- c/s模式:全双工 ;IO模式:同步阻塞,epoll,多路复用。的主要内容,如果未能解决你的问题,请参考以下文章

socket api- c/s模式:服务多次读写,客户多次写读(同步处理多客户,多线程,). IO模式:select 阻塞,多路复用《客户端select,应对服务端奔溃和利用socket的全双工发送和

socket api- c/s模式:服务写,客户读. IO模式:阻塞

socket api- c/s模式:服务读写,客户写读. IO模式:阻塞

socket api- c/s模式:服务多次读写,客户多次写读(同步处理多客户,多线程). IO模式:阻塞t

用socket模拟实现全双工通信

web socket