龙空技术网

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

liwen01001 85

前言:

而今兄弟们对“服务器流量统计软件有哪些”大致比较关切,大家都想要剖析一些“服务器流量统计软件有哪些”的相关知识。那么小编同时在网上网罗了一些对于“服务器流量统计软件有哪些””的相关资讯,希望朋友们能喜欢,姐妹们快快来学习一下吧!

iwen01 2019.06.15

前言:

在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的统计规则中,如果有收到一个添加规则的请求,则判断该规则是否已经添加进Iptable中,如果没有则添加,如果有则放弃此次规则的添加。在其他的进程中,比如进程B,C,D,E...,在进程网络连接的时候,根据需求将需要统计的IP或是端口信息,发送给进程A,让进程A去进行iptables规则的添加。

另外,在进程A中,可以循环的去获取Iptables统计的流量,还可以实时的去获取网卡实际收发的数据,实现网络流量的实时更新。进程A,B,C,D,E之间,可以使用进程间通信的任意一种,这里为了方便扩展,使用了命名管道进程通信。为了方便数据检验传递和处理,在进程间传递的IP地址可以转换为数值二不是字符串,实际在connet函数建立网络连接的时候,使用的也是一个32位的int类型数据来表示IP地址。

功能实现:(1)iptables规则添加

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

    /**统计IP 192.168.20.38 的数据输入**/    iptables -I INPUT  -d 192.168.20.38    /***统计IP 192.168.20.38 的数据输出**/    iptables -I OUTPUT -s 192.168.20.38

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

Chain INPUT (policy ACCEPT)target     prot opt source               destination                    all  --  anywhere             192.168.20.38       Chain FORWARD (policy ACCEPT)target     prot opt source               destination         Chain OUTPUT (policy ACCEPT)target     prot opt source               destination                    all  --  192.168.20.38        anywhere    
(2)Iptable的流量查看:

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

biao@ubuntu:~/test/nettraffic/NetTrafficStatis/Process1$ sudo iptables -n -v -L -t filter -xChain INPUT (policy ACCEPT 5147 packets, 654051 bytes)    pkts      bytes target     prot opt in     out     source               destination                0        0            all  --  *      *       0.0.0.0/0            192.168.20.38       Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)    pkts      bytes target     prot opt in     out     source               destination         Chain OUTPUT (policy ACCEPT 2810 packets, 5760828 bytes)    pkts      bytes target     prot opt in     out     source               destination                0        0            all  --  *      *       192.168.20.38        0.0.0.0/0           biao@ubuntu:~/test/nettraffic/NetTrafficStatis/Process1$ 
(3)网卡流量查看:

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

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

查看本次开机网卡总共接收的数据量:

biao@ubuntu:~/test/nettraffic/NetTrafficStatis/Process1$ cat /sys/class/net/ens33/statistics/rx_bytes6351838biao@ubuntu:~/test/nettraffic/NetTrafficStatis/Process1$
(4)进程间通行

这里使用的是命名管道,在使用命名管道的时候,需要注意管道的阻塞和非阻塞模式,这里接收和发送都是采用的非阻塞模式,另外,还需要注意管道的收发数据规则。

在接收进程从,非阻塞方式打开,可以正常打开,接收数据也会立即返回。在发送端非阻塞方式打开,如果这条管道,没有一个进程在接收,那么在发送端打开管道会返回失败。

如果有多个进程进程进行数据写入,但是只有一个进程在进行读操作的时候,要注意读写数据的原子性操作。

