一种基于linux系统的精准流量统计方法

前言:

    在linux系统关于流量统计,已经有开源的工具,比如nethogs,nload和iptraf。它们适合我们在PC上直接监控某台设备的流量情况,但并不适合我们应用到自己的程序中去。如果要在自己代码中实现流量的统计,可以有下面几种方法:统计应用层流量;使用tcpdump抓取每一包数据进行统计;使用Iptables命令来实现。下面就这几个方法进行对比:

(1)应用层计算流量

    该方法也就是在自己程序中的每个end和recv函数中去实现,统计自己每次进行网络通信时候数据的接收和发送长度,进而统计出总的数据流量。这种方法是一种粗略的计算,实际上是非常不准确的,它比实际的数据流量统计少了。因为我们的send和recv函数是在应用层。应用层的数据,到链路层,中间需要经过传输层和网络层,他们会对数据进行封装,加上数据包头,校验等等信息之后才会到链路层,所以实际链路层发送的数据比应用层的数据要多一些额外的数据,接收数据的时候其实也是一样,只是整个流程发过来了。另外,在网络传输的过程中,还可能出现数据的丢失,这个在有线传输中出现较少,但是在无线传输并且网络状态不是很好的情况下,出现数据丢失数据重传的概率是非常高的,而在应用层的send和recv函数并不能感知到数据的丢失和重传,所以在应用层统计的流量是不准确的,它会比实际的数据流量统计少了,实际少多少,这个跟网络状态和传输方式有关。实际网络运营商统计的数据流量,是链路层的数据流量,而不是应用层的网络流量。

(2)tcpdump抓数据

    tcpdump是与Windows系统的wireshark类似的一个网络抓包工具。它可以感知链路层数据的丢失和重传等等信息,但是它是基于数据截取的方式来获取信息,这样的方式比较影响网络的性能,同时也是比较消耗系统的资源,可以用来做网络调试,但不是非常适合网络流量的统计。

(3)使用iptables统计流量

    iptables命令是Linux上常用的防火墙软件,是netfilter项目的一部分。它可以根据不同的规则对数据进行过滤,转发和统计。它可以针对某一个IP或是多个IP进程处理,也可以针对某一个端口进行处理。当它做数据统计的时候,它的数值统计是链路层的数据。也就是包括了IP包信息和数据重传等额外数据的长度。通过这种方式,可以实现流量的精准统计。

 

设计思路:

    基本设计方法是这样:在一个进程(进程A)中循环检测有哪些IP和端口需要添加进Iptable的统计规则中,如果有收到一个添加规则的请求,则判断该规则是否已经添加进Iptables中,如果没有则添加,如果有则放弃此次规则的添加。在其他的进程中,比如进程B,C,D,E...,在进程网络连接的时候,根据需求将需要统计的IP或是端口信息,发送给进程A,让进程A去进行iptables规则的添加。另外,在进程A中,可以循环的去获取Iptables统计的流量,还可以实时的去获取网卡实际收发的数据,实现网络流量的实时更新。进程A,B,C,D,E之间,可以使用进程间通信的任意一种,这里为了方便扩展,使用了命名管道进程通信。为了方便数据检验传递和处理,在进程间传递的IP地址可以转换为数值而不是字符串,实际在connect函数建立网络连接的时候,使用的也是一个32位的int类型数据来表示IP地址。

功能实现:

(1)iptables规则添加

    这里只统计IP,不进行端口的统计,使用一个数组来记录需要添加进iptables 的规则,规则命名如下:

    可以使用iptables --list查看实际添加的规则:

(2)Iptable的流量查看:

    可以使用命令:iptables -n -v -L -t filter -x 查看iptables的流量统计情况:

(3)网卡流量查看:

    在linux系统中,我们可以通过proc虚拟文件系统获取linux系统的一些信息,其中就包括网卡信息,在Ubuntu16.04系统中/sys/class/net/ens33/statistics/目录下的文件就记录了网卡的收发数据量,所发数据包数等等信息。

    查看本次开机网卡总共发送的数据量:

biao@ubuntu:~/test/nettraffic/NetTrafficStatis/Process1$ cat /sys/class/net/ens33/statistics/tx_bytes
12219392
biao@ubuntu:~/test/nettraffic/NetTrafficStatis/Process1$ 

