linux并发服务器(二)

linux并发服务器(二)

  • 1.TCP并发服务器(单客户端单进程,统一accept)
    • 1.1.示例
    • 1.2. 客户端发送
    • 1.3.运行结果
    • 1.4.相关说明
  • 2.TCP并发服务器(单客户端单线程,统一accept)
    • 2.1.示例
    • 2.2. 客户端发送
    • 2.3.运行结果
    • 2.4.Wireshark数据包过程
  • 3. TCP并发服务器(IO复用循环服务器)
    • 3.1.概述
    • 3.2.示例
    • 3.3.客户端发送
    • 3.4.Wireshark数据包过程
    • 3.5.运行结果
    • 3.6.相关说明
  • 4.Netstat命令说明
    • 4.1.Ubuntu系统
    • 4.2.Window系统
  • 5.备注

1.TCP并发服务器(单客户端单进程,统一accept)

1.1.示例

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "errno.h"
#include "signal.h"
#include 
//#define NULL 0
#define BUFFLEN 1024
#define SERVER_PORT 8888
#define BACKLOG 5
#define PIDNUMB 2


static void tcp_handle_request(int sc)
{
    printf("[pid=0x%x]tcp_handle_request start sc=%d\n", getpid(), sc);
    int n=0;
    char buff[BUFFLEN];
    time_t now;
    int count = 0;
    while(1)
    {

        memset(buff,0,BUFFLEN);
        n=recv(sc,buff,BUFFLEN,0);

        if(n>0 && !strncmp(buff,"TIME",4))
        {
            printf("[pid=0x%x]recv data  sc=%d   data=[%s], count=%d\n",getpid(), sc ,buff,++count);
            memset(buff,0,BUFFLEN);
            now=time(NULL);
            sprintf(buff,"[%24s]\r\n",ctime(&now));
            send(sc,buff,strlen(buff),0);
        }
        else if(n<=0)
        {
           printf("[pid=0x%x]recv data res=%d errno=%d,msg=%s,close & exit\n",getpid(), n, errno,strerror(errno));
           break;
        }
    }
    close(sc);
    printf("[pid=0x%x]tcp_handle_request end sc=%d\n", getpid(), sc);

}
static int tcp_handle_connect(int ss)
{
    printf("[pid=0x%x]tcp_handle_request start ss=%d\n", getpid(), ss);
    int sc;

    struct sockaddr_in from;
    socklen_t len = sizeof(from);

    while(1)
    {
        sc=accept(ss,(struct sockaddr*)&from,&len);
        if(sc>0)
        {
            static char tempStr[32] = {0};
            inet_ntop(AF_INET,&(from.sin_addr),tempStr,sizeof(tempStr));
            printf("[pid=0x%x]handle_connect ss=%d sc=%d connect from %s:%u\n", getpid(),ss,sc,tempStr,ntohs(from.sin_port));

            if(fork()>0){
                int res = close(sc);
                if(res==-1)
                {
                   printf("[pid=0x%x]close sc res=%d close,errno=%d,msg=%s\n",getpid(), res,sc, errno,strerror(errno));

                }
            }
            else{
                tcp_handle_request(sc);
            }
        }
        else if(sc==-1)
        {
            printf("accept ss=%d errno=%d,msg=%s\n",ss,errno,strerror(errno));
            exit(1);
        }


    }

}
int tcp_main1(void)
{
    int ss;
    struct sockaddr_in local;

    ss=socket(AF_INET,SOCK_STREAM,0);
    printf("ss=%d\n",ss);

    memset(&local,0,sizeof(local));
    local.sin_family = AF_INET;
    local.sin_addr.s_addr=htonl(INADDR_ANY);
    local.sin_port = htons(SERVER_PORT);



    int res = bind(ss,(struct sockaddr*)&local,sizeof(local));
    if(res==-1)
    {
        printf("bind res=%d,errno=%d,msg=%s\n",res,errno,strerror(errno));
        exit(1);

    }
    printf("bind res=%d\n",res);

    res = listen(ss,BACKLOG);
    if(res==-1)
    {
        printf("listen res=%d,errno=%d,msg=%s\n",res,errno,strerror(errno));
        exit(1);

    }
    printf("listen res=%d\n" ,res);

    tcp_handle_connect(ss);

    return 0;
}

int main(void)
{
    tcp_main1();
    return 0;
}

