Socket编程与应用开发---- 数据采集的设计与实现(TCP)

​​​​​​

代码

Server:
#include <stdio.h>
#include <winsock2.h>
#pragma comment(lib, "WS2_32")	// 链接到WS2_32.lib
#include <stdlib.h>
#include <ctime>//随机数头文件
 
    这是两个函数!一个是srand函数!这是在调用rand    ()这个函数之前使用的! rand()是一个产生随机数的函数!而srand是一个设置随机数种子的函数! 通常这两个函数是一起使用的!来完成产生随机数的功能!  而time(NULL)这个函数的返回值是作为srand函数的参数的!意思是以现在的系统时间作为随机数的种子来产生随机数!至于NULL这个参数。只有设置成NULL才能获得系统的时间!
 
生成随机数据,模拟采集的设备信息
int Rand()  
 {   srand(time(NULL));
	int j ;	
	j = rand()%1015448481651 ;
	return j;	
}
char buff[1000]={0};
此函数用来生成模拟设备信息的函数
char *creat()                          
{ 
   int num=Rand();                      //将生成的随机数,传给sum
   char *translate=(char *)malloc(1000);//向系统申请一个堆空间,malloc的返回值类型是void*,需要用(char*)来强转类型以和等号左边匹配
   memset(translate,0,1000);            //清空这个区域
   char m[100]={0};
   sprintf(m,"%d",num);                //把格式化的数据写入某个字符串缓冲区
   memcpy(buff,m,sizeof(buff));
   printf("采集到的数据%s\n\n", buff);//以字符串形式打印输出变量str后光标换行
   return(buff);
}
void main()
{	
    printf("TCP服务器(采集点)\n\n\n\n"); 
	SOCKET sock1,sock2;
	WSADATA wsadata;
	struct sockaddr_in sai;
	int count;
	//地址初始化
	sai.sin_family = AF_INET;
	sai.sin_port = htons(4777);
	sai.sin_addr.s_addr = inet_addr("127.0.0.1");
	//连接
	printf("================等待客户端启动==================\n");
	printf("\n\n\n\n");
	WSAStartup(MAKEWORD(1,1),&wsadata);//加载套接字库和版本协商
	sock1=socket(AF_INET,SOCK_STREAM,0);
	bind(sock1,(struct sockaddr*)&sai,sizeof(sai));
	listen(sock1,2);
	count=sizeof(sai);
	sock2=accept(sock1,(struct sockaddr*)&sai,&count);//接收并保存客户端信息
	printf("=============已与客户端连接========================\n");
	for( int i=0;i<5;i++)
	{
	    *creat();
		send(sock2,buff,sizeof(buff),0);
		Sleep(2000);
	}
	closesocket(sock1);
	closesocket(sock2);
	WSACleanup();
    printf("\n=====================关闭========================\n"); 
}


Client:
//TCP客户端
#pragma comment(lib,"WS2_32")
#include"winsock.h"
#include"stdio.h"
#include <windows.h>
    声明IP头数据结构,ICMP协议是IP层的一个协议,但是由于差错报告在发送给报文源发方时可能也要经过若干子网,因此牵涉到路由选择等问题,所以ICMP报文需通过IP协议来发送。
//ICMP数据报的数据发送前需要两级封装:首先添加ICMP报头形成ICMP报文,再添加IP报头形成IP数据报
typedef struct _IPHeader  // 20字节的IP头
{
    UCHAR     iphVerLen;      // 版本号和头长度(各占4位)
    UCHAR     ipTOS;          // 服务类型 
    USHORT    ipLength;       // 封包总长度,即整个IP报的长度
    USHORT    ipID;	          // 封包标识,惟一标识发送的每一个数据报
    USHORT    ipFlags;	      // 标志和片偏移
    UCHAR     ipTTL;	      // 生存时间,就是TTL,指出IP数据报能在网络上停留的最长时间,其值由发送方设定,并在经过路由的每一个节点时减一,当该值为0时,数据报将被丢弃
    UCHAR     ipProtocol;     // 协议,可能是TCP、UDP、ICMP等
    USHORT    ipChecksum;     // 校验和
    ULONG     ipSource;       // 源IP地址
    ULONG     ipDestination;  // 目标IP地址
} IPHeader, *PIPHeader; 

