用C++编写SOCKET控制和接收来自超声机的数据

         前几天接到老师的一个小任务,要求用C++编写一个socket控制超声机并接收来自超声机发送的数据。这个任务师兄本来已经用python完成了,但是我们的总的项目是用C++写的,所以要集成在一起就必须用c++写一个。socket本身比较简单,但我的c++能力一般,socket之前更是没听说过,所以在做的过程中遇到了很多问题,好在费了一番功夫之后解决了问题,实现了要求。在做的过程中在CSDN上查阅了大量的资料,感觉CSDN确实是一个好地方,所以也打算写一次博客记录这次任务,同时也恳请大家批评指正。

1.任务要求

(1)监听端口3001,连接后,接受客户端发送以下两个命令,需以\n结尾
           freeze
           unfreeze
(2)监听端口3000,客户端连接后,实时下持续发送图像数据,数据帧格式为:
    quint32 flag;         //固定值0xAAAA5555
    quint32 time_stamp;      //从解冻开始,以us为单位
    quint32 buf_size;        //固定值640*480
    quint8 buffer[buf_size]; //8位图像像素数据

任务简介:在上面的要求当中,socket的sever端已经被集成在超声机的系统中,采用的是TCP/IP协议,我们无法更改,所以我们只需要实现client端即可,并且采用TCP/IP协议。

2.实现第一个要求

编译环境:vs2010

项目形式:32位控制台应用程序

参考资料链接:

   https://www.cnblogs.com/yuqiao/p/5786427.html

https://blog.csdn.net/xiaoquantouer/article/details/58001960

https://www.cnblogs.com/kefeiGame/p/7246942.html

我的代码:

#include<WINSOCK2.H>
#include<STDIO.H>
#include<iostream>
#include<cstring>
#include<string>
using namespace std;
#pragma comment(lib, "ws2_32.lib")
 