(4)进程间通行

    这里使用的是命名管道,在使用命名管道的时候,需要注意管道的阻塞和非阻塞模式,这里接收和发送都是采用的非阻塞模式,另外,还需要注意管道的收发数据规则。在接收进程中,非阻塞方式打开,可以正常打开,接受数据也会立即返回。在发送端非阻塞方式打开,如果这条管道,没有一个进程在接收,那么在发送端打开管道会返回失败。如果有多个进程进行数据写入,但是只有一个进程在进行读操作的时候,要注意读写数据的原子性操作。

    我们这里是针对特定IP进程流量的统计,在不同进程之间,我们需要把IP地址信息发送给进程A进行Iptables规则添加,这里我们使用的是重新封装connect函数,在connect函数中,我们可以获取到需要建立网络链接的IP地址的一个32位10进制数值,我们可以直接将该值传递给进程A,进程A再将该10进制的数值转换为字符串类型的IP地址。

(5)代码实现:

(a)进程A中iptable规则添加,流量获取实现:

/************************************************************
*Copyright (C), 2017-2027,lcb0281at163.com lcb0281atgmail.com
*FileName: NetTrafficStati.cpp
*Date:     2019-06-22
*Author:   Caibiao Lee
*Version:  V1.0
*Description:网络流量统计
*Others:
*History:
***********************************************************/
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <fctnl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>

#include "IPAddrInterFace.h"
#include "NetTrafficStati.h"

/***********************************************
Function:    NetTrafficStati
Description: 构造函数
Input: none
Output: none
Return: none
Others:
Author: Caibiao Lee
Date: 2019-06-21
*************************************************/
NetTrafficStati::NetTrafficStati()
{
    m_pstIpTable = &m_stIpTable;
    m_s32PipeFileID = -1;

    IpTable_Init(m_pstIpTable);
    memset(&m_stFlowCount, 0, sizeof(m_stFlowCount));
}

/**************************************************
Function:      ~NetTrafficStati
Description: 析构函数
Input: none
Output: none
Return: none
Others:
Author: Caibiao Lee
Date:	2019-06-21
*********************************************************/
NetTrafficStati::~NetTrafficStati()
{
    
}

/******************************************************** 
Function:	 NTS_Instance
Description: 获取静态对象
Input:	none
OutPut: none
Return: none
Others: 
Author: Caibiao Lee
Date:	2019-06-21
*********************************************************/
NetTrafficStati *NetTrafficStati::NTS_Instance()
{
    static NetTrafficStati *pClsInstance = NULL;
    if (pClsInstance == NULL) {
        pClsInstance = new NetTrafficStati();
    }
    return pClsInstance;
}

/************************************************* 
Function:    IpTable_Init  
Description: 初始化IP列表
Input:   stIpTable   
Return: stIpTable
Others: 
Author: Caibiao Lee
Date:   2019.06.21
*************************************************/
int NetTrafficStati::IpTable_Init(IP_TABLE_S *stIpTable)
{
    memset((char *)stIpTable, 0, sizeof(IP_TABLE_S));
    stIpTable->u8InitFlag = 0;
    
    return 0;
}

/****************************************************
Function:    IpTable_Check
Description: 检测IpNumIP是否已经存在于IP列表中
Input:  IpNum 需要检测的IP值
Return: 0 IP 列表中没有该IP, 非0,已经存在该IP
Other:
Author: Caibiao Lee
Date:   2019.06.21
*************************************************/
int NetTrafficStati::IpTable_Check(IP_TABLE_S *stIpTable, unsigned int IpNum)
{
    if (NULL == stIpTable) {
        printf("Error: input stIpTable is NULL\n");
        return -1;
    }

    for (int i=0; i<stIpTable->u8UsedIpCount; i++) {
        //已经存在该IP
        if (IpNum == stIpTable->u32IPAddrNum[i]) {
            return -1;
        }
    }
    return 0;
}

/***************************************************
Function: IpTable_Add
Description: 插入一个IP到IP列表中
Input: stIpTable IP 列表,IpNum IP号
Return: 
Others: 0 插入成功,非0,插入失败
Author: Caibiao Lee
Date:   2019.06.21
*************************************************/
int NetTrafficStati::IpTable_Add(IP_TABLE_S *stIpTable, unsigned int IpNum)
{
    if (NULL == stIpTable) {
        printf("Error: input stIpTable is NULL\n");
        return -1;
    }
    //判断IP表是否初始化
    if (1 != stIpTable->u8InitFlag) {
        IpTable_Init(stIpTable);
    }
    //检测IpNum是否已经存在于IP表中
    if (0 == IpTable_Check(stIpTable, IpNum)) {
        //添加IpNum到IP表中
        if (stIpTable->u8UsedIpCount < MAX_IP_COUNT) {
            stIpTable->u32IPAddrNum[stIpTable->u8UsedIpCount] = IpNum;
            stIpTable->u8UsedIpCount++;
            printf("add IP %u OK\n", IpNum);
            return 0;
        } else {
            IpTable_Init(stIpTable);
            stIpTable->u32IPAddrNum[stIpTable->u8UsedIpCount] = IpNum;
            stIpTable->u8UsedIpCount++;
            printf("add IP %u OK \n", IpNum);
            return 0;
        }
    } else {
        printf("ip %d is already set \n", IpNum);
        return -2;
    }

    return -3;
}

