使用protobuf和socket实现服务器间消息的传递

传递的消息是简单的char*的话,客户端和服务器端以及protoc文件分别如下:

//.protoc文件,包含的信息是身份证号,姓名和生日
package tutorial;
message Person
{
	required string id = 1;
	required string name = 2;
	required int32 birthyear=3;
    required int32 birthmonth=4;
    required int32 birthday=5;

}
//客户端
#pragma warning(disable : 4996)
#include<iostream>
#include "protobuf_test.pb.h"
#include "socket/wsock.h"
//#include<winsock.h>
#include <io.h>
#include <fstream>
#include "Base64.h"

#pragma comment(lib,"ws2_32.lib")
#pragma comment(lib,"libprotobuf.lib")
#pragma comment(lib,"libprotoc.lib")

using namespace std;
using namespace tutorial;

void initialization();
int setPerson();
string getImg();

//定义长度变量
int send_len = 0;
int recv_len = 0;
//定义发送缓冲区和接受缓冲区
char send_buf[1024 * 1024 * 5];
char recv_buf[100];
char ImgBuff2[10000000];

int main()
{
	//定义服务端套接字,接受请求套接字
	SOCKET soc_server;
	//服务端地址客户端地址
	SOCKADDR_IN server_addr;

	cout << "---------client---------" << endl << endl;

	//初始化套接字库
	initialization();

	//填充服务端信息
	server_addr.sin_family = AF_INET;//代表TCP/IP协议族
	server_addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");//IP地址,sin_addr存储IP地址,使用in_addr这个数据结构
	server_addr.sin_port = htons(5010);//端口号
	// htons函数:将一个16位无符号短整型数据由主机排列方式转化为网络排列方式,htonl函数的作用恰好相反。

	//创建套接字
	/*
	SOCKET socket(int af,int type,int protocol);
	af:一个地址家族,通常为AF_INET
	type:套接字类型,SOCK_STREAM表示创建面向流连接的套接字。为SOCK_DGRAM,表示创建面向无连接的数据包套接字。为SOCK_RAW,表示创建原始套接字
	protocol:套接字所用协议,不指定可以设置为0
	返回值:socket
	*/
	soc_server = socket(AF_INET, SOCK_STREAM, 0);

	//向服务器发出连接请求
	/*
	connect函数:客户端socket发送连接请求的函数,
	第一个参数是客户端的socket,
	第二个参数是一个结构体指针,里面包括连接主机的地址和ip,
	第三个参数为缓冲区的长度。
	成功返回0,出错返回-1
	*/
	while (connect(soc_server, (SOCKADDR *)&server_addr, sizeof(SOCKADDR)) == -1)
	{
		cout << "服务器连接失败!" << endl;
		//WSACleanup();
		Sleep(2000);
	}
// 	else
// 	{
// 		cout << "服务器连接成功!" << endl;
// 	}

	//发送数据
	while (1)
	{
		int len = setPerson();
		if (len < 0){
			return -1;
		}
		send_len = send(soc_server, send_buf, len, 0);
		if (send_len < 0)
		{
			cout << "发送失败!" << endl;
			break;
		}
		printf("send %d bytes.\n", send_len);
		recv_len = recv(soc_server, recv_buf, 100, 0);
		if (recv_len < 0)
		{
			cout << "接受失败!" << endl;
			break;
		}
		else
		{
			cout << "服务端信息:" << recv_buf << endl;
		}

	}

	//关闭套接字
	closesocket(soc_server);
	//释放DLL资源
	WSACleanup();
	return 0;
}

//初始化套接字库
void initialization()
{
	WORD w_req = MAKEWORD(2, 2);//版本号
	WSADATA wsadata;
	
	//检查初始化套接字库
	 if (WSAStartup(w_req, &wsadata) != 0)
	{
		cout << "初始化套接字库失败!" << endl;
	}
// 	else
// 	{
// 		cout << "初始化套接字库成功!" << endl;
// 	}

	//检测版本号
	if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wHighVersion) != 2)
	{
		cout << "套接字库版本号不符!" << endl;
		WSACleanup();
	}
// 	else
// 	{
// 		cout << "套接字库版本正确!" << endl;
// 	}
	
}