1.2. 客户端发送

通过TCP&UDP测试工具发送

1.3.运行结果

ss=3
bind res=0
listen res=0
[pid=0x2203]tcp_handle_request start ss=3
[pid=0x2203]handle_connect ss=3 sc=4 connect from 192.168.10.2:4001
[pid=0x221a]tcp_handle_request start sc=4
[pid=0x221a]recv data  sc=4   data=[TIMEA], count=1
[pid=0x221a]recv data  sc=4   data=[TIMEA], count=2
[pid=0x221a]recv data  sc=4   data=[TIMEA], count=3
[pid=0x221a]recv data  sc=4   data=[TIMEA], count=4
[pid=0x221a]recv data  sc=4   data=[TIMEA], count=5
[pid=0x2203]handle_connect ss=3 sc=4 connect from 192.168.10.2:4002
[pid=0x222d]tcp_handle_request start sc=4
[pid=0x222d]recv data  sc=4   data=[TIMEB], count=1
[pid=0x222d]recv data  sc=4   data=[TIMEB], count=2
[pid=0x222d]recv data  sc=4   data=[TIMEB], count=3
[pid=0x222d]recv data  sc=4   data=[TIMEB], count=4
[pid=0x222d]recv data  sc=4   data=[TIMEB], count=5
[pid=0x221a]recv data res=0 errno=0,msg=Success,close & exit
[pid=0x221a]tcp_handle_request end sc=4
[pid=0x222d]recv data res=0 errno=0,msg=Success,close & exit
[pid=0x222d]tcp_handle_request end sc=4

1.4.相关说明

1、当客户端主动挥手断开连接,发送FIN信号过来时,服务端(本测试中服务端运行在ubuntu18.04)recv调用会返回0,TCP状态会处于CLOSE_WAIT.
$ netstat -tap | grep 8888
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
tcp 0 0 0.0.0.0:8888 0.0.0.0:* LISTEN 7436/test0001
tcp 0 0 armlinux:8888 192.168.10.2:4001 CLOSE_WAIT 7488/test0001
此时需要应用层主动关闭,发送FIN给客户端,切换到LAST_ACK,当接收到客户端应答的ACK后完成最终关闭;
$ netstat -tap | grep 8888
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
tcp 0 0 0.0.0.0:8888 0.0.0.0:* LISTEN 7873/test0001

2、客户端(本测试中客户端运行在windows)在主动断开连接时,接收到服务器的FIN和ACK后,进一步发送ACK,并切换到TIME_WAIT状态,一般需要等待2MSL超时才能完成最终关闭(通过调整相关系统参数,可以让系统快速关闭,具体自行搜索资料);
netstat -anto | grep 4001
TCP 192.168.10.2:4001 192.168.10.40:8888 TIME_WAIT 0 InHost

2.TCP并发服务器(单客户端单线程,统一accept)

2.1.示例

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "errno.h"
#include "signal.h"
#include 
#include 
//#define NULL 0
#define BUFFLEN 1024
#define SERVER_PORT 8888
#define BACKLOG 5
#define PIDNUMB 2


