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
?
最近一段時間,由于項目的需要,接觸到了Protobuf這個東東,在Linux環(huán)境下,體驗了一把,感覺挺不錯,很方便,且高效。是一個非常值得學習掌握和應用的數(shù)據(jù)抽象、平臺無關(guān)、功能強大、…(此處省略1000字)的開源工具。
Google雖然把Protobuf做成了跨平臺、跨語言,但作為微軟的死對頭,它在readme.txt文件的第一句話就表明了態(tài)度:為了考慮部分MSVC的用戶,Protobuf提供了針對VS的安裝說明,但Protobuf最好用于Unix環(huán)境下。
在上一篇博客中,我介紹了如何在Linux環(huán)境下安裝Protobuf,現(xiàn)在讓我們了解一下Windows環(huán)境下,如何在VS中使用Protobuf,注意是VS,在VC6的環(huán)境下,我搞弄了一個晚上都沒成功,所以推薦VS2005或者以上版本:
?
1.下載protobuff,我下的是2.3.0版本
最新的protobuf可以到Google Code上下載:http://code.google.com/p/protobuf/downloads/list
當前版本為2.3.0,下載兩個壓縮包:protoc-2.3.0-win32.zip和protobuf-2.3.0.zip,前者是protobuf的編譯器,后者包含了有三程序語言的開發(fā)包。
?
2.解壓
首先解壓protoc-2.3.0-win32.zip,把protoc.exe文件放到path路徑中,最簡單的做法就是把這個文件拷貝到C:/WINDOWS目錄下。
解壓protobuf-2.3.0.zip文件,將文件加壓到C盤根目錄,主文件位于C:/protobuf-2.3.0/protobuf-2.3.0目錄下。
?
3.安裝操作
(1)使用VS2005編譯proto,VS工程目錄位于vsprojects目錄中,工程名字為“protobuf.sln”。
?
(2)選擇“生成”à“生成解決方案”選項進行編譯,編譯過程中可能會由于編譯的順序報錯誤,可以使用手工逐個順序編譯生成,可能會比較順利。按照下圖的順序,右鍵“重新生成”,逐個編譯。但是我在實習操作過程中,libprotobuf-lite工程重來都沒有成功編譯通過過。淡定先,這個不會影響大局的。
?
(3)編譯完成會在目錄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
其實我在測試過程中,lite-test.exe和libprotobuf-lite.lib并沒有生成,因為編譯錯誤了,但這并不影響大局,淡定先。
?
(4)OK,至此,我們已經(jīng)完成了編譯工作,下面需要進行的是protobuf的測試。我們需要使用到之前VS編譯出來的libprotobuf.lib和libprotoc.lib完成一個C/S結(jié)構(gòu)的SOCKET通信測試。
?
àProtobuf的測試
在VS2005下,創(chuàng)建兩個新的工程,分別命名為server和client,每個工程都需要引用protobuf的頭文件和lib文件。
一、添加protobuf頭文件操作:右擊項目à屬性à配置屬性àC/C++à常規(guī)?(也命令行可在中添加)。具體路徑:C:/protobuf-2.3.0/protobuf-2.3.0/src
二、添加protobuf的lib文件操作:右擊項目à屬性à配置屬性à鏈接器à常規(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文件生成的文件放到當前目錄。
我們得到了兩個文件生: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;??}??
五、因為上述兩個代碼用到了我寫了一個初級版本的SOCKET跨平臺的庫,這里貼出來,很齪,但還可以用,小弟也才開始寫socket程序。這個庫文件要放到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/"?//?客戶端存儲文件的初始化路徑??????#define?SERV_FILE_PATH??????????"/tmp/data/server/"?//?服務器端存儲文件的初始化路徑??#else??????#define?CLI_FILE_PATH???????????"D://download//"????//?客戶端存儲文件的初始化路徑??????#define?SERV_FILE_PATH??????????"D://data//"????????//?客戶端存儲文件的初始化路徑??#endif??//?把一段內(nèi)存區(qū)的內(nèi)容全部設(shè)置為??void?op_clean_buffer(void?*buffer,?int?len);??//?設(shè)置sockaddr_in,?internet協(xié)議族,?INADDR_ANY表示自動獲取本機地址??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代表服務器socket??int?op_socket(int?domain,?int?type,?int?protocol);??//?接受一個到server_socket代表的socket的一個連接??//?如果沒有連接請求,就等待到有連接請求--這是accept函數(shù)的特性??//?accept函數(shù)返回一個新的socket,?這個socket(new_server_socket)用于同連接到的客戶的通信??//?new_server_socket代表了服務器和客戶端之間的一個通信通道??//?accept函數(shù)把連接到的客戶端信息填寫到客戶端的socket地址結(jié)構(gòu)client_addr中??int?op_accept(OP_SOCKET?sockfd,?OP_SOCKADDR?*addr,?OP_SOCKLEN_T?*addrlen);??//?IP的點分十記轉(zhuǎn)化為IP的結(jié)構(gòu)體??unsigned?long?op_inet_addr(const?char?*dst);??//?向服務器發(fā)起連接,連接成功后client_socket代表了客戶機和服務器的一個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)聽client請求,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);??//?打開文件??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;??}??//?接收客戶端的socket請求??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的點分十記轉(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指定的服務器套接字??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)聽client請求,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;??}??//?打開文件??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ù),對于usleep為微秒級別,對于Sleep為毫秒級別??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??}??
六、完成了上述的操作之后,就可以分別對client和server端進行編譯了,先啟動server端服務器,然后用命令行的形式運行client端,就可以成功了吧。哈哈!我們來看一下使用protobuf進行socket通信的實際效果!給大家截個圖!
?
?
?
最近項目緊,沒空時間來好好寫博客,只能粗略記錄一下,等實習結(jié)束后,要把這段時間的所學所感好好總結(jié)下來。
順便說一下,op_socket這個SOCKET是跨平臺的,寫個makefile可以直接在UNIX環(huán)境下運行的,咳咳,寫的太齪了大家見笑了。。。
總結(jié)
以上是生活随笔為你收集整理的如何在Windows环境下的VS中安装使用Google Protobuf完成SOCKET通信的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。