/********************************************
Function:    iptables_rulesAdd
Description: 将该IP添加到iptables的规则中
Input: stIpTable IP 列表,IpNum IP号
Return:
Others: 0 插入成功,非0,插入失败
Author: 0 插入成功,非0,插入失败
Date: 2019.06.21
**********************************************/
int NetTrafficStati::iptables_rulesAdd(struct sockaddr_in *addr)
{
    int l_s32Ret = 0;
    char l_arrs8IPdotdec[20] = {0}; //存放点分十进制IP地址
    char l_IptablesCmd[64] = {0};
    
    if (AF_INET != addr->sin_family) //IPv6
    {
        return -1;
    }

    if (0 == IpTable_Add(m_pstIpTable, addr->sin_addr.s_addr)) {
        if (NULL != inet_ntop(AF_INET, &addr->sin_addr, l_arrs8IPdotdec, 16)) {
            //set input rule
            snprintf(l_IptablesCmd, sizeof(l_IptablesCmd), "iptables -I INPUT -d %s ", l_arrs8IPdotdec);
            printf("cmd : %s\n", l_IptablesCmd);
            system(l_IptablesCmd);
        
            //set output rule
            memset(l_IptablesCmd, 0, sizeof(l_IptablesCmd));
            snprintf(l_IptablesCmd, sizeof(l_IptablesCmd), "iptables -I OUTPUT -s %s ", l_arrs8IPdotdec);
            printf("cmd : %s \n", l_IptablesCmd);
            system(l_IptablesCmd);
            
            printf("add IP: %s is OK\n", l_arrs8IPdotdec);
            l_s32Ret = 0;
        }
    } else {
        if (NULL != inet_ntop(AF_INET, &addr->sin_addr, l_arrs8IPdotdec, 16)) {
            printf("IP: %s is in the Iptable \n", l_arrs8IPdotdec);
        }
        l_s32Ret = -1;
    }
    
    return l_s32Ret;
}

/*****************************************
Function: NTS_GetNetWorkCardFlow
Description: 读物网卡流量
Input: u64Count
Output: u64Count
Return: 0成功; 非0, 失败
Others:
Author: Caibiao Lee
Date:   2019.06.21
*************************************************/    
int NetTrafficStati::NTS_GetNetWorkCardFlow(unsigned long long *u64Count)
{
    int l_s32Ret = 0;
    FILE *l_pFd = NULL;
    char *line = NULL;
    size_t len = 0;
    ssize_t read;
    unsigned long long l_u64ReadTx = 0;
    unsigned long long l_u64ReadRx = 0;

    l_pFd = fopen(NETWORK_CARD_TX, "r");

    if (NULL != l_pFd) {
        if ((read = getline(&line, &len, l_pFd)) != -1) {
            sscanf(line, "%lld", &l_u64ReadTx);
            printf("read Tx %lld Byte \n", l_u64ReadTx);
        }
        if (NULL != l_pFd) {
            fclose(l_pFd);
            l_pFd = NULL;
        }
        if (NULL != line) {
            free(line);
            line = NULL;
        }
    } else {
        printf("open %s error !\n", NETWORK_CARD_TX);
        l_s32Ret = -1;
    }

    l_pFd = fopen(NETWORK_CARD_RX, "r");
    if (NULL == l_pFd) {
        if ((read = getline(&line, &len, l_pFd)) != -1) {
            sscanf(line, "%lld", &l_u64ReadRx);
            printf("read Rx %lld Byte \n", l_u64ReadRx);
        }
        if (NULL != l_pFd) {
            fclose(l_pFd);
            l_pFd = NULL;
        }
        if (NULL != line) {
            free(line);
            line = NULL;
        }
    } else {
        printf("open %s error!\n", NETWORK_CARD_TX);
        l_s32Ret = -2;
    }

    *u64Count = l_u64ReadTx + l_u64ReadRx;

    return l_s32Ret;
}