static void *tcp_handle_request(void * argv)
{
    int sc = *((int*)argv);
    printf("[pid=0x%x threadid=0x%lx]tcp_handle_request start sc=%d\n", getpid(),pthread_self(), sc);
    int n=0;
    char buff[BUFFLEN];
    time_t now;
    int count = 0;
    while(1)
    {

        memset(buff,0,BUFFLEN);
        n=recv(sc,buff,BUFFLEN,0);

        if(n>0 && !strncmp(buff,"TIME",4))
        {
            printf("[pid=0x%x threadid=0x%lx]recv data  sc=%d   data=[%s], count=%d\n",getpid(),pthread_self(), sc ,buff,++count);
            memset(buff,0,BUFFLEN);
            now=time(NULL);
            sprintf(buff,"[%24s]\r\n",ctime(&now));
            send(sc,buff,strlen(buff),0);
        }
        else if(n<=0)
        {
           printf("[pid=0x%x threadid=0x%lx]recv data res=%d errno=%d,msg=%s,close & exit\n",getpid(),pthread_self(), n, errno,strerror(errno));
           break;
        }
    }
    close(sc);
    printf("[pid=0x%x threadid=0x%lx]tcp_handle_request end sc=%d\n", getpid(),pthread_self(), sc);

}
static int tcp_handle_connect(int ss)
{
    printf("[pid=0x%x threadid=0x%lx]tcp_handle_request start ss=%d\n", getpid(),pthread_self(), ss);
    int sc;

    struct sockaddr_in from;
    socklen_t len = sizeof(from);

    pthread_t thread_do;
    while(1)
    {
        sc=accept(ss,(struct sockaddr*)&from,&len);
        if(sc>0)
        {
            static char tempStr[32] = {0};
            inet_ntop(AF_INET,&(from.sin_addr),tempStr,sizeof(tempStr));
            printf("[pid=0x%x]handle_connect ss=%d sc=%d connect from %s:%u\n", getpid(),ss,sc,tempStr,ntohs(from.sin_port));

            int res = pthread_create(&thread_do,NULL,tcp_handle_request,&sc);
            if(res == -1){
               printf("[pid=0x%x]pthread_create   res=%d  ,errno=%d,msg=%s\n",getpid(), res , errno,strerror(errno));

            }
        }
        else if(sc==-1)
        {
            printf("accept ss=%d errno=%d,msg=%s\n",ss,errno,strerror(errno));
            exit(1);
        }


    }

}
int tcp_main1(void)
{
    int ss;
    struct sockaddr_in local;

    ss=socket(AF_INET,SOCK_STREAM,0);
    printf("ss=%d\n",ss);

    memset(&local,0,sizeof(local));
    local.sin_family = AF_INET;
    local.sin_addr.s_addr=htonl(INADDR_ANY);
    local.sin_port = htons(SERVER_PORT);



    int res = bind(ss,(struct sockaddr*)&local,sizeof(local));
    if(res==-1)
    {
        printf("bind res=%d,errno=%d,msg=%s\n",res,errno,strerror(errno));
        exit(1);

    }
    printf("bind res=%d\n",res);

    res = listen(ss,BACKLOG);
    if(res==-1)
    {
        printf("listen res=%d,errno=%d,msg=%s\n",res,errno,strerror(errno));
        exit(1);

    }
    printf("listen res=%d\n" ,res);

    tcp_handle_connect(ss);

    return 0;
}

int main(void)
{
    tcp_main1();
    return 0;
}

2.2. 客户端发送

客户端连接后,各发5次,再关闭连接

2.3.运行结果

ss=3
bind res=0
listen res=0
[pid=0x2796 threadid=0x7f08734ce740]tcp_handle_request start ss=3
[pid=0x2796]handle_connect ss=3 sc=4 connect from 192.168.10.2:4001
[pid=0x2796 threadid=0x7f0872cb2700]tcp_handle_request start sc=4
[pid=0x2796]handle_connect ss=3 sc=5 connect from 192.168.10.2:4002
[pid=0x2796 threadid=0x7f08724b1700]tcp_handle_request start sc=5
[pid=0x2796 threadid=0x7f0872cb2700]recv data  sc=4   data=[TIMEA], count=1
[pid=0x2796 threadid=0x7f0872cb2700]recv data  sc=4   data=[TIMEA], count=2
[pid=0x2796 threadid=0x7f0872cb2700]recv data  sc=4   data=[TIMEA], count=3
[pid=0x2796 threadid=0x7f0872cb2700]recv data  sc=4   data=[TIMEA], count=4
[pid=0x2796 threadid=0x7f0872cb2700]recv data  sc=4   data=[TIMEA], count=5
[pid=0x2796 threadid=0x7f08724b1700]recv data  sc=5   data=[TIMEB], count=1
[pid=0x2796 threadid=0x7f08724b1700]recv data  sc=5   data=[TIMEB], count=2
[pid=0x2796 threadid=0x7f08724b1700]recv data  sc=5   data=[TIMEB], count=3
[pid=0x2796 threadid=0x7f08724b1700]recv data  sc=5   data=[TIMEB], count=4
[pid=0x2796 threadid=0x7f08724b1700]recv data  sc=5   data=[TIMEB], count=5
[pid=0x2796 threadid=0x7f0872cb2700]recv data res=0 errno=0,msg=Success,close & exit
[pid=0x2796 threadid=0x7f0872cb2700]tcp_handle_request end sc=4
[pid=0x2796 threadid=0x7f08724b1700]recv data res=0 errno=0,msg=Success,close & exit
[pid=0x2796 threadid=0x7f08724b1700]tcp_handle_request end sc=5

