1.新建一个win32控制台应用程序,是空项目,名字为MyCient
2. Google Protocol环境配置(详细配置看我文章http://blog.youkuaiyun.com/dugaoda/article/details/50342449)
2.1
2.2.添加lib文件到项目Debug下面
3.生成协议person. proto
内容为
package tutorial;
message Person { required string name = 1; required int32 age = 2; optional string email = 3;
}
message msg { required int32 id = 1; // message ID required int32 len = 2; //message Length required bytes info = 3; //message content = 二进制方式. } |
得到person.pb.h和person.pb.cc
4.新建TCPPakDef.hpp。
TCPPakDef.hpp内容。buf的前8个字节的内容,包头的内容封装。
#ifndef __OMP_TCPPAKDEF_HEADER_FILE__ #define __OMP_TCPPAKDEF_HEADER_FILE__
//#include "OmpConfig.hpp"
//#include <boost/static_assert.hpp> #include <cstdlib>
namespace omp { namespace net {
// TCP Packet Define //
#pragma pack(push,1)
struct tcp_pak_header { char flag[4]; int length;
inline void reset() { //flag[0] = 'f'; //flag[1] = 'l'; //flag[2] = 'a'; //flag[3] = 'g';
// WARNING: // // only for little-endian processor now !!! // *((int*)flag) = 0x67616c66; length = 0; }
bool check() { #ifdef _DEBUG char str[5];
memset( str,'\0',sizeof(str) ); memcpy( str,&length,4 );
if( str[0] == '0' && ( str[1] == 'x' || str[1] == 'X' ) ) { char *ptr_stop(0); length = std::strtoul( str,&ptr_stop,16 ); if( ptr_stop != &str[4] ) return false; }
#endif//#ifdef _DEBUG
// WARNING: // // only for little-endian processor now !!! // return( *((int*)flag) == 0x67616c66 && length ); } };
#pragma pack(pop)
// BOOST_STATIC_ASSERT( sizeof(char)==1 ); // BOOST_STATIC_ASSERT( sizeof(int)==4 ); // BOOST_STATIC_ASSERT( sizeof(tcp_pak_header)==8 );
} // end of namespace net
} // end of namespace omp
#endif//#define __OMP_TCPPAKDEF_HEADER_FILE__ |
5.新建文件MyClient.cpp
同时添加文件person.pb.h和person.pb.cc和TCPPakDef.hpp
项目文件结构如下
6.编写MyClient.cpp文件内容
先直接贴上代码
// MyClient.cpp : 定义控制台应用程序的入口点。 //
#include "winsock2.h" #include <iostream> #include <fstream> #include "person.pb.h" #include "TCPPakDef.hpp" #pragma comment(lib, "ws2_32.lib")
using namespace std; using namespace tutorial;
BOOL RecvLine(SOCKET s, char *buf);
int main() { tutorial::Person PERSON;
const int BUF_SIZE = 1024; WSADATA wsd; SOCKET sHost; SOCKADDR_IN servAddr; char buf[BUF_SIZE]="";//接收数据缓冲区 char bufRecv[BUF_SIZE]=""; //截断后面的乱码输出 cout << endl << "1从服务器接受数据:" << buf; cout << endl << "2从服务器接受数据:" << bufRecv; int retVal;
//初始化套结字动态库 if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0) { cout << "WSAStartup failded!" << endl; return 1; }
//创建套接字 sHost = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (INVALID_SOCKET == sHost) { WSACleanup();//释放套接字资源 return -1; }
//设置服务器地址 servAddr.sin_family = AF_INET; servAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); servAddr.sin_port = htons((short)123); int nServAddlen = sizeof(servAddr);
//连接服务器 retVal = connect(sHost, (LPSOCKADDR)&servAddr, sizeof(servAddr)); if (SOCKET_ERROR == retVal) { cout << "connect failed" <<endl; closesocket(sHost); WSACleanup(); return -1; }
while (true) { //向服务器发送数据 ZeroMemory(buf, BUF_SIZE); cout << "向服务器发送数据:"; char buf123[BUF_SIZE]=""; cin >> buf123;
//使用google protobuf 将数据发送出去 //其实在每个MSG的头上面加上ID 在服务器上面解析出来就可以用了 //PERSON.set_name("dugoada"); //PERSON.set_age(123); //string data; //PERSON.SerializeToString(&data); //char dst[BUF_SIZE]; //strcpy(dst, data.c_str()); //retVal = send(sHost, dst, sizeof(dst), 0);
////*方式2 发送数据 加上包的编号*// //新的 google::protobuf::Message* user_send_msg; PERSON.set_name("dugoada"); PERSON.set_age(123); user_send_msg = &PERSON;
tutorial::msg head;// 包的ID 包的长度为Msg的ByteSize head.set_id(1); int nLength = 0; if ( user_send_msg ) nLength = user_send_msg->ByteSize(); head.set_len(nLength);
if (nLength > 0) { //序列化之后的字符串 将数据存入到 head.info(); user_send_msg->SerializeToString(head.mutable_info()); } //将数据组装到head完毕
//设置tcp的内容 omp::net::tcp_pak_header tcp = {0}; int tcplength = sizeof(tcp);//tcplength = 8; int nLen = head.ByteSize() + sizeof(tcp);//包头的长度+message的size tcp.length = htonl(nLen); //设置 tcp.length的值
//将tcp首地址开始 到sizeof(tcp)的长度 将数据存入到Buf中 处理Buff前8个长度 ::memcpy(buf, &tcp, sizeof(tcp));
//进行序列化,将head中的数据全部序列化到buf+sizeof(tcp)中 处理Buff从8开始到最后 head.SerializeToArray(buf+sizeof(tcp), nLen);
retVal = send(sHost, buf, nLen, 0); ////buf nLen ////*发送数据 加上包的编号*//
//retVal = send(sHost, buf, strlen(buf), 0); if (SOCKET_ERROR == retVal) { cout << "send failed!" << endl; closesocket(sHost);//关闭套接字 WSACleanup(); return -1; }
// 接收服务器端的数据 ZeroMemory(buf, BUF_SIZE); recv(sHost, bufRecv, BUF_SIZE, 0); cout << endl << "从服务器接受数据:" << bufRecv; }
closesocket(sHost);//关闭套接字 WSACleanup();
return 0; } |
7.关键代码说明
////*方式2 发送数据 加上包的编号*// //新的 google::protobuf::Message* user_send_msg; PERSON.set_name("dugoada"); PERSON.set_age(123); user_send_msg = &PERSON;
tutorial::msg head;// 包的ID 包的长度为Msg的ByteSize head.set_id(1); int nLength = 0; if ( user_send_msg ) nLength = user_send_msg->ByteSize(); head.set_len(nLength);
if (nLength > 0) { //序列化之后的字符串 将数据存入到 head.info(); user_send_msg->SerializeToString(head.mutable_info()); } //将数据组装到head完毕
//设置tcp的内容 omp::net::tcp_pak_header tcp = {0}; int tcplength = sizeof(tcp);//tcplength = 8; int nLen = head.ByteSize() + sizeof(tcp);//包头的长度+message的size tcp.length = htonl(nLen); //设置 tcp.length的值
//将tcp首地址开始 到sizeof(tcp)的长度 将数据存入到Buf中 处理Buff前8个长度 ::memcpy(buf, &tcp, sizeof(tcp));
//进行序列化,将head中的数据全部序列化到buf+sizeof(tcp)中 处理Buff从8开始到最后 head.SerializeToArray(buf+sizeof(tcp), nLen);
retVal = send(sHost, buf, nLen, 0); ////buf nLen ////*发送数据 加上包的编号*// |
B 服务器模块:
环境配置部分跟客户端一样,代码不一样(注意)
1.编写MyServer.cpp文件内容
先直接贴上代码
// MyServer.cpp : 定义控制台应用程序的入口点。 //
//#include "stdafx.h" #include "winsock2.h" #pragma comment(lib, "ws2_32.lib") #include <iostream> using namespace std;
#include <iostream> #include<fstream> #include "TCPPakDef.hpp" #include "person.pb.h"
using namespace tutorial;
int main() {
tutorial::Person PERSON;
const int BUF_SIZE = 1024; WSADATA wsd; SOCKET sServer; SOCKET sClient; SOCKADDR_IN addrServ; char buf[BUF_SIZE]; char sendBuf[BUF_SIZE]; int retVal;
//初始化套结字动态库 if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0) { cout << "WSAStartup failded!" << endl; return 1; }
//创建套接字 sServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (INVALID_SOCKET == sServer) { cout << "socket failed!" << endl; WSACleanup(); return -1; }
//服务器套接字地址信息设置 addrServ.sin_family = AF_INET; addrServ.sin_port = htons(123); addrServ.sin_addr.s_addr = INADDR_ANY;
//绑定 retVal = bind(sServer, (LPSOCKADDR)&addrServ, sizeof(SOCKADDR_IN)); if (SOCKET_ERROR == retVal) { cout << "bind failed!" << endl; closesocket(sServer); WSACleanup(); return -1; }
//开始监听 retVal = listen(sServer, 1); if (SOCKET_ERROR == retVal) { cout << "listen failed!" << endl; closesocket(sServer); WSACleanup(); return -1; }
//接受客户端请求 sockaddr_in addrClient; int addrClientlen = sizeof(addrClient); sClient = accept(sServer, (sockaddr FAR *)&addrClient, &addrClientlen);
if (INVALID_SOCKET == sClient) { cout << "accept failed!" << endl; closesocket(sServer); WSACleanup(); return -1; }
//数据处理 while (true) { //接受客户端数据 ZeroMemory(buf, BUF_SIZE); retVal = recv(sClient, buf, BUF_SIZE, 0);
//方法1 解析google protobuf //string data = buf; //PERSON.ParseFromString(data); //cout << "Name: " << PERSON.name() << endl; //cout << "Age: " << PERSON.age() << endl; int nHeadLength = sizeof(omp::net::tcp_pak_header); //char receveBuf[BUF_SIZE]; omp::net::tcp_pak_header* pTcpMsg = (omp::net::tcp_pak_header*)buf; unsigned short wAllSize = ntohl(pTcpMsg->length); int nPacketSize = wAllSize - nHeadLength;//得到 tutorial::msg 的ByteSize()
tutorial::msg head; head.ParseFromArray(buf+nHeadLength, nPacketSize);
unsigned char cbDataBuffer[1024+20];
//将head的数据放入到buf中 ::memcpy(cbDataBuffer, head.info().data(), head.info().length());
//::memmove(buf, buf+nPacketSize, m_wRecvSize);
int sfde = head.id(); cbDataBuffer; nPacketSize;
tutorial::Person PP; PP.ParseFromArray(cbDataBuffer, nPacketSize); string sfe = PP.name(); int asfewe = PP.age(); cout << "名字:" << PP.name() << endl; cout << "年龄:" << PP.age() << endl;
if (SOCKET_ERROR == retVal) { cout << "recv, failed!" << endl; closesocket(sClient); closesocket(sServer); WSACleanup(); return -1; }
if (buf[0] == '0') break;
cout << "客户端发送的数据:" << buf << endl; cout << "向客户端发送数据:"; cin >> sendBuf; send(sClient, sendBuf, strlen(sendBuf), 0);
}
closesocket(sClient); closesocket(sServer); WSACleanup();
return 0; }
|
6.关键代码说明
retVal = recv(sClient, buf, BUF_SIZE, 0);
//方法1 解析google protobuf //string data = buf; //PERSON.ParseFromString(data); //cout << "Name: " << PERSON.name() << endl; //cout << "Age: " << PERSON.age() << endl; int nHeadLength = sizeof(omp::net::tcp_pak_header); //char receveBuf[BUF_SIZE]; omp::net::tcp_pak_header* pTcpMsg = (omp::net::tcp_pak_header*)buf; unsigned short wAllSize = ntohl(pTcpMsg->length); int nPacketSize = wAllSize - nHeadLength;//得到 tutorial::msg 的ByteSize()
tutorial::msg head; head.ParseFromArray(buf+nHeadLength, nPacketSize);
unsigned char cbDataBuffer[1024+20];
//将head的数据放入到buf中 ::memcpy(cbDataBuffer, head.info().data(), head.info().length());
//::memmove(buf, buf+nPacketSize, m_wRecvSize);
int sfde = head.id(); cbDataBuffer; nPacketSize;
tutorial::Person PP; PP.ParseFromArray(cbDataBuffer, nPacketSize); string sfe = PP.name(); int asfewe = PP.age(); cout << "名字:" << PP.name() << endl; cout << "年龄:" << PP.age() << endl;
|