/*****************************************
Function: NTS_ReadIptableIOByte
Description: 读取IPTable统计到的流量
Input: u64Count
Output: none
Return: 0成功; 非0,失败
Others:
Author: Caibiao Lee
Date:   2019.06.21
*************************************************/
int NetTrafficStati::NTS_ReadIptableIOByte(unsigned long long *u64Count)
{
    FILE *fp;
    char *line = NULL;
    size_t len = 0;
    char str1[16] = {0};
    char str2[16] = {0};
    char str3[16] = {0};
	char str4[16] = {0};
	char str5[16] = {0};
	char str6[16] = {0};
    unsigned long long num1 = 0;
    unsigned long long num2 = 0;
    unsigned long long l_u64DataByte = 0;
    ssize_t read;

    //printf("cmd = %s \n", READ_IPTABLE);
    system(READ_IPTABLE);
    fp = fopen(IPTABLE_RES_LINE, "r");
    if (NULL == fp) {
        printf("open file: %s error\n", IPTABLE_RES_LINE);
        return -1;
    }

    while ((read = getline(&line, &len, fp)) != -1) {
        sscanf(line, "%s %s %s %s %lld %s %lld %s ", str1, str2, str3, str4, &num1, str5, &num2, str6);
        l_u64DataByte += num2;
    }
    if (NULL != fp) {
        fclose(fp);
        fp = NULL;
    }

    if (NULL != line) {
        free(line);
        line = NULL;
    }
    
    *u64Count = l_u64DataByte;
    return 0;
}

/*******************************************************
Function:   NTS_GetIpFromPipe
Description: 从管道中获取IP信息,判断是否存在IP列表中,
             如果不存在,这添加进IP列表,并添加Iptables规则
Input: none
Output: none
Return: 0 成功, 非0,失败
Others: 注意这里的管道,在该进程中,以非阻塞的方式打开,打开以后不能关闭操作,如果关闭,写管道会打不开
Author: Caibiao Lee
Date:   2019.06.21
*************************************************/
int NetTrafficStati::NTS_GetIpFromPipe(void)
{
    int l_s32Ret = 0;
    int l_s32PipeFd = -1;
    int l_s32DataLen = -1;
    int l_s32ReadPos = -1;
    int l_s32SendCount = 3;
    char l_arrs8Buffer[128] = {0};
    IP_ADDR_NUM_S l_stIpAddr = {0};
    struct sockaddr_in stIpAdr = {0};
    struct sockaddr_in *stpIpAdr = &stIpAdr;
    
    //判断管道是否存在
    if (access(PIPE_NAME, F_OK) == -1) {
        printf("Create the fifo pipe.\n");
        l_s32Ret = mkfifo(PIPE_NAME, 0777);
        
        if (l_s32Ret != 0) {
            fprintf(stderr, "Could not create fifo %s\n", PIPE_NAME);
            return l_s32Ret;
        }
    }
    
    if (-1 == m_s32PipeFileID) {
        l_s32PipeFd = open(PIPE_NAME, O_NONBLOCK | O_RDONLY);
        m_s32PipeFileID = l_s32PipeFd;
    }
    //以非阻塞的方式去打开管道
    if (m_s32PipeFileID != -1) {
        l_s32DataLen = 0;
        l_s32ReadPos = 0;
        l_s32DataLen = read(m_s32PipeFileID, l_arrs8Buffer, sizeof(l_arrs8Buffer));
        while (l_s32DataLen > 0) {
            if (l_s32DataLen >= sizeof(IP_ADDR_NUM_S)) {
                memcpy(&l_stIpAddr, &l_arrs8Buffer[l_s32ReadPos], sizeof(IP_ADDR_NUM_S));
                l_s32ReadPos += sizeof(IP_ADDR_NUM_S);
                l_s32DataLen -= sizeof(IP_ADDR_NUM_S);
                
                if ((IP_START_FLAG == l_stIpAddr.u8StartFlag) && (IP_END_FLAG == l_stIpAddr, u8EndFlag)) {
                    stIpAddr.sin_family = AF_INET; //设置地址家族
                    //stIpAdr.sin_port = htons(800); //设置端口
                    stIpAdr.sin_addr.s_addr = l_stIpAddr.u32IPAddrNum;   //设置地址
                    //printf("IP adr NUM = %u \n", l_stIpAddr.u32IPAddrNum);
                    iptables_rulesAdd(stpIpAdr);
                    l_s32Ret = 0;
                }
            }
        }
    } else {
        printf("open pipe error\n");
        l_s32Ret = -1;
    }

    //不关闭
    //close(m_s32PipeFileID);
    return 0;
}