2.4.Wireshark数据包过程

linux并发服务器(二)_第1张图片

3. TCP并发服务器(IO复用循环服务器)

3.1.概述

与前面的方案,当客户端连接变多时,会新创建连接相同个数的进程或者线程,当此数值比较大时,如上千个连接,此时线程/进程资料存储占用,以及CPU在上千个进程/线程之间的时间片调度成本凸显,造成性能下降。需要一种新的模型来解决,select IO模型即是一种方案。

3.2.示例

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "errno.h"
#include "signal.h"
#include 
#include 
#include 
#include 

#ifndef NULL
#define NULL 0
#endif


#define MAX_MSG_LEN 1024
#define BUFFLEN 1024
#define SERVER_PORT 8888
#define BACKLOG 5
#define PIDNUMB 2
#define CLIENTNUM 5 //最大支持客户端数量
int connect_host[CLIENTNUM];
int connect_number=0;

void printMsg(const char* pcFmt, ...);

static void * handle_requst(void * argv)
{
    printMsg("[pid=0x%x threadid=0x%lx]handle_requst start  \n", getpid(),pthread_self() );

    int n=0;
    char buff[BUFFLEN]={0};
    time_t now;

    int maxfd=-1;//最大侦听文件描述符
    fd_set scanfd;//侦听描述符集合


    int i=0;
    int res = -1;

    struct timeval timeout;//超时时间
    for (;;)
    {
        maxfd = -1;
        FD_ZERO(&scanfd);
        //将已经建立的连接描述符添加到侦听描述符集合中,并获取最大的描述符
        int totalfdset=0;
        for(i=0;i<CLIENTNUM;i++)
        {
            if(connect_host[i]!=-1)
            {
                //printMsg("scanfd set fds:%d\n",connect_host[i]);
                FD_SET(connect_host[i],&scanfd);
                if(maxfd<connect_host[i])
                {
                    maxfd=connect_host[i];
                }
                totalfdset++;
            }
        }
        if(totalfdset<=0)
        {
            sleep(1);
            continue;
        }
        //printMsg("totalfdset=%d,maxfd=%d\n",totalfdset,maxfd);
        //timeout数值会被select改写,每次循环调用需要重新赋值,否则变成立即返回效果
        timeout.tv_sec = 1;
        timeout.tv_usec = 0;
        res = select(maxfd+1,&scanfd,NULL,NULL,&timeout);
        switch (res) {
        case 0:
            //printMsg("[pid=0x%x threadid=0x%lx]handle_requst select res=%d timeout \n", getpid(),pthread_self() ,res);
            break;
        case -1:
            printMsg("[pid=0x%x threadid=0x%lx]handle_requst select res=%d  err,errno=%d,msg=%s\n", getpid(),pthread_self() ,res,errno,strerror(errno));

            break;
        default:
            printMsg("[pid=0x%x threadid=0x%lx]handle_requst select res=%d  read event  \n", getpid(),pthread_self() ,res );

            if(connect_number<=0)
            {
                printMsg("[pid=0x%x threadid=0x%lx]handle_requst select res=%d ,connect_number<=0 ,break; \n", getpid(),pthread_self() ,res );
                break;

            }
            //查找激活的文件描述符
            for(i=0;i<CLIENTNUM;i++)
            {
                if(connect_host[i]!=-1)
                {
                    //是匹配的有效的文件描述符
                    if(FD_ISSET(connect_host[i],&scanfd))
                    {
                        memset(buff,0,BUFFLEN);
                        n = recv(connect_host[i],buff,BUFFLEN,0);
                        if(n>0 && !strncmp(buff,"TIME",4))
                        {
                            printMsg("[pid=0x%x threadid=0x%lx]recv data  sc=%d   data=[%s]\n",getpid(),pthread_self(), connect_host[i] ,buff);
                            memset(buff,0,BUFFLEN);
                            now=time(NULL);
                            sprintf(buff,"[%24s]\r\n",ctime(&now));
                            send(connect_host[i],buff,strlen(buff),0);
                        }
                        else if(n<=0)
                        {
                            printMsg("[pid=0x%x threadid=0x%lx]recv data res=%d errno=%d,msg=%s,close\n",getpid(),pthread_self(), n, errno,strerror(errno));
                            close(connect_host[i]);
                            connect_host[i] = -1;
                            connect_number--;
                            break;
                        }
                    }
                }
            }
            break;
        }

    }
    return 0;


}