int main()
{
	//初始化WSA,我看别人的代码都加上了这段代码,所以就默认加上了
	WORD sockVersion = MAKEWORD(2, 2);
	WSADATA data;
	if(WSAStartup(sockVersion, &data)!=0)
	{
		return 0;
	}
  
	    //循环创建和关闭socket,并发送指令
	while(true)
	{
		 //创建套接字,采用SOCK_STREAM形式,即TCP协议,保证接收数据的可靠性
		SOCKET sclient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
		if(sclient == INVALID_SOCKET)   //如果没有创建成功,就发出创建失败的信息打印在屏幕上
		{
			printf("invalid socket!");
			return 0;
		}
		
		  //绑定IP和端口
		sockaddr_in serAddr;   //可以理解为类型和变量的关系,实际上就是一个声明
		serAddr.sin_family = AF_INET;
		serAddr.sin_port = htons(3001);   //绑定端口号,即client的端口号是3001
		serAddr.sin_addr.S_un.S_addr = inet_addr("192.168.1.125");//这个按我的理解是绑
                                                   //定服务器的IP地址,就是客户端的目的地
		if(connect(sclient, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR)
		{  // 创建socket并绑定端口号和目的地地址后,要通过connect连接sever端,连接失败 
			printf("connect error !");        //会在屏幕上打印信息
			closesocket(sclient);
			return 0;
		}

        //由于我感觉在屏幕上打英文单词太麻烦,就把用数字来代替指令
		string data;
		int a=0;
		cout<<"please enter a number:1 = freeze and 2 = unfreeze \n ";
		BEGIN:
		cin>>a;
		if(a==1)   //也可以用switch实现
		{
			data="freeze\n";
		}
		else if(a==2)
		{
			data="unfreeze\n";
		}
		else{
			cout<<"enter error!please enter 1 or 2:\n";
			goto BEGIN;
		}
		const char * sendData;
		sendData = data.c_str();   //string转const char* 这是我在网上代码基础上改的,
		                          //我认为直接定义一个字符型数组就可以了,不需要转换
		send(sclient, sendData, strlen(sendData), 0);
		//send()用来将数据由指定的socket传给对方主机
		//int send(int s, const void * msg, int len, unsigned int flags)
		//s为已建立好连接的socket,msg为指向数据内容的指针,len则为数据长度(单位是字节),参数flags一般设0
		//成功则返回实际传送出去的字符数,失败返回-1,错误原因存于error 
		
		
		closesocket(sclient);  //关闭套接字
	}
	
	
	WSACleanup();
	return 0;
	
}
 

 

我的代码比较简单,本身c++水平也比较低,能实现要求我就尽量运用简单的语法。

socket最关键的几个函数就是socket(),bind(),listen(),accept(),send(),recv()等,主要就是了解他们的参数和参数意义,在我上面的链接中有详细介绍,大概看个十几分钟就能现学现用了。

3.实现第二个要求

编译环境:vs2010

实现思路:这部分要求其实就是接受数据而已,用recv()接收数据存在数组里面就OK了,我为了检测数组里面的数据是否正确,就生成了BMP位图,自己写的位图头文件等信息,结果反而走了弯路,浪费了些时间,不过也学到了些知识。

难点:如何定量接收数据。

我的代码;

#include<WINSOCK2.H>
#include<iostream>
#include<cstring>
#include<string>
#include "time.h" 
using namespace std;
#pragma comment(lib, "ws2_32.lib")

int main()
{
	//初始化WSA,固定套路,加上就可以了
	WORD sockVersion = MAKEWORD(2, 2);
	WSADATA data;
	if(WSAStartup(sockVersion, &data)!=0)
	{
		return 0;
	}

	clock_t start, finish; //时间函数变量声明,为了测量程序运行时间
	double   duration;   //持续时间声明
	/*
	{
	BITMAPFILEHEADER fileHead;  //位图头文件,大小14字节
	fileHead.bfType=(WORD)0x4D42;//表明是BMP位图文件
	fileHead.bfSize=308278;//图片总大小:640*480+14+40+1024
	fileHead.bfReserved1=0;//保留为0
	fileHead.bfReserved2=0;//保留为0
	fileHead.bfOffBits=1078;//从位图最开始字节到位图像素数据字节

	BITMAPINFOHEADER infoHead;  //位图信息头
	infoHead.biSize=40;   //信息头大小
	infoHead.biWidth= 640;//宽度
	infoHead.biHeight=480;//高度
	infoHead.biPlanes=1;  //目标设备位平面数,其值设置为1
	infoHead.biBitCount=8;  //每个像素所占位数
	infoHead.biCompression=0;//表示不压缩
	infoHead.biSizeImage=0;   //压缩图像大小的字节数,为压缩图像为0 
	infoHead.biXPelsPerMeter=0;//水平分辨率(像素点每米)默认为0
	infoHead.biYPelsPerMeter=0;//垂直分辨率(像素点每米)默认为0
	infoHead.biClrUsed=256;//调色板颜色数
	infoHead.biClrImportant=0;  //颜色都重要,设为0

	RGBQUAD *ipRGB2 = (RGBQUAD *)malloc(256*sizeof(RGBQUAD));//位图调色板
	for ( int m = 0; m < 256; m++ )
	{                     //灰度图像,红绿蓝颜色分量相等
	ipRGB2[m].rgbRed = ipRGB2[m].rgbGreen = ipRGB2[m].rgbBlue = m;
	ipRGB2[m].rgbReserved=0;}
	}
	*/
    start = clock();   //开始计时
	
		//创建套接字
		SOCKET sclient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
		if(sclient == INVALID_SOCKET)
		{
			printf("invalid socket!");
			return 0;
		}

		//绑定IP和端口
		sockaddr_in serAddr;
		serAddr.sin_family = AF_INET;
		serAddr.sin_port = htons(3000);//端口号
		serAddr.sin_addr.S_un.S_addr = inet_addr("192.168.1.125");//绑定服务器IP
		if(connect(sclient, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR)
		{  //连接失败 
			printf("connect error !");
			closesocket(sclient);
			return 0;
		}

		char PICTUREDATA[307200];  //设置缓存数组,长度307200,640*480

			//int i=1;  //为了循环使用
      // while(i<=1)   //不循环可以去掉这个while
		//{   
			//cout<<i<<endl;

			//i++;


			char HeadData[12];   //存储头信息,包括flag,时间戳,图像大小
			recv(sclient, HeadData, 12, 0);  //接收数据头信息,包括标志位、时间戳、大小

			int n=0;
			char PictureData[3072];
			int k = 0;

			while(n<307200)   //循环接收一幅图像的像素数据,严格保证接收307200个字节数据
			{                //并存在数组中

				int a = recv(sclient,PictureData, 3072, 0);
				// cout<<a<<" ";  //a是实际接收的字节数,不要以为指定了3072个字节它的服务器
                                 //端就真的每次发送3072个字节

                while(k<a)
				{
					PICTUREDATA[k+n] = PictureData[k];
					k++;
				}
				k = 0;

				n += a;
				// cout<<n<<endl;

				if((0!=(307200-n))&&((307200-n)<3072))
				{
					a = recv(sclient,PictureData, (307200-n), 0);
					while(k<a)
					{
						PICTUREDATA[k+n] = PictureData[k];
						k++;
					}
					n = 307200;

				}

			}
			
		//}

	closesocket(sclient);  //关闭SOCket
	WSACleanup();	

	finish = clock(); //接收一幅图片数据的时间
	duration = (double)(finish - start) / CLOCKS_PER_SEC; 
	printf( "%f seconds\n", duration ); 


	//char FileName[40];//存文件名的数组
    //sprintf_s(FileName,"F:\\Socket\\data%d.bmp",1); //文件名
   //FILE* fp=fopen(FileName,"wb+");  //以可读写方式创建文件

	//fwrite (&fileHead, sizeof(BITMAPFILEHEADER), 1, fp );//写入位图头文件

	 //fwrite (&infoHead, sizeof( BITMAPINFOHEADER), 1, fp );//写入位图头信息

    //fwrite (ipRGB2, 1024, 1, fp );//写入位图调色板

	//fwrite (PICTUREDATA, sizeof(char), 307200, fp );//写入数据信息

	//fclose(fp);  //关闭文件
	return 0;
}

BMP位图链接:https://blog.csdn.net/caicai_zju/article/details/51008892

一个隐形BUG:https://blog.csdn.net/cnhk1225/article/details/77883701

fopen,fwrite用法:https://blog.csdn.net/yang2011079080010/article/details/52528261

在windows平台下写数据时一定要用二进制形式写数据,否则会莫名其妙多若干个字节,这是我认为的一个隐形bug,坑了我整整两天,写成位图时莫名其妙多几百个字节的数据,还好我用UltralEdit发现了,其实还是我编程经验少。其实要是不写成文件的话就不会多字节。

我写的这个程序最后会封装成一个函数,在项目的大程序里调用一下就存一个图片的数据,所有有些代码还没有添加上。

关于socket我认为还是比较简单的,网上的很多代码只需要简单修改就可以了,主要是对socket的几个函数参数多了解就行了。我也只是对应用有所了解,对底层原理我不太了解,所以有些叙述可能有错误,欢迎大家批评指正。

最后要说的是我用的是vs2010写的代码,运行是没问题的,但是在2015平台上有个地方会报错,不过不用担心,把报错信息百度下就有结果了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值