• VS下使用Google Protobuf完成SOCKET通信


    如何在Windows环境下的VS中安装使用Google Protobuf完成SOCKET通信

     出处:如何在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文件内容如下:

    [cpp] view plaincopy
     
    1. package CPFS;     
    2. message People  
    3. {     
    4.   required string name = 1;     
    5.   required int32 id = 2;     
    6.   required string email = 3;     
    7. }  

     

    四、server和client端源代码:

    server端源代码:

    [cpp] view plaincopy
     
    1. #include "common/op_socket.h"  
    2. #include "people.pb.h"  
    3. #pragma comment(lib, "libprotobuf.lib")  
    4. #pragma comment(lib, "libprotoc.lib")  
    5. using namespace std;  
    6. int main()     
    7. {     
    8.     GOOGLE_PROTOBUF_VERIFY_VERSION;  
    9.     OP_SOCKET server_sockfd;  
    10.     OP_SOCKET new_server_sockfd;  
    11.     OP_SOCKADDR_IN server_addr;  
    12.     OP_SOCKADDR_IN client_addr;  
    13.     OP_SOCKLEN_T sin_size;  
    14.     char buffer[BUFFER_SIZE + 1];  
    15.     int bytes;  
    16.     string str;  
    17.     string data;  
    18.     CPFS::People p;  
    19. #ifdef WIN32  
    20.     WSADATA  Ws;  
    21.     //Init Windows Socket  
    22.     if (WSAStartup(MAKEWORD(2,2), &Ws) != 0)  
    23.     {  
    24.         fprintf(stderr, "Init Windows Socket Failed::%s", GetLastError());  
    25.         return EXIT_FAILURE;  
    26.     }  
    27. #endif  
    28.     server_sockfd = op_socket(AF_INET, SOCK_STREAM, 0);   
    29.     op_set_sockaddr_in(server_addr, AF_INET, htons(INADDR_ANY), htons(OP_PORT));  
    30.     op_bind(server_sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr));     
    31.     op_listen(server_sockfd, LISTEN_QUEUE);     
    32.     
    33.     while(1)  
    34.     {     
    35.         sin_size = sizeof(struct sockaddr_in);  
    36.         new_server_sockfd = op_accept(server_sockfd, (struct sockaddr *)&client_addr, &sin_size);  
    37.     
    38.         bytes = op_recv(new_server_sockfd, buffer, BUFFER_SIZE, 0);  
    39.         buffer[bytes] = '/0';  
    40.         str = buffer;  
    41.         cout << "You got a message from " << inet_ntoa(client_addr.sin_addr) << endl;  
    42.         cout << "client_addr Message: " << str << endl;  
    43.         if(str == "get")  
    44.         {  
    45.             p.set_id(1);  
    46.                 p.set_name("monkey");  
    47.                 p.set_email("mokeydong@gmail.com");  
    48.                 p.SerializeToString(&data);  
    49.             char dst[BUFFER_SIZE];  
    50.             strcpy(dst, data.c_str());  
    51.                 op_send(new_server_sockfd, dst, sizeof(dst), 0);  
    52.         }  
    53.         else  
    54.         {  
    55.             op_send(new_server_sockfd, "Fucking client_addr!/n", 16, 0);  
    56.         }  
    57.         op_close(new_server_sockfd);  
    58.     }     
    59.     op_close(server_sockfd);  
    60.     google::protobuf::ShutdownProtobufLibrary();  
    61.     getchar();  
    62. #ifdef WIN32  
    63.     WSACleanup();  
    64. #endif  
    65.     return EXIT_SUCCESS;  
    66. }  

    client源代码:

    [cpp] view plaincopy
     
    1. #include "common/op_socket.h"  
    2. #include "people.pb.h"  
    3. #pragma comment(lib, "libprotobuf.lib")  
    4. #pragma comment(lib, "libprotoc.lib")  
    5. using namespace std;  
    6. int main(int argc, char **argv)     
    7. {  
    8.     GOOGLE_PROTOBUF_VERIFY_VERSION;  
    9.     OP_SOCKET client_sockfd;  
    10.     OP_SOCKADDR_IN server_addr;  
    11.     OP_SOCKADDR_IN client_addr;  
    12.     char buffer[BUFFER_SIZE + 1];  
    13.     int bytes;  
    14.     CPFS::People p;  
    15.     if (argc != 2)  
    16.     {  
    17.         printf("Usage: %s /"COMMAND/"/n",argv[0]);  
    18.         exit(0);  
    19.     }  
    20. #ifdef WIN32  
    21.     WSADATA  Ws;  
    22.     //Init Windows Socket  
    23.     if (WSAStartup(MAKEWORD(2,2), &Ws) != 0)  
    24.     {  
    25.         fprintf(stderr, "Init Windows Socket Failed::%s", GetLastError());  
    26.         return EXIT_FAILURE;  
    27.     }  
    28. #endif  
    29.     client_sockfd = op_socket(AF_INET, SOCK_STREAM, 0);  
    30.     op_set_sockaddr_in(server_addr, AF_INET, op_inet_addr(DEFAULT_SERVER_IP), htons(OP_PORT));  
    31.     op_connect(client_sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr));  
    32.     op_send(client_sockfd, argv[1], 20, 0);  
    33.     bytes = op_recv(client_sockfd, buffer, BUFFER_SIZE, 0);  
    34.     buffer[bytes] = '/0';  
    35.     string data = buffer;  
    36.     p.ParseFromString(data);  
    37.     cout << "Name: " << p.name() << endl;  
    38.     cout << "ID: " << p.id() << endl;  
    39.     cout << "Email: " << p.email() << endl;  
    40.     op_close(client_sockfd);  
    41. #ifdef WIN32  
    42.     WSACleanup();  
    43. #endif  
    44.     google::protobuf::ShutdownProtobufLibrary();  
    45.     return EXIT_SUCCESS;  
    46. }  

    五、因为上述两个代码用到了我写了一个初级版本的SOCKET跨平台的库,这里贴出来,很龊,但还可以用,小弟也才开始写socket程序。这个库文件要放到common/目录下面。

    op_socket.h源代码:

    [cpp] view plaincopy
     
    1. #ifndef OP_SOCKET_H_  
    2. #define OP_SOCKET_H_  
    3. #include <stdio.h>  
    4. #include <errno.h>  
    5. #include <string.h>  
    6. #include <stdlib.h>  
    7. #include <iostream>  
    8. #include <string>  
    9. #ifndef WIN32  
    10.     #include <sys/types.h>  
    11.     #include <sys/wait.h>  
    12.     #include <sys/socket.h>  
    13.     #include <arpa/inet.h>  
    14.     #include <netinet/in.h>  
    15.     #include <signal.h>  
    16.     #include <netdb.h>  
    17.     #include <unistd.h>  
    18.     #include <fcntl.h>  
    19. #else  
    20.     #include <winsock2.h>  
    21.     #pragma comment(lib, "ws2_32.lib")  
    22. #endif  
    23. // Linux  
    24. #ifndef WIN32  
    25.     #define OP_SOCKET               int  
    26.     #define OP_SOCKADDR_IN          struct sockaddr_in  
    27.     #define OP_SOCKADDR             struct sockaddr  
    28.     #define OP_SOCKLEN_T            socklen_t  
    29. // Windows  
    30. #else  
    31.     #define OP_SOCKET               SOCKET  
    32.     #define OP_SOCKADDR_IN          SOCKADDR_IN  
    33.     #define OP_SOCKADDR             SOCKADDR  
    34.     #define OP_SOCKLEN_T            int FAR  
    35. #endif  
    36. #define OP_PORT                     8888  
    37. #define BUFFER_SIZE                 1024  
    38. #define LISTEN_QUEUE                20  
    39. #define MD5_SIZE                    32  
    40. #define FILE_PATH_MAX_SIZE          512  
    41. #define FILE_NAME_MAX_SIZE          260  
    42. #define FILE_FULL_NAME_MAX_SIZE     1024  
    43. #define HOST                        "localhost"  
    44. #define DEFAULT_SERVER_IP           "127.0.0.1"  
    45. #ifndef WIN32  
    46.     #define CLI_FILE_PATH           "/tmp/data/client/" // 客户端存储文件的初始化路径  
    47.     #define SERV_FILE_PATH          "/tmp/data/server/" // 服务器端存储文件的初始化路径  
    48. #else  
    49.     #define CLI_FILE_PATH           "D://download//"    // 客户端存储文件的初始化路径  
    50.     #define SERV_FILE_PATH          "D://data//"        // 客户端存储文件的初始化路径  
    51. #endif  
    52. // 把一段内存区的内容全部设置为  
    53. void op_clean_buffer(void *buffer, int len);  
    54. // 设置sockaddr_in, internet协议族, INADDR_ANY表示自动获取本机地址  
    55. void op_set_sockaddr_in(OP_SOCKADDR_IN &addr, short op_sin_family, unsigned long op_s_addr, unsigned short op_sin_port);  
    56. // 创建用于internet的流协议(TCP)socket, 用server_socket代表服务器socket  
    57. int op_socket(int domain, int type, int protocol);  
    58. // 接受一个到server_socket代表的socket的一个连接  
    59. // 如果没有连接请求,就等待到有连接请求--这是accept函数的特性  
    60. // accept函数返回一个新的socket, 这个socket(new_server_socket)用于同连接到的客户的通信  
    61. // new_server_socket代表了服务器和客户端之间的一个通信通道  
    62. // accept函数把连接到的客户端信息填写到客户端的socket地址结构client_addr中  
    63. int op_accept(OP_SOCKET sockfd, OP_SOCKADDR *addr, OP_SOCKLEN_T *addrlen);  
    64. // IP的点分十记转化为IP的结构体  
    65. unsigned long op_inet_addr(const char *dst);  
    66. // 向服务器发起连接,连接成功后client_socket代表了客户机和服务器的一个socket连接  
    67. int op_connect(OP_SOCKET sockfd, const OP_SOCKADDR *addr, OP_SOCKLEN_T addrlen);  
    68. // addr指定的地址分配给与文件描述符socket关联的未命名套接字  
    69. int op_bind(OP_SOCKET sockfd, const OP_SOCKADDR *addr, OP_SOCKLEN_T addrlen);  
    70. // 监听client请求,backlog指定最大连接数  
    71. int op_listen(OP_SOCKET sockfd, int backlog);  
    72. // send发送消息  
    73. int op_send(OP_SOCKET sockfd, const char *buffer, size_t len, int flags);  
    74. // recv接收消息  
    75. int op_recv(OP_SOCKET sockfd, char *buffer, size_t len, int flags);  
    76. // 关闭socket或文件指针  
    77. FILE* op_fopen(const char *path, const char *mode);  
    78. // 打开文件  
    79. int op_close(OP_SOCKET sockfd);  
    80. // 关闭文件指针  
    81. int op_fclose(FILE *stream);  
    82. // 休眠函数  
    83. void op_sleep(int micro_seconds);  
    84. // 字符串比较函数  
    85. int op_stricmp(char *s1,char * s2);  
    86. #endif  

    op_socket.cpp源文件代码:

    [cpp] view plaincopy
     
    1. #include "op_socket.h"  
    2. // 把一段内存区的内容全部设置为  
    3. void op_clean_buffer(void *buffer, int len)  
    4. {  
    5. #ifndef WIN32  
    6.     bzero(buffer, len);  
    7. #else  
    8.     memset(buffer, 0, len);  
    9. #endif  
    10. }  
    11. // 设置sockaddr_in  
    12. void op_set_sockaddr_in(OP_SOCKADDR_IN &addr, short op_sin_family, unsigned long op_s_addr, unsigned short op_sin_port)  
    13. {  
    14.     op_clean_buffer(&addr, sizeof(addr));  
    15.     addr.sin_family = op_sin_family;  
    16.     addr.sin_addr.s_addr = op_s_addr;  
    17.     addr.sin_port = op_sin_port;  
    18. }  
    19. // 创建socket  
    20. int op_socket(int domain, int type, int protocol)  
    21. {  
    22.     int sockfd;  
    23. #ifndef WIN32  
    24.     if ((sockfd = socket(domain, type, protocol)) < 0)  
    25. #else  
    26.     if ((sockfd = socket(domain, type, protocol)) == INVALID_SOCKET)  
    27. #endif  
    28.     {  
    29.         fprintf(stderr, "op_socket error/n");  
    30.         exit(EXIT_FAILURE);  
    31.     }  
    32.     return sockfd;  
    33. }  
    34. // 接收客户端的socket请求  
    35. int op_accept(OP_SOCKET sockfd, OP_SOCKADDR *addr, OP_SOCKLEN_T *addrlen)  
    36. {  
    37.     int ret;  
    38.     if ((ret = accept(sockfd, addr, addrlen)) < 0)  
    39.     {  
    40.         fprintf(stderr, "op_accept error/n");  
    41.         exit(EXIT_FAILURE);  
    42.     }  
    43.     return ret;  
    44. }  
    45. // IP的点分十记转化为IP的结构体  
    46. unsigned long op_inet_addr(const char *dst)  
    47. {  
    48.     long ret;  
    49.     if ((ret = inet_addr(dst)) < 0)  
    50.     {  
    51.         fprintf(stderr, "op_inet_addr error for %s/n", dst);  
    52.         exit(EXIT_FAILURE);  
    53.     }  
    54.     return (unsigned long)ret;  
    55. }  
    56. // sockfd指定的套接字连接到addr指定的服务器套接字  
    57. int op_connect(OP_SOCKET sockfd, const OP_SOCKADDR *addr, OP_SOCKLEN_T addrlen)  
    58. {  
    59.     int ret;  
    60.     if ((ret = connect(sockfd, addr, addrlen)) < 0)  
    61.     {  
    62.         fprintf(stderr, "op_connect error/n");  
    63.         exit(EXIT_FAILURE);  
    64.     }  
    65.     return ret;  
    66. }  
    67. // addr指定的地址分配给与文件描述符socket关联的未命名套接字  
    68. int op_bind(OP_SOCKET sockfd, const OP_SOCKADDR *addr, OP_SOCKLEN_T addrlen)  
    69. {  
    70.     int ret;  
    71.     if ((ret = bind(sockfd, addr, addrlen)) < 0)  
    72.     {  
    73.         fprintf(stderr, "op_bind error/n");  
    74.         exit(EXIT_FAILURE);  
    75.     }  
    76.     return ret;  
    77. }  
    78. // 监听client请求,backlog指定最大连接数  
    79. int op_listen(OP_SOCKET sockfd, int backlog)  
    80. {  
    81.     int ret;  
    82.     if ((ret = listen(sockfd, backlog)) < 0)  
    83.     {  
    84.         fprintf(stderr, "op_listen error/n");  
    85.         exit(EXIT_FAILURE);  
    86.     }  
    87.     return ret;  
    88. }  
    89. // send发送消息  
    90. int op_send(OP_SOCKET sockfd, const char *buffer, size_t len, int flags)  
    91. {  
    92.     int ret;  
    93.     if ((ret = send(sockfd, buffer, len, flags)) < 0)  
    94.     {  
    95.         fprintf(stderr, "op_send error/n");  
    96.         exit(EXIT_FAILURE);  
    97.     }  
    98.     return ret;  
    99. }  
    100. // recv接收消息  
    101. int op_recv(OP_SOCKET sockfd, char *buffer, size_t len, int flags)  
    102. {  
    103.     size_t ret;  
    104.     op_clean_buffer(buffer, len);  
    105.     if ((ret = recv(sockfd, buffer, len, flags)) < 0)  
    106.     {  
    107.         fprintf(stderr, "op_recv error/n");  
    108.         exit(EXIT_FAILURE);  
    109.     }  
    110.     return ret;  
    111. }  
    112. // 关闭socket或文件指针  
    113. int op_close(OP_SOCKET sockfd)  
    114. {  
    115.     int ret;  
    116. #ifndef WIN32  
    117.     if ((ret = close(sockfd)) < 0)  
    118. #else  
    119.     if((ret = closesocket(sockfd)) < 0)  
    120. #endif  
    121.     {  
    122.         fprintf(stderr, "op_close error/n");  
    123.         exit(EXIT_FAILURE);  
    124.     }  
    125.     return ret;  
    126. }  
    127. // 打开文件  
    128. FILE* op_fopen(const char *path, const char *mode)  
    129. {  
    130.     FILE *fp = fopen(path, mode);  
    131.     if (NULL == fp)  
    132.     {  
    133.         printf("File:/t%s Can Not Open To Write/n", path);  
    134.         exit(EXIT_FAILURE);  
    135.     }  
    136.     return fp;  
    137. }  
    138. // 关闭文件指针  
    139. int op_fclose(FILE *stream)  
    140. {  
    141.     int ret;  
    142.     if ((ret = fclose(stream)) < 0)  
    143.     {  
    144.         fprintf(stderr, "op_fclose error/n");  
    145.         exit(EXIT_FAILURE);  
    146.     }  
    147.     return ret;  
    148. }  
    149. // 休眠函数,对于usleep为微秒级别,对于Sleep为毫秒级别  
    150. void op_sleep(int micro_seconds)  
    151. {  
    152. #ifndef WIN32  
    153.     usleep(micro_seconds);  
    154. #else  
    155.     Sleep(micro_seconds);  
    156. #endif  
    157. }  
    158. // 字符串比较函数  
    159. int op_stricmp(char *s1,char * s2)  
    160. {  
    161. #ifndef WIN32  
    162.     return strcasecmp(s1, s2);  
    163. #else  
    164.     return stricmp(s1, s2);  
    165. #endif  
    166. }  

    六、完成了上述的操作之后,就可以分别对client和server端进行编译了,先启动server端服务器,然后用命令行的形式运行client端,就可以成功了吧。哈哈!我们来看一下使用protobuf进行socket通信的实际效果!给大家截个图!

     

     

  • 相关阅读:
    兼容多浏览器的网页复制插件(ZeroClipboard)
    兼容主流浏览器的css渐变色
    如何让字体随窗口缩放自动调整大小
    使用javascript实现html页面直接下载网盘文件
    360随身wifi隐藏ssid方法
    javascript特效——烟花燃放的效果[xyytit]
    JavaScript数组定义
    常用特殊符号的HTML代码(HTML字符实体)
    9.JAVA之GUI编程列出指定目录内容
    8.JAVA之GUI编程键盘码查询器
  • 原文地址:https://www.cnblogs.com/xxiaoye/p/3984004.html
Copyright © 2020-2023  润新知