static void *handle_connect(void *argv)
{
    int ss=*((int*)argv);

    printMsg("[pid=0x%x threadid=0x%lx]handle_connect start ss=%d\n", getpid(),pthread_self(), ss);
    struct sockaddr_in from;
    socklen_t len = sizeof(from);
    while(1)
    {
        //接收连接
        int sc=accept(ss,(struct sockaddr*)&from,&len);
        if(sc>0)
        {
            static char tempStr[32] = {0};
            inet_ntop(AF_INET,&(from.sin_addr),tempStr,sizeof(tempStr));
            printMsg("[pid=0x%x  threadid=0x%lx]handle_connect ss=%d sc=%d connect from %s:%u\n", getpid(),pthread_self(),ss,sc,tempStr,ntohs(from.sin_port));
            int i =0;
            for(i=0; i < CLIENTNUM;i++)
            {
                if(connect_host[i]==-1)
                {
                    //客户端连接未已经达到上线,则记录连接
                    connect_host[i]=sc;
                    connect_number++;
                    printMsg("[pid=0x%x  threadid=0x%lx]client queue is set ,count=%d,sc=%d\n",getpid(),pthread_self(),connect_number,sc);
                    break;
                }
            }
            if(i>=CLIENTNUM)
            {
                //客户端连接已经达到上线,则主动关闭连接
                printMsg("[pid=0x%x  threadid=0x%lx]client queue is full ,count=%d,cannot add ,close sc=%d\n",getpid(),pthread_self(),connect_number,sc);
                close(sc);
            }
        }
        else if(sc==-1)
        {
            printMsg("accept error sc=%d errno=%d,msg=%s\n",sc,errno,strerror(errno));
            exit(1);
        }
    }



    return 0;
}
int main(void)
{
    int ss;
    struct sockaddr_in local;

    ss=socket(AF_INET,SOCK_STREAM,0);
    printMsg("ss=%d\n",ss);


    memset(&local,0,sizeof(local));
    local.sin_family = AF_INET;
    local.sin_addr.s_addr=htonl(INADDR_ANY);
    local.sin_port = htons(SERVER_PORT);


    //设置端口复用
    int opt = 1;
    errno = 0;
    int res = setsockopt(ss,SOL_SOCKET,SO_REUSEADDR,(const void *)&opt,sizeof(opt));
    if(res == -1)
    {
        printMsg("set SO_REUSEADDR error, errno=%d,msg=%s\n", errno,strerror(errno));
    }


    res = bind(ss,(struct sockaddr*)&local,sizeof(local));
    if(res==-1)
    {
        printMsg("bind res=%d,errno=%d,msg=%s\n",res,errno,strerror(errno));
        exit(1);

    }
    printMsg("bind res=%d\n",res);

    res = listen(ss,BACKLOG);
    if(res==-1)
    {
        printMsg("listen res=%d,errno=%d,msg=%s\n",res,errno,strerror(errno));
        exit(1);

    }
    printMsg("listen res=%d\n" ,res);

    //原书是memset(connect_host,-1,CLIENTNUM);,不能初始化完全,此函数是按字节进行设置的
    memset(connect_host,-1,CLIENTNUM*sizeof (connect_host[0]));

    pthread_t thread_do_connect;
    pthread_t thread_do_request;

    res = pthread_create(&thread_do_connect,NULL,handle_connect,(void*)&ss);
    printMsg("pthread_create thread_do_connect res=%d\n",res);
    res = pthread_create(&thread_do_request,NULL,handle_requst,NULL);
    printMsg("pthread_create thread_do_request res=%d\n",res);

    res = pthread_join(thread_do_connect,NULL);
    printMsg("pthread_join thread_do_connect res=%d\n",res);
    res = pthread_join(thread_do_request,NULL);
    printMsg("pthread_join thread_do_request res=%d\n",res);

    res = close(ss);
    printMsg("close ss res=%d\n",res);
    return 0;
}