//ICMP头数据结构
typedef struct icmp_hdr
{
    unsigned char   icmp_type;		// 消息类型
    unsigned char   icmp_code;		// 代码
    unsigned short  icmp_checksum;	// 校验和
    unsigned short  icmp_id; // 用来惟一标识此请求的ID号,通常设置为进程ID
    unsigned short  icmp_sequence;	// 序号
    unsigned long   icmp_timestamp; // 数据传输时间
}ICMP_HDR, *PICMP_HDR;

//ICMP回送请求的数据结构
typedef struct _EchoRequest
{
	ICMP_HDR icmphdr;
	char cData[32];
}ECHOREQUEST,*PECHOREQUEST; 

//ICMP回送应答的数据结构
#define REQ_DATASIZE 32
typedef struct _EchoReply
{
	IPHeader iphdr;
	ECHOREQUEST echoRequest;
}ECHOREPLAY,*PECHOREPLAY; 
// 校验和的计算
// 以16位的字为单位将缓冲区的内容相加,如果缓冲区长度为奇数,
// 则再加上一个字节。它们的和存入一个32位的双字中
//USHORT checksum(USHORT* buff, int size);
USHORT checksum(USHORT* buff, int size)
{	u_long cksum = 0;
	while(size>1)                        // 将数据以字为单位累加到cksum 中
    {
		cksum=cksum+ *buff;  
        buff= buff +1;
		size= size- sizeof(USHORT); //等价于size=size-2;
	}  
	if(size==1)                    // 共有奇数个字节将最后一个字节扩展为字,再累加
      {                      
		USHORT u=0;
		u=(USHORT)(*(UCHAR*)buff);
		cksum = cksum +u;	
      }
	// 校验位计算
	//高16位和低16位相加
	cksum = (cksum >> 16) + (cksum & 0x0000ffff); 
	cksum = cksum+(cksum >> 16);//本身再加上当前的高16位 
    u_short  answer=(u_short)(~cksum);//取反并转换为16位数
	return (answer);
}
void Sock();
int getmax(int backTime[],int n) // 计算最大时间
{
	int nmax = backTime[0];
	for(int i=0;i<n;i++)
	{
		if(nmax < backTime[i])
		{
			nmax = backTime[i];
		}
	}
	return nmax;
}



int getmin(int backTime[],int n) // 计算最小时间
{
	int nmax = backTime[0];
	for(int i=0;i<n;i++)
	{
		if(nmax > backTime[i])
		{
			nmax = backTime[i];
		}
	}
	return nmax;
}

int getavg(int backTime[],int n) // 计算平均值
{
	int nmax = 0;
	for(int i=0;i<n;i++)
	{
		nmax += backTime[i];
	}
	return nmax/n;
}

