首页 > 代码库 > socket api- c/s模式:全双工 ;IO模式:同步阻塞,select,多路复用。

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

server:

client:

 

知识点:

1)服务端的多线程改为select复用来处理多用户。

  应该服务器瓶颈是在网络。CPU应该不是瓶颈,如果网络不是瓶颈,想再提高性能,可以考虑增加线程处理。

  1.标准输入可以另用线程,从select 分离出去。

  2.serverFD的accpet,也可以另用线程,分离出去。不过就要处理共享数据的tempserver的Fd集合了,加锁。所以这个感觉还是不要分离出去好。毕竟集合的添加删除是个频繁操作。

  3.多用户的处理,倒是可以用多线程处理。根据集合大小的情况。超过某个数量。就新开线程处理集合的某部分。新开的线程明显应该从某个数量检测,而select要从0开始检测fd,这个倒是浪费性能的事情。

 

未处理点:

1)还是要分行进行任务的处理。

 

#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>

using namespace std;

const int MAXLINE=1024*4;

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(3007);
    //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;

    //stdin->cmd  .socket->read&write. socket -> accept.

    fd_set fdset;
    int maxsocketFD=serverFD>STDIN_FILENO?serverFD+1:serverFD+1;

    vector<int> fdArrays;

    while(true)
    {

        FD_ZERO(&fdset);
        FD_SET(serverFD,&fdset);
        FD_SET(STDIN_FILENO,&fdset);


        //int max_id= max_element(fdArrays.begin(),fdArrays.end());

        for(int tempfdid: fdArrays)
        {
            FD_SET(tempfdid,&fdset);
            maxsocketFD=maxsocketFD>tempfdid?maxsocketFD:tempfdid+1;

            SA peerSA;
            socklen_t slen;
            int gresult= getpeername(tempfdid, (sockaddr*)&peerSA,&slen);
            if(gresult==-1)
            {
                perror("getpeername");
            }
            else
            {
                cout<<peerSA.sin_port<<endl;
            }


        }

        select(maxsocketFD,&fdset,0,0,0);

        if(FD_ISSET(STDIN_FILENO,&fdset))
        {
            cin>>cmd;
            if(cmd=="88")
            {
                close(serverFD);
                break;
            }
        }

        if(FD_ISSET(serverFD,&fdset))
        {
            //accept.& save it to array.
            int serverTempFD=accept(serverFD,0,0);
            if(serverTempFD==-1)
            {
                perror("accept");
            }
            else
            {
                fdArrays.push_back(serverTempFD);
            }
        }


        for(int tempfdid: fdArrays)
        {
            if(FD_ISSET(tempfdid,&fdset))
            {
                char readbuf[MAXLINE];
                bzero(readbuf,MAXLINE);
                int sizeread= read(tempfdid,readbuf,MAXLINE-1);
                if(sizeread==-1)//-1到底是个什么状态?是彻底不能连接还是可以重试?
                {
                    perror("read");
                    fdArrays.erase(find(fdArrays.begin(),fdArrays.end(),tempfdid));
                    close(tempfdid);
                    break;
                }
                else if(sizeread==0)//peer close or shutdown wr.
                {
                    fdArrays.erase(find(fdArrays.begin(),fdArrays.end(),tempfdid));
                    close(tempfdid);
                    break;
                }
                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(tempfdid,writebuff,strlen(writebuff));
                }
            }
        }
    }
    return 0;
}

client:

#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,fgets
#include <fcntl.h>
#include <unistd.h>//close.
#include <time.h>
#include <netinet/in.h>
#include <arpa/inet.h>//INET_PTON
#include <string>

const int MAXLINE=1024*4;

using namespace std;

int main()
{

    //socket->connect->read.
    int socketClientFD;
    int statusFlag;

    socketClientFD=socket(PF_INET,SOCK_STREAM,IPPROTO_IP);
    if(socketClientFD==-1)
    {
        perror("socket()");
        return -1;
    }


    struct sockaddr_in serverAddr;
    bzero(&serverAddr,sizeof(serverAddr));
    serverAddr.sin_family=AF_INET;
    inet_pton(AF_INET,"127.0.0.1",&serverAddr.sin_addr);

    //printf("%0x,%0x,%0x,%0x",((char*)&serverAddr.sin_addr)[0],((char*)&serverAddr.sin_addr)[1],((char*)&serverAddr.sin_addr)[2],((char*)&serverAddr.sin_addr)[3]);

    serverAddr.sin_port=htons(3007);


    statusFlag=connect(socketClientFD,(sockaddr*)&serverAddr,sizeof(serverAddr));

    if(statusFlag==-1)
    {
        perror("connect()");
        return -1;
    }

    char writeChar[MAXLINE];
    char buff[MAXLINE];

    fd_set fdset;//定义描述符集。

    FD_ZERO(&fdset);//初始化描述符集。

    int endflag=0;

    while(true)
    {
        //1)如果没有会触发很多次shutdown(socketClientFD,SHUT_WR);。,并且导致服务端,read 的时候会收到-1.
        //这样直接就关闭connect。客户端可能读不到任何数据。
        //2)为什么每次都要重新设置?
        if(endflag==0)
        {
            FD_SET(STDIN_FILENO,&fdset);//打开标准输入bit位。
        }
        else
        {
            FD_CLR(STDIN_FILENO,&fdset);//与其后面取消。还不如根绝标志这里设置。这样还统一一点。代码更清晰.
        }
        FD_SET(socketClientFD,&fdset);//打开客户socket bit位。

        int maxCheckFDCount=socketClientFD>STDIN_FILENO?socketClientFD+1:STDIN_FILENO+1;//只有2个描述符,肯定是除标准外的那个+1.

        select(maxCheckFDCount,&fdset,0,0,0);//只关心接收描述符。

        if(FD_ISSET(STDIN_FILENO,&fdset))
        {
            //cin.getline(writeChar,MAXLINE);
            bzero(writeChar,MAXLINE);

            cout<<"stdin fire"<<endl;

            int n=read(STDIN_FILENO,writeChar,MAXLINE);
            if(n==0)
            {
                cout<<"wirte over"<<endl;
                shutdown(socketClientFD,SHUT_WR);
                endflag=1;
                //close(socketClientFD);
            }
            else
            {

                statusFlag= write(socketClientFD,writeChar,strlen(writeChar));//写的时候只发送字符。不发送结束符。所以用strlen.

                if(statusFlag==-1)
                {
                    perror("write()");
                    return -1;
                }
            }
        }

        if(FD_ISSET(socketClientFD,&fdset))
        {
            bzero(buff,MAXLINE);
            statusFlag=read(socketClientFD,buff,MAXLINE-1);
            cout<<"start read"<<endl;
            if(statusFlag==0)//非正常关闭。因为这个cs模式下,服务端是被动关闭。程序是不可能得到0的。只有客户端close后才会由内核得到0.
            {
                cout<<"server close."<<endl;
                //close(socketClientFD);//到这里关闭。
                break;
            }
            else if(statusFlag==-1)
            {
                perror("read()");
                return -1;
            }
            else
            {
                buff[statusFlag]=\0;
                cout<<buff<<flush;
            }
        }
    }
    return 0;
}

 

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