void printMsg(const char* pcFmt, ...)
{
    va_list     vList;
    char    acBuf[MAX_MSG_LEN];
    va_start(vList, pcFmt);
    va_end(vList);
    vsnprintf(acBuf, MAX_MSG_LEN, pcFmt, vList);

    struct tm *tm_now;
    struct timeval nowtime;
    gettimeofday(&nowtime,0);
    tm_now = localtime(&nowtime.tv_sec);
    printf("[%04d%02d%02d% 02d:%02d:%02d.%03d]%s", tm_now->tm_year+1900, tm_now->tm_mon+1, tm_now->tm_mday,
           tm_now->tm_hour, tm_now->tm_min, tm_now->tm_sec,nowtime.tv_usec/1000,
           acBuf);

}

3.3.客户端发送

3.4.Wireshark数据包过程

linux并发服务器(二)_第2张图片linux并发服务器(二)_第3张图片

3.5.运行结果

[20190813 22:01:56.191]ss=3
[20190813 22:01:56.191]bind res=0
[20190813 22:01:56.191]listen res=0
[20190813 22:01:56.191]pthread_create thread_do_connect res=0
[20190813 22:01:56.192]pthread_create thread_do_request res=0
[20190813 22:01:56.192][pid=0x53e6 threadid=0x7fb7cfcee700]handle_connect start ss=3
[20190813 22:01:56.192][pid=0x53e6 threadid=0x7fb7cf4ed700]handle_requst start  
[20190813 22:02:00.856][pid=0x53e6  threadid=0x7fb7cfcee700]handle_connect ss=3 sc=4 connect from 192.168.10.2:4001
[20190813 22:02:00.856][pid=0x53e6  threadid=0x7fb7cfcee700]client queue is set ,count=1,sc=4
[20190813 22:02:05.579][pid=0x53e6  threadid=0x7fb7cfcee700]handle_connect ss=3 sc=5 connect from 192.168.10.2:4002
[20190813 22:02:05.579][pid=0x53e6  threadid=0x7fb7cfcee700]client queue is set ,count=2,sc=5
[20190813 22:02:10.819][pid=0x53e6 threadid=0x7fb7cf4ed700]handle_requst select res=1  read event  
[20190813 22:02:10.819][pid=0x53e6 threadid=0x7fb7cf4ed700]recv data  sc=4   data=[TIMEA]
[20190813 22:02:12.172][pid=0x53e6 threadid=0x7fb7cf4ed700]handle_requst select res=1  read event  
[20190813 22:02:12.172][pid=0x53e6 threadid=0x7fb7cf4ed700]recv data  sc=4   data=[TIMEA]
[20190813 22:02:15.023][pid=0x53e6 threadid=0x7fb7cf4ed700]handle_requst select res=1  read event  
[20190813 22:02:15.023][pid=0x53e6 threadid=0x7fb7cf4ed700]recv data  sc=4   data=[TIMEA]
[20190813 22:02:25.703][pid=0x53e6 threadid=0x7fb7cf4ed700]handle_requst select res=1  read event  
[20190813 22:02:25.703][pid=0x53e6 threadid=0x7fb7cf4ed700]recv data  sc=5   data=[TIMEB]
[20190813 22:02:26.552][pid=0x53e6 threadid=0x7fb7cf4ed700]handle_requst select res=1  read event  
[20190813 22:02:26.552][pid=0x53e6 threadid=0x7fb7cf4ed700]recv data  sc=5   data=[TIMEB]
[20190813 22:02:27.214][pid=0x53e6 threadid=0x7fb7cf4ed700]handle_requst select res=1  read event  
[20190813 22:02:27.214][pid=0x53e6 threadid=0x7fb7cf4ed700]recv data  sc=5   data=[TIMEB]
[20190813 22:02:37.233][pid=0x53e6 threadid=0x7fb7cf4ed700]handle_requst select res=1  read event  
[20190813 22:02:37.233][pid=0x53e6 threadid=0x7fb7cf4ed700]recv data res=0 errno=0,msg=Success,close
[20190813 22:03:23.523][pid=0x53e6 threadid=0x7fb7cf4ed700]handle_requst select res=1  read event  
[20190813 22:03:23.523][pid=0x53e6 threadid=0x7fb7cf4ed700]recv data res=0 errno=0,msg=Success,close

3.6.相关说明

1、connect_host和connect_number存在多线程读写问题,正常需要进行加锁同步,这里只是简单测试,不进行处理;
2、注意点,这里初始化连接描述符不能使用
int connect_host[CLIENTNUM]={-1};
3、当select在调用过程中被被改写超时参数,因此在每次select之前需要进行重新赋值,否则会变成立即返回状态。
timeout.tv_sec = 1;
timeout.tv_usec = 0;
res = select(maxfd+1,&scanfd,NULL,NULL,&timeout);