我们这里是针对特定IP进程流量的统计,在不同进程之间,我们需要把Ip地址信息发送给进程A进程Iptable规则的添加,这里我们使用的是重新封装connet函数,在connet函数中,我们可以获取到需要建立网络链接的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 <fcntl.h>#include <unistd.h>#include <sys/types.h> #include <sys/stat.h>#include "IPAddrInterFace.h"#include "NetTrafficStati.h"#define IPTABLE_RES_FILE 	"/tmp/iptable_res"#define READ_IPTABLE		"iptables -n -v -L -t filter -x | grep -E \"Chain OUTPUT|Chain INPUT\">/tmp/iptable_res"/******************************************************** Function:	 NetTrafficStatiDescription: 构造函数Input:	noneOutPut: noneReturn: noneOthers: Author: Caibiao LeeDate:	2019-06-21*********************************************************/NetTrafficStati::NetTrafficStati(){	m_pstIpTable = &m_stIpTable;	m_s32PipeFileID = -1;		IpTable_Init(m_pstIpTable);	memset(&m_stFlowCount,0,sizeof(m_stFlowCount));}/******************************************************** Function:	 ~NetTrafficStatiDescription: 析构函数Input:	noneOutPut: noneReturn: noneOthers: Author: Caibiao LeeDate:	2019-06-21*********************************************************/NetTrafficStati::~NetTrafficStati(){}/******************************************************** Function:	 NTS_InstanceDescription: 获取静态对象Input:	noneOutPut: noneReturn: noneOthers: Author: Caibiao LeeDate:	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: stIpTableOthers: Author: Caibiao LeeDate:   2019.06.21*************************************************/int NetTrafficStati::IpTable_Init(IP_TABLE_S *stIpTable){	memset((char*)stIpTable,0,sizeof(IP_TABLE_S));		stIpTable->u8InitFlag = 1;	return0;}/************************************************* Function:    IpTable_Check  Description: 检测IpNumIP是否已经存在于IP列表中Input:   IpNum 需要检测的IP值   Return:  0 IP 列表中没有该IP,非0,已经存在该IPOthers: Author: Caibiao LeeDate:   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;		}	}		return0;}/************************************************* Function:    IpTable_Add  Description: 插入一个IP到IP列表中Input: stIpTable IP 列表, IpNum IP 号Return:Others: 0 插入成功,非0,插入失败Author: Caibiao LeeDate:   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);			return0;		}else		{			IpTable_Init(stIpTable);			stIpTable->u32IPAddrNum[stIpTable->u8UsedIpCount] = IpNum;			stIpTable->u8UsedIpCount++;				printf("add IP %u OK \n",IpNum);			return0;		}	}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: Caibiao LeeDate:   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: u64CountOutput:u64CountReturn: 0 成功,非0,失败Others: Author: Caibiao LeeDate:   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;	unsignedlonglong l_u64ReadTx = 0;	unsignedlonglong 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: u64CountOutput:noneReturn:0 成功,非0,失败Others:Author: Caibiao LeeDate:   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};	unsignedlonglong num1 = 0;	unsignedlonglong num2 = 0;	unsignedlonglong l_u64DataByte = 0;	ssize_t read;		//printf("cmd = %s \n",READ_IPTABLE);	system(READ_IPTABLE);	    fp = fopen(IPTABLE_RES_FILE, "r");    if (NULL==fp)	{		printf("open file: %s error \n",IPTABLE_RES_FILE);		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;		return0;}/************************************************* Function:    NTS_GetIpFromPipe  Description: 从管道中获取IP信息,判断是否存在IP列表中	如果不存在,这添加进IP列表,并添加Iptable规则Input: noneOutput:noneReturn:0 成功,非0,失败Others: 注意这里的管道,在该进程中,以非阻塞的方式打开	,打开以后不能关闭操作,如果关闭,写管道会打不开Author: Caibiao LeeDate:   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))				{					stIpAdr.sin_family = AF_INET;  //设置地址家族					//stIpAdr.sin_port = htons(800);  //设置端口					stIpAdr.sin_addr.s_addr = l_stIpAddr.u32IPAddrNum;  //设置地址					//printf("IP ddr NUM = %u \n",l_stIpAddr.u32IPAddrNum);					iptables_rulesAdd(stpIpAdr);					l_s32Ret = 0;				}			}		}    }	else	{		printf("open pipe errror !\n");		l_s32Ret  = -1;	}	/**不关闭**/	//close(m_s32PipeFileID);	return0;}/************************************************* Function:    NTS_GetIpFromPipe  Description: 从管道中获取IP信息,判断是否存在IP列表中	如果不存在,这添加进IP列表,并添加Iptable规则Input: noneReturn:Others: 该函数需要被业务进程周期调用,建议1S调用一次Author: Caibiao LeeDate:   2019.06.21*************************************************/int NetTrafficStati::NTS_AddIpToIPTable(void){	NTS_GetIpFromPiPe();	}/************************************************* Function:    NTS_UpdateFlowData  Description: 流量统计更新Input: noneReturn:Others: 该函数需要被业务进程周期调用,建议1S调用一次Author: Caibiao LeeDate:   2019.06.21*************************************************/int NetTrafficStati::NTS_UpdateFlowData(void){	int l_s32Ret = 0;	unsignedlonglong l_u64NetWorkCardFlow  = 0;	unsignedlonglong *l_pu64NetWorkCardFlow = &l_u64NetWorkCardFlow;	unsignedlonglong l_u64IptableIOByte  = 0;	unsignedlonglong *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 <stdlib.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: u32IPAddroutput:noneReturn:0 成功,非0,失败Others:注意命名管道的读写规则	1.以非阻塞只写方式打开时,在多进程中同时写入数据,注意写入的原子性。	2.以非阻塞只写方式打开时,如果没有一个进程在读管道,打开会失败。	3.以非阻塞只写方式打开时,如果所有读管道进程关闭,写进程会收到SIGPIPE信号	   如果写进程不对SIGPIPE信号进行处理,会导致写进程退出。Author: Caibiao LeeDate:   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 errror !\n");		l_s32Ret  = -1;	}		close(l_s32PipeFd);		return l_s32Ret;}/************************************************* Function:    lcb_connect  Description: 重新封装connet函数,与connect函数的应用			 完全一致Input: connect 系统函数的返回值Return:Others: 在这个函数中,将IP地址的十进制数值写入到管道中	通过wsd_GetIpAddr接口获取IP值,以实现去耦合及进程间	通行Author: Caibiao LeeDate:   2019.06.21*************************************************/int wsd_connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen){	int l_s32Ret = 0;	unsignedint 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    6666int 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); /*接收到数据后再向服务端发送一个字符串*/		}		elseif(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("clinet 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);/*关闭文件*/	return0;}
工程下载:

完整工程文件结构:

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.cpp4 directories, 14 filesbiao@ubuntu:~/test/nettraffic/NetTrafficStatis$ 

liwen01 公众号中回复 网络编程 获取工程代码,本章代码工程名为:NetTrafficStatis.tar.gz

--------------End--------------

如需获取更多内容

请关注公众号 liwen01

标签: #服务器流量统计软件有哪些 #linux怎么看流量 #ubuntu 网口流量统计 #ubuntu查看流量 #ubuntu 流量统计