void ping(){
	WSADATA wsaData;
	WORD version = MAKEWORD(2, 2);
    WSAStartup(version, &wsaData); //载入Winsock库	
	char DestIp[] = "0";      // 目的IP地址,即要Ping的IP地址
	printf("请输入需要Ping的IP地址:");
	gets(DestIp);
	printf("\n正在创建套接字..............................\n");
	SOCKET sRaw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
	SOCKADDR_IN dest;         // 设置目的地址
	dest.sin_family = AF_INET;
	dest.sin_port = htons(0); // 0表示程序执行时系统自动分配一个
	dest.sin_addr.S_un.S_addr = inet_addr(DestIp);
	ECHOREQUEST echoReq;      // 创建ICMP封包(回送请求)
	echoReq.icmphdr.icmp_type = 8;
    echoReq.icmphdr.icmp_code = 0;
	echoReq.icmphdr.icmp_id = (USHORT)GetCurrentProcessId();
	echoReq.icmphdr.icmp_checksum = 0;
	echoReq.icmphdr.icmp_sequence = 0;
	// 填充数据部分,可以为任意
	memset(&echoReq.cData, 'E', 32);//Sets buffers to a specified character.
	// 开始发送和接收ICMP封包
	USHORT   nSeq = 0;    
	SOCKADDR_IN from;
	struct timeval timeout;
	timeout.tv_sec = 100000;
	timeout.tv_usec = 0;
	int nLen = sizeof(from);
	int sendCount = 0; // 发送的数据包数
	int backCount = 0;// 返回的数据包个数;
	int backTime[4] = {0,0,0,0}; // 返回的数据包用的时间
	int backTime_index = 0;// 数组游标
	printf("\n正在Ping :%s\n",DestIp);
	int nCount = 0;
	
	while(TRUE){
	    //int nCount = 0;
		int nRet;
		if(nCount++ == 4)   break;
		echoReq.icmphdr.icmp_checksum = 0;
		echoReq.icmphdr.icmp_timestamp = GetTickCount();	// GetTickCount() 系统开始后,已经经过的毫秒数
		echoReq.icmphdr.icmp_sequence = nSeq++;
		echoReq.icmphdr.icmp_checksum = checksum((USHORT*)&echoReq, sizeof(echoReq));
		nRet = sendto(sRaw, (char*)&echoReq, sizeof(echoReq), 0,(SOCKADDR *)&dest, sizeof(dest));
		
		 if(nRet == SOCKET_ERROR){
			printf(" sendto() failed: %d \n", WSAGetLastError());
			return ;
		}
		else
		{
			sendCount++; // 发送的数据包加1,
		}
			
		ECHOREPLAY echoReply;//接收回送应答包
		nRet = recvfrom(sRaw, (char*)&echoReply,sizeof(ECHOREPLAY), 0,(sockaddr*)&from, &nLen);
		if (nRet == SOCKET_ERROR)
		{
			printf(" recvfrom() failed: %d\n", WSAGetLastError());
			return ;
        }
		else
		{
			backCount++;
		}
		// 下面开始解析接收到的封包
		if(nRet < sizeof(ECHOREPLAY)){
			printf(" Too few bytes from %s \n", inet_ntoa(from.sin_addr));
		}
		// 接收到的数据中包含IP头,IP头大小为20个字节
		if(echoReply.echoRequest.icmphdr.icmp_type != 0) { // 回显
			printf(" nonecho type %d 
                 recvd \n",echoReply.echoRequest.icmphdr.icmp_type);
			return ;
		}
		if(echoReply.echoRequest.icmphdr.icmp_id!=    GetCurrentProcessId())
{
			printf(" someone else's packet! \n");
			return ;
		}
		printf(" %d bytes Reply from %s:", 
                    nRet,inet_ntoa(from.sin_addr));
		printf(" icmp_seq = %d. ",echoReply.echoRequest.icmphdr.icmp_sequence);
		int nTick = GetTickCount();
		int time = nTick - echoReply.echoRequest.icmphdr.icmp_timestamp;
		printf(" time: %d ms",time);
		backTime[backTime_index] = time;
		backTime_index++;
		printf(" TTL= %d ", echoReply.iphdr.ipTTL);
		printf(" \n");		
        Sleep(1000);
	}

	// 显示统计信息
	printf("================Ping 的统计信息=================\n");
	double ratio = (double)(sendCount-backCount)/(double)sendCount;
	printf("\n数据包:\n已发送 = %d ,\n已接收 = %d ,\n丢失 = %d ( %.0f%% 丢失)\n",sendCount,backCount,sendCount-backCount,ratio*100);
	printf("往返行程的估计时间:\n");
	printf("\n最短 = %d ms,\n最长 = %d ms,\n平均 = %d ms\n",getmin(backTime,backCount),getmax(backTime,backCount),getavg(backTime,backCount));
	closesocket(sRaw);
	WSACleanup();//释放Winsock库
	
    //建立服务器连接

}

void sock(){      //建立与客户端的连接
    SOCKET sock;
	WSADATA wsadata;
	char buff[1000];
	struct sockaddr_in sai;
	WSAStartup(MAKEWORD(1,1),&wsadata);//加载套接字库和版本协商
	sock=socket(AF_INET,SOCK_STREAM,0);
	sai.sin_family = AF_INET;
	sai.sin_port = htons(4777);
	sai.sin_addr.s_addr = inet_addr("127.0.0.1");
	connect(sock,(struct sockaddr*)&sai,sizeof(sai));
   printf("=================接受数据========================\n\n\n");
	while(true)
	{
	     int i=1;
        for(i=1;i<=5;i++)    //建立一个for循环,接受5次数据
	   {
		  recv(sock,buff,1000,0);
	      printf("第%d个电脑的数据为%s\n\n",i,buff);   
	   }
      break;
	}
	closesocket(sock);
	WSACleanup();

}

void main(){
    printf("客户机程序(数据中心)\n\n\n\n");

	for(int i=0;i<5;i++)   //ping5次
	{
      ping();
	}
    printf("是否接受数据:");  //建立一个判断语句,使用户确定是否接受数据
	int chose;
	scanf("%d",&chose); 	
	if(chose=1)               //黑窗口输入1,接受数据
	 {
printf("=====================客户端启动========================\n");
       sock();	
	 }
}

​​​​​​程序运行

客户端先ping5次

平后,客户端确定,是否接受服务端的数据

确定接受后,客户端显示接受到的数据

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值