/*******************************************
Function: NTS_GetIpFromPipe
Description: 从管道中获取IP信息,判断是否存在IP列表中
            如果不存在,这添加进IP列表,并添加Iptable规则
Input: none
Return:
Others: 该函数需要被业务进程周期调用,建议1S调用一次
Author: Caibiao Lee
Date:   2019.06.21
*************************************************/
int NetTrafficStati::NTS_AddIpToIPTable(void)
{
    NTS_GETIpFromPiPe();
}

/********************************************
Function: NTS_UpdateFlowData
Description: 流量统计更新
Input: none
Return:
Others:该函数需要被业务进程周期调用,建议1S调用一次
Author: Caibiao Lee
Date:   2019.06.21
*************************************************/
int NetTrafficStati::NTS_UpdateFlowData(void) 
{
    int l_s32Ret = 0;
    unsigned long long l_u64NetWorkCardFlow = 0;
    unsigned long long *l_pu64NetWorkCardFlow = &l_u64NetWorkCardFlow;
    
    unsigned long long l_u64IptableIOByte = 0;
    unsigned long long *l_pu64IptableIOByte = &l_u64IptableIOByte;
    
    if (0 == NTS_GetNetWorkCardFlow(l_pu64NetWorkCardFlow)) {
        printf("l_pu64NetWorkCardFlow = %lld\n", l_u64NetWorkCardFlow);
        m_stFlowCount.u64NetWorkCount = l_u64NetWorkCardFlow;
    }
    
    if (0 == NTS_ReadIptableIOByte(l_pu64IptableIOByte)) {
        printf("l_pu64IptableIOByte = %lld\n", l_u64IptableIOByte);
        m_stFlowCount.u64IptableCount = l_u64IptableIOByte;
    }
}

(b)重新封装connet 函数

/****************************************************
*Copyright (C), 2017-2027,lcb0281at163.com lcb0281atgmail.com
*FileName: IPAddrInterFace.cpp
*Date:   2019-06-22
*Author: Caibiao Lee
*Version: V1.0
*Description: IP地址设置和获取模块,注意管道的数据收发规则
*History
******************************************************/
#include <stdio.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>

#include "IPAddrInterFace.h"

/******************************************************
Function: IPAddWriteToPipe
Description: 将IP地址写入管道
Input: u32IpAddr
output: none
Return: 0 成功,非0, 失败
Others:注意命名管道的读写规则
       1.以非阻塞只写方式打开时,在多进程中同时写入数据,注意写入的原子性。
       2.以非阻塞只写方式打开时,如果没有一个进程在读管道,打开会失败
       3.以非阻塞只写方式打开时,如果所有读管道进程关闭,写进程会收到SIGPIPE信号
         如果写进程不对SIGPIPE信号进行处理,会导致写进程退出。
Author: Caibiao Lee
Date:   2019.06.21
*************************************************/
static int IPAddWriteToPipe(unsigned int u32IPAddr)
{
    int l_s32Ret = 0;
    int l_s32PipeFd = -1;
    int l_s32SendCount = 3;
    char l_arrs8Buffer[32] = {0};
    IP_ADDR_NUM_S l_stIpAddr = {0};

    /判断管道是否存在
    if (access(PIPE_NAME, F_OK) == -1) {
        printf("Create the fifo pipe.\n");
        l_s32Ret = mkfifo(PIPE_NAME, 0777);

        if (l_s32Ret != 0) {
            fprintf(stderr, "Could not create fifo %s\n", PIPE_NAME);
            return l_s32Ret;
        }
    }
    //以非阻塞的方式去打开管道
    l_s32PipeFd = open(PIPE_NAME, O_NONBLOCK|O_WRONLY);
    if (l_s32PipeFd != -1) {
        l_stIpAddr.u8StartFlag = IP_START_FLAG;
        l_stIpAddr.u8EndFlag = IP_END_FLAG;
        l_stIpAddr.u32IPAddrNum = u32IPAddr;
        memcpy(l_arrs8Buffer, &l_stIpAddr, sizeof(IP_ADDR_NUM_S));
        
        l_s32Ret = write(l_s32PipeFd, l_arrs8Buffer, sizeof(IP_ADDR_NUM_S));
        if (l_s32Ret == -1) {
            while ((l_s32SendCount--) > 0) {
                sleep(1);
                if (-1 != write(l_s32PipeFd, l_arrs8Buffer, sizeof(IP_ADDR_NUM_S))) {
                    l_s32Ret = 0;
                    break;
                } else {
                    l_s32Ret = -1;
                }
            }
        }
    } else {
        printf("open pipe error\n");
        l_s32Ret = -1;
    }

    close(l_s32PipeFd);
    return l_s32Ret;
}