//protobuf序列化信息
int setPerson(){
	Person p;
//	cout << "输入身份证号:";
//	char id[220];
// 	cin >> id;
// 	p.set_id(id);
//	cin.ignore(256, '\n');
	

//	cout << "输入姓名:";
// 	string name;
// 	getline(cin, *p.mutable_name());
//	cin.ignore(256, '\n');


// 	cout << "输入出生日期:";
// 	cout << endl << "年:";
// 	int year;
// 	cin >> year;
// 	p.set_birthyear(year);
// 	cin.ignore(256, '\n');
	

// 	cout << endl << "月:";
// 	int month;
// 	cin >> month;
// 	p.set_birthmonth(month);
// 	cin.ignore(256, '\n');
// 	
// 	cout << endl << "日:";
// 	int day;
// 	cin >> day;
// 	p.set_birthday(day);
// 	cin.ignore(256, '\n');

	p.set_id("510122199611147760");
	p.set_name("gugu");
	p.set_birthyear(1996);
	p.set_birthmonth(11);
	p.set_birthday(14);

	p.set_imgname("sichuanuniversity");
	string data = getImg();
	p.set_imgdata(data);
	int proto_len = p.ByteSize();/*
	if (proto_len >= sizeof(send_buf))
		exit(0);*/
	assert(proto_len < sizeof(send_buf));
	bool bret = p.SerializeToArray(send_buf, proto_len);
	//assert(bret);
	if (!bret){
		fprintf(stderr, "SerializeToArray failed\n");
		return -1;
	}
	return proto_len;
}

//从本地获取图片 加密图片
string getImg()
{
	string ImgName = "sichuanuniversity.jpg";
	ifstream StreamImg;
	StreamImg.open(ImgName.c_str(), ios_base::in | ios_base::out | ios_base::binary);//第一个参数是打开文件名字,第二个是打开的模式:ios_base::binary是二进制模式,ios_base::in是以读的方式打开
	StreamImg.seekg(0, ios::end);//seekg作用是输入指针移到指定的文件位置,第一个参数是需要偏移的值(正数向后偏移),第二个参数是搜索的位置(ios::end输入流结束位置,ios::beg开始位置,ios::cur当前位置)
	int ImgSize = StreamImg.tellg();//tellg()不需要带参数,它返回当前定位指针的位置,也代表着输入流的大小
	printf("img size: %d\n", ImgSize);
	char* ImgBuff = new char[ImgSize + 1];//图片的缓存区
	string _base64;
	Base64 encrypt;//加密
	StreamImg.seekg(0, ios::beg);//输入指针指向开始位置
	StreamImg.read(ImgBuff, ImgSize);//ImgSize个字符到ImgBuff这个缓冲区

	_base64 = encrypt.Encode((unsigned char*)ImgBuff, ImgSize + 1);//加密
	int eLen = _base64.length();//加密之后的长度

	memcpy_s(ImgBuff2, sizeof(ImgBuff2), _base64.c_str(), eLen);//四个参数(目的地址,目的地址字符个数(sizeof获取),源地址,需要拷贝的原字符个数)  是一个内存复制函数

	StreamImg.close();
	delete ImgBuff;
	return ImgBuff2;//返回加密之后的内存地址

}

//服务器端
#pragma warning(disable : 4996)
#include<iostream>
#include<winsock.h>
#include "base64.h"
#include "protobuf_test.pb.h"
#include <fstream>
#include <stdlib.h>   
#include <stdio.h>
#include<cstdlib>
#include <io.h>
#include<typeinfo>

#pragma comment(lib,"libprotobuf.lib")
#pragma comment(lib,"libprotoc.lib")
#pragma comment(lib,"ws2_32.lib")


using namespace std;
using namespace tutorial;


void initialization();

//定义长度变量
int send_len = 0;
int recv_len = 0;
int len = 0;

//定义发送缓冲区和接受缓冲区
char send_buf[100];
char recv_buf[1024 * 1024];