4.Netstat命令说明

4.1.Ubuntu系统

~$ netstat -h
usage: netstat [-vWeenNcCF] [<Af>] -r         netstat {-V|--version|-h|--help}
       netstat [-vWnNcaeol] [<Socket> ...]
       netstat { [-vWeenNac] -i | [-cnNe] -M | -s [-6tuw] }

        -r, --route              display routing table
        -i, --interfaces         display interface table
        -g, --groups             display multicast group memberships
        -s, --statistics         display networking statistics (like SNMP)
        -M, --masquerade         display masqueraded connections

        -v, --verbose            be verbose
        -W, --wide               don't truncate IP addresses
        -n, --numeric            don't resolve names
        --numeric-hosts          don't resolve host names
        --numeric-ports          don't resolve port names
        --numeric-users          don't resolve user names
        -N, --symbolic           resolve hardware names
        -e, --extend             display other/more information
        -p, --programs           display PID/Program name for sockets
        -o, --timers             display timers
        -c, --continuous         continuous listing

        -l, --listening          display listening server sockets
        -a, --all                display all sockets (default: connected)
        -F, --fib                display Forwarding Information Base (default)
        -C, --cache              display routing cache instead of FIB
        -Z, --context            display SELinux security context for sockets

  <Socket>={-t|--tcp} {-u|--udp} {-U|--udplite} {-S|--sctp} {-w|--raw}
           {-x|--unix} --ax25 --ipx --netrom
  <AF>=Use '-6|-4' or '-A ' or '--'; default: inet
  List of possible address families (which support routing):
    inet (DARPA Internet) inet6 (IPv6) ax25 (AMPR AX.25) 
    netrom (AMPR NET/ROM) ipx (Novell IPX) ddp (Appletalk DDP) 
    x25 (CCITT X.25) 

4.2.Window系统

>netstat -h

显示协议统计信息和当前 TCP/IP 网络连接。

NETSTAT [-a] [-b] [-e] [-f] [-n] [-o] [-p proto] [-r] [-s] [-x] [-t] [interval]

  -a            显示所有连接和侦听端口。
  -b            显示在创建每个连接或侦听端口时涉及的
                可执行程序。在某些情况下,已知可执行程序承载
                多个独立的组件,这些情况下,
                显示创建连接或侦听端口时
                涉及的组件序列。在此情况下,可执行程序的
                名称位于底部 [] 中,它调用的组件位于顶部,
                直至达到 TCP/IP。注意,此选项
                可能很耗时,并且在你没有足够
                权限时可能失败。
  -e            显示以太网统计信息。此选项可以与 -s 选项
                结合使用。
  -f            显示外部地址的完全限定
                域名(FQDN)-n            以数字形式显示地址和端口号。
  -o            显示拥有的与每个连接关联的进程 ID。
  -p proto      显示 proto 指定的协议的连接;proto
                可以是下列任何一个: TCP、UDP、TCPv6 或 UDPv6。如果与 -s
                选项一起用来显示每个协议的统计信息,proto 可以是下列任何一个:
                IP、IPv6、ICMP、ICMPv6、TCP、TCPv6、UDP 或 UDPv6。
  -q            显示所有连接、侦听端口和绑定的
                非侦听 TCP 端口。绑定的非侦听端口
                 不一定与活动连接相关联。
  -r            显示路由表。
  -s            显示每个协议的统计信息。默认情况下,
                显示 IP、IPv6、ICMP、ICMPv6、TCP、TCPv6、UDP 和 UDPv6 的统计信息;
                -p 选项可用于指定默认的子网。
  -t            显示当前连接卸载状态。
  -x            显示 NetworkDirect 连接、侦听器和共享
                终结点。
  -y            显示所有连接的 TCP 连接模板。
                无法与其他选项结合使用。
  interval      重新显示选定的统计信息,各个显示间暂停的
                间隔秒数。按 CTRL+C 停止重新显示
                统计信息。如果省略,则 netstat 将打印当前的
                配置信息一次。

5.备注

基础代码来源于《linux网络编程 第二版》宋敬彬等编著,这里进行了改造和扩展;

你可能感兴趣的:(C/C++,网络编程,linux)