http://blog.csdn.net/whuancai/article/details/11994341
如何在Windows環(huán)境下的VS中安裝使用Google Protobuf完成SOCKET通信
原文出自:http://blog.csdn.net/monkey_d_meng/article/details/5894910
尊重作者:MONKEY_D_MENG
?
最近一段時(shí)間,由于項(xiàng)目的需要,接觸到了Protobuf這個(gè)東東,在Linux環(huán)境下,體驗(yàn)了一把,感覺(jué)挺不錯(cuò),很方便,且高效。是一個(gè)非常值得學(xué)習(xí)掌握和應(yīng)用的數(shù)據(jù)抽象、平臺(tái)無(wú)關(guān)、功能強(qiáng)大、…(此處省略1000字)的開(kāi)源工具。
Google雖然把Protobuf做成了跨平臺(tái)、跨語(yǔ)言,但作為微軟的死對(duì)頭,它在readme.txt文件的第一句話(huà)就表明了態(tài)度:為了考慮部分MSVC的用戶(hù),Protobuf提供了針對(duì)VS的安裝說(shuō)明,但Protobuf最好用于Unix環(huán)境下。
在上一篇博客中,我介紹了如何在Linux環(huán)境下安裝Protobuf,現(xiàn)在讓我們了解一下Windows環(huán)境下,如何在VS中使用Protobuf,注意是VS,在VC6的環(huán)境下,我搞弄了一個(gè)晚上都沒(méi)成功,所以推薦VS2005或者以上版本:
?
1.下載protobuff,我下的是2.3.0版本
最新的protobuf可以到Google Code上下載:http://code.google.com/p/protobuf/downloads/list
當(dāng)前版本為2.3.0,下載兩個(gè)壓縮包:protoc-2.3.0-win32.zip和protobuf-2.3.0.zip,前者是protobuf的編譯器,后者包含了有三程序語(yǔ)言的開(kāi)發(fā)包。
?
2.解壓
首先解壓protoc-2.3.0-win32.zip,把protoc.exe文件放到path路徑中,最簡(jiǎn)單的做法就是把這個(gè)文件拷貝到C:/WINDOWS目錄下。
解壓protobuf-2.3.0.zip文件,將文件加壓到C盤(pán)根目錄,主文件位于C:/protobuf-2.3.0/protobuf-2.3.0目錄下。
?
3.安裝操作
(1)使用VS2005編譯proto,VS工程目錄位于vsprojects目錄中,工程名字為“protobuf.sln”。
?
(2)選擇“生成”à“生成解決方案”選項(xiàng)進(jìn)行編譯,編譯過(guò)程中可能會(huì)由于編譯的順序報(bào)錯(cuò)誤,可以使用手工逐個(gè)順序編譯生成,可能會(huì)比較順利。按照下圖的順序,右鍵“重新生成”,逐個(gè)編譯。但是我在實(shí)習(xí)操作過(guò)程中,libprotobuf-lite工程重來(lái)都沒(méi)有成功編譯通過(guò)過(guò)。淡定先,這個(gè)不會(huì)影響大局的。
?
(3)編譯完成會(huì)在目錄vsprojects下的Debug目錄中生成lib和exe文件。
生成清單如下:
exe文件:
2010-04-15??09:51?????????950,272 lite-test.exe
2010-04-15??09:50?????????3,219,456 protoc.exe
2010-04-15??09:48?????????9,228,288 tests.exe
2010-04-15??09:56?????????2,519,040 test_plugin.exe
?
lib文件:
2010-04-15??09:50????????2,685,922 libprotobuf-lite.lib
2010-04-15??09:56????????24,100,794 libprotobuf.lib
2010-04-15??09:56????????17,302,068 libprotoc.lib
其實(shí)我在測(cè)試過(guò)程中,lite-test.exe和libprotobuf-lite.lib并沒(méi)有生成,因?yàn)榫幾g錯(cuò)誤了,但這并不影響大局,淡定先。
?
(4)OK,至此,我們已經(jīng)完成了編譯工作,下面需要進(jìn)行的是protobuf的測(cè)試。我們需要使用到之前VS編譯出來(lái)的libprotobuf.lib和libprotoc.lib完成一個(gè)C/S結(jié)構(gòu)的SOCKET通信測(cè)試。
?
àProtobuf的測(cè)試
在VS2005下,創(chuàng)建兩個(gè)新的工程,分別命名為server和client,每個(gè)工程都需要引用protobuf的頭文件和lib文件。
一、添加protobuf頭文件操作:右擊項(xiàng)目à屬性à配置屬性àC/C++à常規(guī)?(也命令行可在中添加)。具體路徑:C:/protobuf-2.3.0/protobuf-2.3.0/src
二、添加protobuf的lib文件操作:右擊項(xiàng)目à屬性à配置屬性à鏈接器à常規(guī)(也可在命令行中添加)。具體路徑:C:/protobuf-2.3.0/protobuf-2.3.0/vsprojects/Debug
三、CMD窗口下編譯生成頭文件:
C:/protobuf-2.3.0/protobuf-2.3.0/examples>protoc -I=./ --cpp_out=./ people.proto
將proto文件生成的文件放到當(dāng)前目錄。
我們得到了兩個(gè)文件生:people.pb.h和people.pb.cc
?
people.proto文件內(nèi)容如下:
[cpp]?view plaincopy
package?CPFS;?????message?People??{???????required?string?name?=?1;???????required?int32?id?=?2;???????required?string?email?=?3;?????}??
?
?
四、server和client端源代碼:
server端源代碼:
[cpp]?view plaincopy
#include?"common/op_socket.h"??#include?"people.pb.h"??#pragma?comment(lib,?"libprotobuf.lib")??#pragma?comment(lib,?"libprotoc.lib")??using?namespace?std;??int?main()?????{?????????GOOGLE_PROTOBUF_VERIFY_VERSION;??????OP_SOCKET?server_sockfd;??????OP_SOCKET?new_server_sockfd;??????OP_SOCKADDR_IN?server_addr;??????OP_SOCKADDR_IN?client_addr;??????OP_SOCKLEN_T?sin_size;??????char?buffer[BUFFER_SIZE?+?1];??????int?bytes;??????string?str;??????string?data;??????CPFS::People?p;??#ifdef?WIN32??????WSADATA??Ws;??????//Init?Windows?Socket??????if?(WSAStartup(MAKEWORD(2,2),?&Ws)?!=?0)??????{??????????fprintf(stderr,?"Init?Windows?Socket?Failed::%s",?GetLastError());??????????return?EXIT_FAILURE;??????}??#endif??????server_sockfd?=?op_socket(AF_INET,?SOCK_STREAM,?0);???????op_set_sockaddr_in(server_addr,?AF_INET,?htons(INADDR_ANY),?htons(OP_PORT));??????op_bind(server_sockfd,?(struct?sockaddr?*)&server_addr,?sizeof(struct?sockaddr));?????????op_listen(server_sockfd,?LISTEN_QUEUE);?????????????while(1)??????{?????????????sin_size?=?sizeof(struct?sockaddr_in);??????????new_server_sockfd?=?op_accept(server_sockfd,?(struct?sockaddr?*)&client_addr,?&sin_size);??????????????bytes?=?op_recv(new_server_sockfd,?buffer,?BUFFER_SIZE,?0);??????????buffer[bytes]?=?'/0';??????????str?=?buffer;??????????cout?<<?"You?got?a?message?from?"?<<?inet_ntoa(client_addr.sin_addr)?<<?endl;??????????cout?<<?"client_addr?Message:?"?<<?str?<<?endl;??????????if(str?==?"get")??????????{??????????????p.set_id(1);??????????????????p.set_name("monkey");??????????????????p.set_email("mokeydong@gmail.com");??????????????????p.SerializeToString(&data);??????????????char?dst[BUFFER_SIZE];??????????????strcpy(dst,?data.c_str());??????????????????op_send(new_server_sockfd,?dst,?sizeof(dst),?0);??????????}??????????else??????????{??????????????op_send(new_server_sockfd,?"Fucking?client_addr!/n",?16,?0);??????????}??????????op_close(new_server_sockfd);??????}?????????op_close(server_sockfd);??????google::protobuf::ShutdownProtobufLibrary();??????getchar();??#ifdef?WIN32??????WSACleanup();??#endif??????return?EXIT_SUCCESS;??}??
client源代碼:
?
[cpp]?view plaincopy
#include?"common/op_socket.h"??#include?"people.pb.h"??#pragma?comment(lib,?"libprotobuf.lib")??#pragma?comment(lib,?"libprotoc.lib")??using?namespace?std;??int?main(int?argc,?char?**argv)?????{??????GOOGLE_PROTOBUF_VERIFY_VERSION;??????OP_SOCKET?client_sockfd;??????OP_SOCKADDR_IN?server_addr;??????OP_SOCKADDR_IN?client_addr;??????char?buffer[BUFFER_SIZE?+?1];??????int?bytes;??????CPFS::People?p;??????if?(argc?!=?2)??????{??????????printf("Usage:?%s?/"COMMAND/"/n",argv[0]);??????????exit(0);??????}??#ifdef?WIN32??????WSADATA??Ws;??????//Init?Windows?Socket??????if?(WSAStartup(MAKEWORD(2,2),?&Ws)?!=?0)??????{??????????fprintf(stderr,?"Init?Windows?Socket?Failed::%s",?GetLastError());??????????return?EXIT_FAILURE;??????}??#endif??????client_sockfd?=?op_socket(AF_INET,?SOCK_STREAM,?0);??????op_set_sockaddr_in(server_addr,?AF_INET,?op_inet_addr(DEFAULT_SERVER_IP),?htons(OP_PORT));??????op_connect(client_sockfd,?(struct?sockaddr?*)&server_addr,?sizeof(struct?sockaddr));??????op_send(client_sockfd,?argv[1],?20,?0);??????bytes?=?op_recv(client_sockfd,?buffer,?BUFFER_SIZE,?0);??????buffer[bytes]?=?'/0';??????string?data?=?buffer;??????p.ParseFromString(data);??????cout?<<?"Name:?"?<<?p.name()?<<?endl;??????cout?<<?"ID:?"?<<?p.id()?<<?endl;??????cout?<<?"Email:?"?<<?p.email()?<<?endl;??????op_close(client_sockfd);??#ifdef?WIN32??????WSACleanup();??#endif??????google::protobuf::ShutdownProtobufLibrary();??????return?EXIT_SUCCESS;??}??
五、因?yàn)樯鲜鰞蓚€(gè)代碼用到了我寫(xiě)了一個(gè)初級(jí)版本的SOCKET跨平臺(tái)的庫(kù),這里貼出來(lái),很齪,但還可以用,小弟也才開(kāi)始寫(xiě)socket程序。這個(gè)庫(kù)文件要放到common/目錄下面。
op_socket.h源代碼:
?
[cpp]?view plaincopy
#ifndef?OP_SOCKET_H_??#define?OP_SOCKET_H_??#include?<stdio.h>??#include?<errno.h>??#include?<string.h>??#include?<stdlib.h>??#include?<iostream>??#include?<string>??#ifndef?WIN32??????#include?<sys/types.h>??????#include?<sys/wait.h>??????#include?<sys/socket.h>??????#include?<arpa/inet.h>??????#include?<netinet/in.h>??????#include?<signal.h>??????#include?<netdb.h>??????#include?<unistd.h>??????#include?<fcntl.h>??#else??????#include?<winsock2.h>??????#pragma?comment(lib,?"ws2_32.lib")??#endif??//?Linux??#ifndef?WIN32??????#define?OP_SOCKET???????????????int??????#define?OP_SOCKADDR_IN??????????struct?sockaddr_in??????#define?OP_SOCKADDR?????????????struct?sockaddr??????#define?OP_SOCKLEN_T????????????socklen_t??//?Windows??#else??????#define?OP_SOCKET???????????????SOCKET??????#define?OP_SOCKADDR_IN??????????SOCKADDR_IN??????#define?OP_SOCKADDR?????????????SOCKADDR??????#define?OP_SOCKLEN_T????????????int?FAR??#endif??#define?OP_PORT?????????????????????8888??#define?BUFFER_SIZE?????????????????1024??#define?LISTEN_QUEUE????????????????20??#define?MD5_SIZE????????????????????32??#define?FILE_PATH_MAX_SIZE??????????512??#define?FILE_NAME_MAX_SIZE??????????260??#define?FILE_FULL_NAME_MAX_SIZE?????1024??#define?HOST????????????????????????"localhost"??#define?DEFAULT_SERVER_IP???????????"127.0.0.1"??#ifndef?WIN32??????#define?CLI_FILE_PATH???????????"/tmp/data/client/"?//?客戶(hù)端存儲(chǔ)文件的初始化路徑??????#define?SERV_FILE_PATH??????????"/tmp/data/server/"?//?服務(wù)器端存儲(chǔ)文件的初始化路徑??#else??????#define?CLI_FILE_PATH???????????"D://download//"????//?客戶(hù)端存儲(chǔ)文件的初始化路徑??????#define?SERV_FILE_PATH??????????"D://data//"????????//?客戶(hù)端存儲(chǔ)文件的初始化路徑??#endif??//?把一段內(nèi)存區(qū)的內(nèi)容全部設(shè)置為??void?op_clean_buffer(void?*buffer,?int?len);??//?設(shè)置sockaddr_in,?internet協(xié)議族,?INADDR_ANY表示自動(dòng)獲取本機(jī)地址??void?op_set_sockaddr_in(OP_SOCKADDR_IN?&addr,?short?op_sin_family,?unsigned?long?op_s_addr,?unsigned?short?op_sin_port);??//?創(chuàng)建用于internet的流協(xié)議(TCP)socket,?用server_socket代表服務(wù)器socket??int?op_socket(int?domain,?int?type,?int?protocol);??//?接受一個(gè)到server_socket代表的socket的一個(gè)連接??//?如果沒(méi)有連接請(qǐng)求,就等待到有連接請(qǐng)求--這是accept函數(shù)的特性??//?accept函數(shù)返回一個(gè)新的socket,?這個(gè)socket(new_server_socket)用于同連接到的客戶(hù)的通信??//?new_server_socket代表了服務(wù)器和客戶(hù)端之間的一個(gè)通信通道??//?accept函數(shù)把連接到的客戶(hù)端信息填寫(xiě)到客戶(hù)端的socket地址結(jié)構(gòu)client_addr中??int?op_accept(OP_SOCKET?sockfd,?OP_SOCKADDR?*addr,?OP_SOCKLEN_T?*addrlen);??//?IP的點(diǎn)分十記轉(zhuǎn)化為IP的結(jié)構(gòu)體??unsigned?long?op_inet_addr(const?char?*dst);??//?向服務(wù)器發(fā)起連接,連接成功后client_socket代表了客戶(hù)機(jī)和服務(wù)器的一個(gè)socket連接??int?op_connect(OP_SOCKET?sockfd,?const?OP_SOCKADDR?*addr,?OP_SOCKLEN_T?addrlen);??//?addr指定的地址分配給與文件描述符socket關(guān)聯(lián)的未命名套接字??int?op_bind(OP_SOCKET?sockfd,?const?OP_SOCKADDR?*addr,?OP_SOCKLEN_T?addrlen);??//?監(jiān)聽(tīng)client請(qǐng)求,backlog指定最大連接數(shù)??int?op_listen(OP_SOCKET?sockfd,?int?backlog);??//?send發(fā)送消息??int?op_send(OP_SOCKET?sockfd,?const?char?*buffer,?size_t?len,?int?flags);??//?recv接收消息??int?op_recv(OP_SOCKET?sockfd,?char?*buffer,?size_t?len,?int?flags);??//?關(guān)閉socket或文件指針??FILE*?op_fopen(const?char?*path,?const?char?*mode);??//?打開(kāi)文件??int?op_close(OP_SOCKET?sockfd);??//?關(guān)閉文件指針??int?op_fclose(FILE?*stream);??//?休眠函數(shù)??void?op_sleep(int?micro_seconds);??//?字符串比較函數(shù)??int?op_stricmp(char?*s1,char?*?s2);??#endif??
op_socket.cpp源文件代碼:
?
[cpp]?view plaincopy
#include?"op_socket.h"??//?把一段內(nèi)存區(qū)的內(nèi)容全部設(shè)置為??void?op_clean_buffer(void?*buffer,?int?len)??{??#ifndef?WIN32??????bzero(buffer,?len);??#else??????memset(buffer,?0,?len);??#endif??}??//?設(shè)置sockaddr_in??void?op_set_sockaddr_in(OP_SOCKADDR_IN?&addr,?short?op_sin_family,?unsigned?long?op_s_addr,?unsigned?short?op_sin_port)??{??????op_clean_buffer(&addr,?sizeof(addr));??????addr.sin_family?=?op_sin_family;??????addr.sin_addr.s_addr?=?op_s_addr;??????addr.sin_port?=?op_sin_port;??}??//?創(chuàng)建socket??int?op_socket(int?domain,?int?type,?int?protocol)??{??????int?sockfd;??#ifndef?WIN32??????if?((sockfd?=?socket(domain,?type,?protocol))?<?0)??#else??????if?((sockfd?=?socket(domain,?type,?protocol))?==?INVALID_SOCKET)??#endif??????{??????????fprintf(stderr,?"op_socket?error/n");??????????exit(EXIT_FAILURE);??????}??????return?sockfd;??}??//?接收客戶(hù)端的socket請(qǐng)求??int?op_accept(OP_SOCKET?sockfd,?OP_SOCKADDR?*addr,?OP_SOCKLEN_T?*addrlen)??{??????int?ret;??????if?((ret?=?accept(sockfd,?addr,?addrlen))?<?0)??????{??????????fprintf(stderr,?"op_accept?error/n");??????????exit(EXIT_FAILURE);??????}??????return?ret;??}??//?IP的點(diǎn)分十記轉(zhuǎn)化為IP的結(jié)構(gòu)體??unsigned?long?op_inet_addr(const?char?*dst)??{??????long?ret;??????if?((ret?=?inet_addr(dst))?<?0)??????{??????????fprintf(stderr,?"op_inet_addr?error?for?%s/n",?dst);??????????exit(EXIT_FAILURE);??????}??????return?(unsigned?long)ret;??}??//?sockfd指定的套接字連接到addr指定的服務(wù)器套接字??int?op_connect(OP_SOCKET?sockfd,?const?OP_SOCKADDR?*addr,?OP_SOCKLEN_T?addrlen)??{??????int?ret;??????if?((ret?=?connect(sockfd,?addr,?addrlen))?<?0)??????{??????????fprintf(stderr,?"op_connect?error/n");??????????exit(EXIT_FAILURE);??????}??????return?ret;??}??//?addr指定的地址分配給與文件描述符socket關(guān)聯(lián)的未命名套接字??int?op_bind(OP_SOCKET?sockfd,?const?OP_SOCKADDR?*addr,?OP_SOCKLEN_T?addrlen)??{??????int?ret;??????if?((ret?=?bind(sockfd,?addr,?addrlen))?<?0)??????{??????????fprintf(stderr,?"op_bind?error/n");??????????exit(EXIT_FAILURE);??????}??????return?ret;??}??//?監(jiān)聽(tīng)client請(qǐng)求,backlog指定最大連接數(shù)??int?op_listen(OP_SOCKET?sockfd,?int?backlog)??{??????int?ret;??????if?((ret?=?listen(sockfd,?backlog))?<?0)??????{??????????fprintf(stderr,?"op_listen?error/n");??????????exit(EXIT_FAILURE);??????}??????return?ret;??}??//?send發(fā)送消息??int?op_send(OP_SOCKET?sockfd,?const?char?*buffer,?size_t?len,?int?flags)??{??????int?ret;??????if?((ret?=?send(sockfd,?buffer,?len,?flags))?<?0)??????{??????????fprintf(stderr,?"op_send?error/n");??????????exit(EXIT_FAILURE);??????}??????return?ret;??}??//?recv接收消息??int?op_recv(OP_SOCKET?sockfd,?char?*buffer,?size_t?len,?int?flags)??{??????size_t?ret;??????op_clean_buffer(buffer,?len);??????if?((ret?=?recv(sockfd,?buffer,?len,?flags))?<?0)??????{??????????fprintf(stderr,?"op_recv?error/n");??????????exit(EXIT_FAILURE);??????}??????return?ret;??}??//?關(guān)閉socket或文件指針??int?op_close(OP_SOCKET?sockfd)??{??????int?ret;??#ifndef?WIN32??????if?((ret?=?close(sockfd))?<?0)??#else??????if((ret?=?closesocket(sockfd))?<?0)??#endif??????{??????????fprintf(stderr,?"op_close?error/n");??????????exit(EXIT_FAILURE);??????}??????return?ret;??}??//?打開(kāi)文件??FILE*?op_fopen(const?char?*path,?const?char?*mode)??{??????FILE?*fp?=?fopen(path,?mode);??????if?(NULL?==?fp)??????{??????????printf("File:/t%s?Can?Not?Open?To?Write/n",?path);??????????exit(EXIT_FAILURE);??????}??????return?fp;??}??//?關(guān)閉文件指針??int?op_fclose(FILE?*stream)??{??????int?ret;??????if?((ret?=?fclose(stream))?<?0)??????{??????????fprintf(stderr,?"op_fclose?error/n");??????????exit(EXIT_FAILURE);??????}??????return?ret;??}??//?休眠函數(shù),對(duì)于usleep為微秒級(jí)別,對(duì)于Sleep為毫秒級(jí)別??void?op_sleep(int?micro_seconds)??{??#ifndef?WIN32??????usleep(micro_seconds);??#else??????Sleep(micro_seconds);??#endif??}??//?字符串比較函數(shù)??int?op_stricmp(char?*s1,char?*?s2)??{??#ifndef?WIN32??????return?strcasecmp(s1,?s2);??#else??????return?stricmp(s1,?s2);??#endif??}??
六、完成了上述的操作之后,就可以分別對(duì)client和server端進(jìn)行編譯了,先啟動(dòng)server端服務(wù)器,然后用命令行的形式運(yùn)行client端,就可以成功了吧。哈哈!我們來(lái)看一下使用protobuf進(jìn)行socket通信的實(shí)際效果!給大家截個(gè)圖!
?
?
?
最近項(xiàng)目緊,沒(méi)空時(shí)間來(lái)好好寫(xiě)博客,只能粗略記錄一下,等實(shí)習(xí)結(jié)束后,要把這段時(shí)間的所學(xué)所感好好總結(jié)下來(lái)。
順便說(shuō)一下,op_socket這個(gè)SOCKET是跨平臺(tái)的,寫(xiě)個(gè)makefile可以直接在UNIX環(huán)境下運(yùn)行的,咳咳,寫(xiě)的太齪了大家見(jiàn)笑了。。。
總結(jié)
以上是生活随笔為你收集整理的如何在Windows环境下的VS中安装使用Google Protobuf完成SOCKET通信的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。