int main() {
	
	//定义服务端套接字,接受请求套接字
	SOCKET soc_server;
	SOCKET soc_accept;

	//服务端地址客户端地址
	SOCKADDR_IN server_addr;
	SOCKADDR_IN accept_addr;

	cout << "-----------server-----------------" << endl << endl;

	//初始化套接字库
	initialization();

	//填充服务端信息
	server_addr.sin_family = AF_INET;//ipv4
	server_addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);//绑定默认的网卡
	server_addr.sin_port = htons(5010);//和客户端使用相同的端号5010

	//创建套接字
	soc_server = socket(AF_INET, SOCK_STREAM, 0);

	//绑定套接字在一个IP地址个一个端口上
	if (bind(soc_server, (SOCKADDR *)&server_addr, sizeof(SOCKADDR)) == -1)
	{
		cout << "套接字绑定失败!" << endl;
		WSACleanup();
	}
// 	else
// 	{
// 		cout << "套接字绑定成功!" << endl;
// 	}

	//设置套接字为监听状态
	if (listen(soc_server, SOMAXCONN) < 0)
	{
		cout << "设置监听状态失败:" << WSAGetLastError() << endl;
		WSACleanup();
	}
// 	else
// 	{
// 		cout << "设置监听状态成功!" << endl;
// 	}
	cout << "服务端正在监听连接,请稍候...." << endl;

	//接受连接请求
	len = sizeof(SOCKADDR);
	soc_accept = accept(soc_server, (SOCKADDR *)&accept_addr, &len);
	if (soc_accept == -1) {
		cout << "连接失败:" << WSAGetLastError() << endl;
		WSACleanup();
		return 0;
	}
	cout << "连接建立,准备接受数据" << endl;

	//接收数据
	while (1) {
		int len = recv(soc_accept, recv_buf, sizeof(recv_buf), 0);
		if (len < 0)
		{
			cout << "接受失败!" << endl;
			break;
		}
		
		//反序列化
		Person p;
		p.ParseFromArray(recv_buf, len);

		//打印基本信息
		cout << "身份证号:" << p.id() << endl;
		cout << "名字:" << p.name() << endl;
		cout << "出生日期:" << p.birthyear() << "." << p.birthmonth() <<"." << p.birthday() << endl;
	
		
	

		//图片解密 
		string _base64;
		Base64 decrypt;

		memset(recv_buf,0,sizeof(recv_buf));//memset方便清空一个结构体或者数组,第一个参数是指针或者数组,第二个参数是给数组的值(一般为0,相当于初始化),第三个参数是数组的长度(由sizeof来求)
		int OutByte = 0;
		string imgStr = p.imgdata();
		string ImgBuff = decrypt.Decode(p.imgdata().c_str(), strlen(p.imgdata().c_str()), OutByte);
		
		//char img[1000000];
		//memcpy_s(img, sizeof(img), ImgBuff.c_str(), OutByte);
		printf("image file len: %d\n", OutByte);
		FILE*fp;
		fp = fopen("sichuanuniversity.jpg", "wb");
		fwrite(ImgBuff.c_str(), 1, OutByte, fp);
		cout << "图片接收成功,存入当前项目路径!" << endl;
		fclose(fp);

///
		//显示图片
		cout << "图片:" <<p.imgname()<< endl;
		system("sichuanuniversity.jpg");


		cout << "请输入回复信息:";
		cin >> send_buf;
		cout << endl;


		send_len = send(soc_accept, send_buf, 100, 0);
		if (send_len < 0)
		{
			cout << "发送失败!" << endl;
			break;
		}
	}
	//关闭套接字
	closesocket(soc_server);
	closesocket(soc_accept);

	//释放DLL资源
	WSACleanup();
	system("pause");
	return 0;
}

//初始化套接字库
void initialization() {

	//初始化
	WSADATA wsadata;

	if (WSAStartup(MAKEWORD(2, 2), &wsadata))
	{
		cout << "初始化套接字失败!" << endl;
		return;
	}
// 	else
// 	{
// 		cout << "初始化套接字成功!" << endl;
// 	}

	//检测版本号
	if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wHighVersion) != 2)
	{
		cout << "套接字库版本号不符!" << endl;
		WSACleanup();
		return;
	}
// 	else
// 	{
// 		cout << "套接字库版本正确!" << endl;
// 	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值