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

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

服务端:

1)多线程处理客户connect。主线程,处理用户输入,二线程处理accpet,每次有新的connect,就建立新线程处理每个客户端。

2)二线程阻塞在accept,其他新线程阻塞在read。

3)当客户端close。服务端对应线程,read得到0. 之后也进行close(这一部上次实验没做,导致服务socket一直在close wait ,而客户一直在fin wait 2.)

  所以客户端socket阻塞在内核的time wait。而服务端阻塞的内核的 close wait。

 

缺点:

服务端一直轮循accept。

 

 

server:

#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 <thread>
#include <arpa/inet.h>//INET_PTON

using namespace std;

const int MAXLINE=1024*4;

typedef struct sockaddr_in SA;
int 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=0;
    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(3006);
    //serverAddr.sin_zero??

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

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

    thread a=thread(Accpetthread,serverFD);

    a.detach();

    int cmd;
    cout<<"exist:input 88"<<endl;
    for(;;)
    {
        cin>>cmd;
        if(cmd==88)
        {
            g_cmd=88;
            break;
        }
    }

    cout<<"close listen"<<endl;
    close(serverFD);//close 之后,地址不能使用.要等待2ml
    return 0;
}

//
void Accpetthread(int serverFD)
{
    //这里其实没有什么用。因为是阻塞在下面的语句。
    while(g_cmd!=88)
    {
        int serverTempFD=accept(serverFD,0,0);
        //之前没有判断。导致-1.也去处理了。
        if(serverTempFD!=-1)
        {
            thread bg=thread(threadProcess,serverTempFD);
            bg.detach();
        }

    }
    cout<<"not accept"<<endl;
}

void threadProcess(int serverTempFD)
{
    while(g_cmd!=88)
    {
        char readbuf[MAXLINE];
        int sizeread= read(serverTempFD,readbuf,MAXLINE-1);
        if(sizeread==-1)
        {
            perror("read");
            close(serverTempFD);
            break;
        }
        if(sizeread>0)
        {
            readbuf[sizeread]=\0;//以免溢出,插入结束符号.
            char writebuff[MAXLINE+10];
            //snprintf如果格式化后的字符串长度 >= size,则只将其中的(size-1)个字符复制到str中,并给其后添加一个字符串结束符(‘\0‘)
            int tempsize= snprintf(writebuff,MAXLINE+10-1,"%s:%d\n",readbuf,strlen(readbuf));
            cout<<writebuff<<":"<<strlen(writebuff)<<endl;
            write(serverTempFD,writebuff,strlen(writebuff));
        }
        else
        {
            cout<<"server close tempfd,and will send ack+1 & server fin"<<endl;
            //上一次实验,忘记了这个close。应该2端都关闭。这样服务端的临时端口就可以马上关闭。而客户端也可以尽快到达外time wait状态。
            //因为服务端的close是发送 server fin,和ack+1.发送完就closed,最后接收一个客户的ack。
            //不过通过这次错误的实验,突然就搞清楚了。为什么服务端的wait close是2个msl。(因为怕客户端再次使用同一个端口,而分不清,
            //而客户端,假如是最极端的情况。发送了close,最多一个msl之后进入 wait time,而服务端要等待客户的最后一次ack,又是最多
            //一个msl,所以只要2个msl,服务端就可以安全再次放开之前打算关闭的端口,)
            close(serverTempFD);
            break;
        }
    }
}

 

 

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(3006);


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

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




    char writeChar[MAXLINE];
    char buff[MAXLINE];
    ////和fgets(line,MAXLINE,stdin);的作用是一样的。都会只读取vmaxline-1的数量。最后加入0x00.但是用这个跨平台。而且fgets会读取换行符号。
    while(true)
    {
        cin.getline(writeChar,MAXLINE);
        cout<<writeChar<<endl;

        if(writeChar[0]==8&&writeChar[1]==8&&writeChar[2]==\0)
        {
            cout<<"88"<<endl;
            close(socketClientFD);
            break;
        }
        else
        {
            if(strlen(writeChar)>0)
            {
                statusFlag= write(socketClientFD,writeChar,strlen(writeChar));//写的时候只发送字符。不发送结束符。所以用strlen.

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

                statusFlag=read(socketClientFD,buff,MAXLINE-1);//这里会读入任何符号。包括结束符号。所以要求写的时候,不要发送结束符号。因此要求用strlen,确定实际字符。
                if(statusFlag>0)
                {
                    buff[statusFlag]=\0;
                    cout<<buff<<flush;
                }
                else if(statusFlag==0)
                {
                    cout<<endl;
                    break;
                }
                else
                {
                    perror("read()");
                    return -1;
                }
            }
            else
            {
                break;
            }

        }
    }




    //fgets(getLine,MAXLINE,cin);

//    statusFlag= write(socketClientFD,writeChar,strlen(writeChar));//写的时候只发送字符。不发送结束符。所以用strlen.
//
//    if(statusFlag==-1)
//    {
//        perror("write()");
//        return -1;
//    }
//
//    for(;;)
//    {
//        //bzero(buff,11);//每次要清空,更安全点。虽然后面已经buff[statusFlag]=‘\0‘;
//        statusFlag=read(socketClientFD,buff,10);//这里会读入任何符号。包括结束符号。所以要求写的时候,不要发送结束符号。因此要求用strlen,确定实际字符。
//        if(statusFlag>0)
//        {
//            buff[statusFlag]=‘\0‘;
//            cout<<buff<<flush;
//        }
//        else if(statusFlag==0)
//        {
//            cout<<endl;
//            break;
//        }
//        else
//        {
//            perror("read()");
//            return -1;
//        }
//    }
//
//    cout<<"eixist:input 88."<<endl;
//    int cmd;
//    while(1)
//    {
//        cin>>cmd;
//
//        if(cmd==88)
//        {
//            close(socketClientFD);
//            break;
//        }
//    }

    return 0;
}

 

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