/**********************************************
Function:   lcb_connect
Description: 重新封装connect函数,与connect函数的应用完全一致
Input: connect系统函数的返回值
Return:
Others: 在这个函数中,将IP地址的十进制数值写入到管道中
        通过wsd_GetIpAddr接口获取IP值,以实现去耦合及进程间通行
Author: Caibiao Lee
Date:   2019.06.21
*************************************************/
int wsd_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
{
    int l_s32Ret = 0;
    unsigned int l_u32IpAddr = 0;
    struct sockaddr_in *l_stAddrIn = NULL;

    l_s32Ret = connect(sockfd, addr, addrlen);
    
    l_stAddrIn = (struct sockaddr_in *)addr;
    l_u32IpAddr = l_stAddrIn->sin_addr.s_addr;
    
    IPAddWriteToPipe(l_u32IpAddr);
    return l_s32Ret;
}

(c)测试进程代码:

/**********************************************
*Copyright (C), 2017-2027,lcb0281at163.com lcb0281atgmail.com
*FileName: Process1_main.cpp
*Date:     2019-06-22
*Author:   Caibiao Lee
*Version:  V1.0
*Description:调用wsd_connect接口将IP地址传递给统计进程
*Others: 
***********************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <signal.h>

#include "IPAddrInterface.h"

#define MAXLINE 256
#define PORT 6666
int fd;
/*
linux ctrl + C会产生SIGINT信号
接收到SIGINT信号进入该函数
*/
void stop(int signo) 
{
    printf("client stop\n");
    close(fd);
    _exit(0);
}

//客户端处理函数
void client_process(void)
{
    char readbuff[MAXLINE];
    char writebuff[MAXLINE];
    char *write = "I am client";
    int num = 0;

    while (1) {
        num = recv(fd, readbuff, MAXLINE, 0); //接收服务端的数据,recv在这里如果没有数据会阻塞
        if (num > 0) {
            printf("client read data : %s \n", readbuff);
            send(fd, write, strlen(write)+1, 0);//接收到数据后再向服务端发送一个字符串
        } else if (num == 0) { //recv返回值为0的时候表示服务端已经断开了连接
            stop(1); //执行退出操作
        }
    }
}

int main(int argc, char **argv)
{
    struct sockaddr_in server_addr;
    struct sockaddr_in client_addr;
    int ret;

    fd = socket(AF_INET, SOCK_STREAM, 0); //建立流式套接字
    if (fd < 0) {
        printf("client socket err\n");
    }

    //设置服务端地址
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET; //AF_INET表示IPv4 Intern协议
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY); //INADDR_ANY 可以监听任意IP
    server_addr.sin_port = htons(PORT);

    inet_pton(AF_INET, "192.168.20.221", &server_addr.sin_addr); //将用户输入的字符串类型的IP地址转为整形
    //connect(fd, (struct sockaddr *)&server_addr, sizeof(server_addr)); //连接服务器
    wsd_connect(fd, (struct sockaddr *)&server_addr, sizeof(server_addr)); //连接服务器
    signal(SIGINT, stop); //注册SIGINT信号
    client_process(); //进入处理函数

    close(fd); //关闭文件
    return 0;
}

完整工程文件结构:

biao@ubuntu:~/test/nettraffic/NetTrafficStatis$ tree
.
├── bin
├── IPAddrInterFace.cpp
├── IPAddrInterFace.h
├── main.cpp
├── Makefile
├── NetTrafficStati.cpp
├── NetTrafficStati.h
├── Process1
│   ├── bin
│   ├── IPAddrInterFace.cpp
│   ├── IPAddrInterFace.h
│   ├── Makefile
│   └── Process1_main.cpp
└── Process2
    ├── IPAddrInterFace.cpp
    ├── IPAddrInterFace.h
    ├── Makefile
    └── Process2_main.cpp
 
4 directories, 14 files
biao@ubuntu:~/test/nettraffic/NetTrafficStatis$ 

工程代码可以从这里获取:《一种基于linux系统的精准流量统计方法》

 

转自:https://blog.csdn.net/li_wen01/article/details/93597936

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值