如何在Windows環境下的VS中安裝使用Google Protobuf完成SOCKET通訊
最近一段時間,由於專案的需要,接觸到了Protobuf這個東東,在Linux環境下,體驗了一把,感覺挺不錯,很方便,且高效。是一個非常值得學習掌握和應用的資料抽象、平臺無關、功能強大、…(此處省略1000字)的開源工具。
Google雖然把Protobuf做成了跨平臺、跨語言,但作為微軟的死對頭,它在readme.txt檔案的第一句話就表明了態度:為了考慮部分MSVC的使用者,Protobuf提供了針對VS的安裝說明,但Protobuf最好用於Unix環境下。
在上一篇部落格中,我介紹瞭如何在Linux環境下安裝Protobuf,現在讓我們瞭解一下Windows環境下,如何在VS中使用Protobuf,注意是VS,在VC6的環境下,我搞弄了一個晚上都沒成功,所以推薦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的編譯器,後者包含了有三程式語言的開發包。
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,至此,我們已經完成了編譯工作,下面需要進行的是protobuf的測試。我們需要使用到之前VS編譯出來的libprotobuf.lib和libprotoc.lib完成一個C/S結構的SOCKET通訊測試。
àProtobuf的測試
在VS2005下,建立兩個新的工程,分別命名為server和client,每個工程都需要引用protobuf的標頭檔案和lib檔案。
一、新增protobuf標頭檔案操作:右擊專案à屬性à配置屬性àC/C++à常規 (也命令列可在中新增)。具體路徑:C:/protobuf-2.3.0/protobuf-2.3.0/src
二、新增protobuf的lib檔案操作:右擊專案à屬性à配置屬性à連結器à常規(也可在命令列中新增)。具體路徑: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檔案內容如下:
- package CPFS;
- message People
- {
- required string name = 1;
- required int32 id = 2;
- required string email = 3;
- }
四、server和client端原始碼:
server端原始碼:
- #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原始碼:
- #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原始碼:
- #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
- // 把一段記憶體區的內容全部設定為
- void op_clean_buffer(void *buffer, int len);
- // 設定sockaddr_in, internet協議族, 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);
- // 建立用於internet的流協議(TCP)socket, 用server_socket代表伺服器socket
- int op_socket(int domain, int type, int protocol);
- // 接受一個到server_socket代表的socket的一個連線
- // 如果沒有連線請求,就等待到有連線請求--這是accept函式的特性
- // accept函式返回一個新的socket, 這個socket(new_server_socket)用於同連線到的客戶的通訊
- // new_server_socket代表了伺服器和客戶端之間的一個通訊通道
- // accept函式把連線到的客戶端資訊填寫到客戶端的socket地址結構client_addr中
- int op_accept(OP_SOCKET sockfd, OP_SOCKADDR *addr, OP_SOCKLEN_T *addrlen);
- // IP的點分十記轉化為IP的結構體
- unsigned long op_inet_addr(const char *dst);
- // 向伺服器發起連線,連線成功後client_socket代表了客戶機和伺服器的一個socket連線
- int op_connect(OP_SOCKET sockfd, const OP_SOCKADDR *addr, OP_SOCKLEN_T addrlen);
- // addr指定的地址分配給與檔案描述符socket關聯的未命名套接字
- int op_bind(OP_SOCKET sockfd, const OP_SOCKADDR *addr, OP_SOCKLEN_T addrlen);
- // 監聽client請求,backlog指定最大連線數
- int op_listen(OP_SOCKET sockfd, int backlog);
- // send傳送訊息
- 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);
- // 關閉socket或檔案指標
- FILE* op_fopen(const char *path, const char *mode);
- // 開啟檔案
- int op_close(OP_SOCKET sockfd);
- // 關閉檔案指標
- int op_fclose(FILE *stream);
- // 休眠函式
- void op_sleep(int micro_seconds);
- // 字串比較函式
- int op_stricmp(char *s1,char * s2);
- #endif
op_socket.cpp原始檔程式碼:
- #include "op_socket.h"
- // 把一段記憶體區的內容全部設定為
- void op_clean_buffer(void *buffer, int len)
- {
- #ifndef WIN32
- bzero(buffer, len);
- #else
- memset(buffer, 0, len);
- #endif
- }
- // 設定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;
- }
- // 建立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的點分十記轉化為IP的結構體
- 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關聯的未命名套接字
- 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;
- }
- // 監聽client請求,backlog指定最大連線數
- 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傳送訊息
- 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;
- }
- // 關閉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;
- }
- // 關閉檔案指標
- int op_fclose(FILE *stream)
- {
- int ret;
- if ((ret = fclose(stream)) < 0)
- {
- fprintf(stderr, "op_fclose error/n");
- exit(EXIT_FAILURE);
- }
- return ret;
- }
- // 休眠函式,對於usleep為微秒級別,對於Sleep為毫秒級別
- void op_sleep(int micro_seconds)
- {
- #ifndef WIN32
- usleep(micro_seconds);
- #else
- Sleep(micro_seconds);
- #endif
- }
- // 字串比較函式
- 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通訊的實際效果!給大家截個圖!
最近專案緊,沒空時間來好好寫部落格,只能粗略記錄一下,等實習結束後,要把這段時間的所學所感好好總結下來。
順便說一下,op_socket這個SOCKET是跨平臺的,寫個makefile可以直接在UNIX環境下執行的,咳咳,寫的太齪了大家見笑了。。。
相關文章
- Windows 環境下 Python 環境安裝WindowsPython
- Windows 環境下安裝 LaravelWindowsLaravel
- Windows 環境下安裝 RedisWindowsRedis
- Windows環境下安裝RabbitMQWindowsMQ
- wsl中ubuntu20.04下安裝google protobufUbuntuGo
- windows7環境下使用pip安裝MySQLdbWindowsMySql
- Windows環境下安裝LinuxWindowsLinux
- Windows環境下安裝NexusWindows
- windows下配置安裝YAF環境Windows
- windows環境下安裝seleniumWindows
- socket 完成簡單的通訊
- Android 使用Socket完成程式間通訊Android
- 如何在Windows上安裝JAVA開發環境WindowsJava開發環境
- HMMer在Windows環境下的安裝HMMWindows
- monogdb windows環境下 安裝及使用簡單示例MonoWindows
- zookeeper安裝和使用 windows環境Windows
- 如何在Google Web Toolkit環境下GetshellGoWeb
- Windows環境下phpMyAdmin的安裝配置方法WindowsPHP
- Windows 下安裝miniconda 虛擬python環境,搭配 VS CodeWindowsPython
- Vscode 安裝go環境中遇到的坑!VSCodeGo
- (轉)Windows下安裝Docker, GitBash環境配置WindowsDockerGit
- Windows 環境安裝LdapWindowsLDA
- Windows環境安裝ScrapyWindows
- 安裝Scrapy(Windows下Python的爬蟲環境)WindowsPython爬蟲
- Windows環境下的Oracle Data Guard安裝和配置WindowsOracle
- CVSNT在Windows下的安裝和使用(轉)Windows
- MongoDB —— Windows環境安裝與簡單使用MongoDBWindows
- Ubuntu下 解除安裝protobuf並安裝指定版本的protobufUbuntu
- Windows10下如何安裝配置 perl 環境Windows
- linux環境程式設計(2): 使用pipe完成程式間通訊Linux程式設計
- Windows環境下Oracle11g安裝的問題WindowsOracle
- 【mycat】mycat在windows環境下的安裝和啟動Windows
- Windows10 安裝 protobufWindows
- ROS環境下串列埠通訊ROS串列埠
- (四)使用VScode安裝VUE3環境VSCodeVue
- 如何在C#中使用Google.Protobuf工具C#Go
- windows環境docker安裝LNMP環境藉助dockers-compose.yml3分鐘快速完成WindowsDockerLNMP
- Windows環境下Python3.7安裝TensorflowWindowsPython