转载自:http://blog.csdn.net/mydipan396/article/details/44411171


现在几乎所有C/C++的后台程序都需要进行网络通讯,其实现方法无非有两种:使用系统底层socket或者使用已有的封装好的网络库。本文对两种方式进行总结,并介绍一个轻量级的网络通讯库ZeroMQ。


1.基本的Scoket编程

关于基本的scoket编程网络上已有很多资料,作者在这里引用一篇文章中的内容进行简要说明。

基于socket编程,基本上就是以下6个步骤:

  • 1、socket()函数
  • 2、bind()函数
  • 3、listen()、connect()函数
  • 4、accept()函数
  • 5、read()、write()函数等
  • 6、close()函数
下面直接引用文章里的代码说明。

  1. 服务器端  
  2.   
  3. #include<stdio.h>  
  4. #include<stdlib.h>  
  5. #include<string.h>  
  6. #include<errno.h>  
  7. #include<sys/types.h>  
  8. #include<sys/socket.h>  
  9. #include<netinet/in.h>  
  10.   
  11. #define MAXLINE 4096  
  12.   
  13. int main(int argc, char** argv)  
  14. {  
  15.     int    listenfd, connfd;  
  16.     struct sockaddr_in     servaddr;  
  17.     char    buff[4096];  
  18.     int     n;  
  19.   
  20.     if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ){  
  21.     printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);  
  22.     exit(0);  
  23.     }  
  24.   
  25.     memset(&servaddr, 0, sizeof(servaddr));  
  26.     servaddr.sin_family = AF_INET;  
  27.     servaddr.sin_addr.s_addr = htonl(INADDR_ANY);  
  28.     servaddr.sin_port = htons(6666);  
  29.   
  30.     if( bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){  
  31.     printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);  
  32.     exit(0);  
  33.     }  
  34.   
  35.     if( listen(listenfd, 10) == -1){  
  36.     printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);  
  37.     exit(0);  
  38.     }  
  39.   
  40.     printf("======waiting for client's request======\n");  
  41.     while(1){  
  42.     if( (connfd = accept(listenfd, (struct sockaddr*)NULL, NULL)) == -1){  
  43.         printf("accept socket error: %s(errno: %d)",strerror(errno),errno);  
  44.         continue;  
  45.     }  
  46.     n = recv(connfd, buff, MAXLINE, 0);  
  47.     buff[n] = '\0';  
  48.     printf("recv msg from client: %s\n", buff);  
  49.     close(connfd);  
  50.     }  
  51.   
  52.     close(listenfd);  
  53. }  
  1. 客户端  
  2.   
  3. #include<stdio.h>  
  4. #include<stdlib.h>  
  5. #include<string.h>  
  6. #include<errno.h>  
  7. #include<sys/types.h>  
  8. #include<sys/socket.h>  
  9. #include<netinet/in.h>  
  10.   
  11. #define MAXLINE 4096  
  12.   
  13. int main(int argc, char** argv)  
  14. {  
  15.     int    sockfd, n;  
  16.     char    recvline[4096], sendline[4096];  
  17.     struct sockaddr_in    servaddr;  
  18.   
  19.     if( argc != 2){  
  20.     printf("usage: ./client <ipaddress>\n");  
  21.     exit(0);  
  22.     }  
  23.   
  24.     if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){  
  25.     printf("create socket error: %s(errno: %d)\n", strerror(errno),errno);  
  26.     exit(0);  
  27.     }  
  28.   
  29.     memset(&servaddr, 0, sizeof(servaddr));  
  30.     servaddr.sin_family = AF_INET;  
  31.     servaddr.sin_port = htons(6666);  
  32.     if( inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0){  
  33.     printf("inet_pton error for %s\n",argv[1]);  
  34.     exit(0);  
  35.     }  
  36.   
  37.     if( connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0){  
  38.     printf("connect error: %s(errno: %d)\n",strerror(errno),errno);  
  39.     exit(0);  
  40.     }  
  41.   
  42.     printf("send msg to server: \n");  
  43.     fgets(sendline, 4096, stdin);  
  44.     if( send(sockfd, sendline, strlen(sendline), 0) < 0)  
  45.     {  
  46.     printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);  
  47.     exit(0);  
  48.     }  
  49.   
  50.     close(sockfd);  
  51.     exit(0);  
  52. }  

具体可参考:  Linux Socket编程(不限Linux)

小结:直接基于socket编程看起来十分简单,但是要写出一个稳定、高性能的程序还是十分考验水平的,比如数据分包与重组,异步IO,并发访问,poll/select等,非大牛不能为之。鉴于此出现很多封装好的C/C++网络通讯库,或者叫消息中间件,下面进行介绍。

2.流行的通讯库/消息中间件

网络上各种各样的通讯中间件/MQ多不胜数。具作者所知,比较有名的有ACE、ICE、Boost::ASIO、MSMQ、ActiveMQ、RabbitMQ、ZeroMQ等等。

其中ACE、ICE是经典,网上资料很丰富,不过入门门槛较高,说白了就是有点难学难精。属于高端大气上档次的货。

Boost::ASIO作为大名鼎鼎的Boost模块之一,感觉很不错,只需引用.hpp文件,不需要动态库,性能据说也不错,跨平台,值得推荐和学习。不过没有最简单只有更简单,当有更好的选择时,Boost::ASIO的语法就略显复杂了。(不过还是强力推荐)。

MSMQ 微软的东西,用起来还可以,不过一般不推荐,毕竟Linux下没人会用它。

JSM、ActiveMQ、RabbitMQ、ZeroMQ基本上是一类东西。activemq,基于jms稳定可靠安全。rabbitmq,基于erlang,充分利用并发和分布式特性。zeromq,号称世上最快消息内核。

lActiveMQ
pActiveMQ是Apache开发的开源消息中间件,纯Java实现,基于JMS1.1及J2EE 1.4规范。
lRabbitMQ
pRabbitMQ 是由 LShift 提供的一个 Advanced Message Queuing Protocol (AMQP) 的开源实现,由以高性能、健壮以及可伸缩性出名的 Erlang 写成,因此也是继承了这些优点。
lZeroMQ
p是由iMatix公司使用C语言开发的高性能消息中间件,是对socket的封装,在发送端缓存消息。

 

ActiveMQ RabbitMQ ZeroMQ
遵循规范 JMS1.1J2EE1.4 AMPQ ---
架构模型 消息代理架构Broker 消息代理架构Broker C/S架构
实现语言 Java Erlang C/C++
支持消息协议 Stomp AMPQStomp ---
主要推动力量 ApacheRedhat LshiftVmwareSpringSource iMatix
支持编程语言 CJavaPython CJavaPython CJavaPython
编程复杂度 复杂 简单 中等
持久化 支持 支持,不支持第三方数据库 发送端缓存
性能 Normal Normal High
内存使用率 High High Normal
引用自邱志刚的Message oriented middleware analysis,抱歉未找到链接。

下面是一个网上对消息系统的总结,也可以参考以下。

常见开源消息系统


3.最快的消息中间件zeroMQ

ZeroMQ简介

参考1: ØMQ(ZeroMQ)简介

参考2:ZeroMQ研究与应用分析[推荐]

参考3:高性能的通讯库-zeroMQ的几个高性能特征 

参考4:ZeroMQ详细教程,从入门到深入,34篇

4.ZeroMQ的C版本nanomsg,更快

为啥又整出个ZeroMQ的C版本呢?

具本人所知,ZeroMQ作者在实现ZeroMQ后,有一天幡然醒悟“如果用C来实现ZeroMQ会不会更快呢?”所以他就用C语言重新实现了ZeroMQ,即nanomsg,目前是alpha2版本。官方网站:http://nanomsg.org/index.html

关于为什么要用C实现zeromq,其实上面是作者的杜撰。具体原因可以参照以下分析:)

为什么我希望用C而不是C++来实现ZeroMQ

为什么我希望用C而不是C++来实现ZeroMQ(第二篇)


从网上的资料来看,nanomsg确实比zeromq更快。

5.ZeorMQ优点,我为什么使用MQ?

1)使用简单,不需要部署服务器什么的,直接编译后作为一个动态库使用;

2)  编程开发简单

以下是zeromq的一个“helloword”示例:

Server

  1. //  
  2. // Hello World server in C++  
  3. // Binds REP socket to tcp://*:5555  
  4. // Expects "Hello" from client, replies with "World"  
  5. //  
  6. #include <zmq.hpp>  
  7. #include <string>  
  8. #include <iostream>  
  9. #ifndef _WIN32  
  10. #include <unistd.h>  
  11. #else  
  12. #include <windows.h>  
  13. #endif  
  14.   
  15. int main () {  
  16.     // Prepare our context and socket  
  17.     zmq::context_t context (1);  
  18.     zmq::socket_t socket (context, ZMQ_REP);  
  19.     socket.bind ("tcp://*:5555");  
  20.   
  21.     while (true) {  
  22.         zmq::message_t request;  
  23.   
  24.         // Wait for next request from client  
  25.         socket.recv (&request);  
  26.         std::cout << "Received Hello" << std::endl;  
  27.   
  28.         // Do some 'work'  
  29. #ifndef _WIN32  
  30.             sleep(1);  
  31. #else  
  32.         Sleep (1);  
  33. #endif  
  34.   
  35.         // Send reply back to client  
  36.         zmq::message_t reply (5);  
  37.         memcpy ((void *) reply.data (), "World", 5);  
  38.         socket.send (reply);  
  39.     }  
  40.     return 0;  
  41. }  

Client

  1. //  
  2. // Hello World client in C++  
  3. // Connects REQ socket to tcp://localhost:5555  
  4. // Sends "Hello" to server, expects "World" back  
  5. //  
  6. #include <zmq.hpp>  
  7. #include <string>  
  8. #include <iostream>  
  9. int main ()  
  10. {  
  11.     // Prepare our context and socket  
  12.     zmq::context_t context (1);  
  13.     zmq::socket_t socket (context, ZMQ_REQ);  
  14.     std::cout << "Connecting to hello world server..." << std::endl;  
  15.     socket.connect ("tcp://localhost:5555");  
  16.     // Do 10 requests, waiting each time for a response  
  17.     for (int request_nbr = 0; request_nbr != 10; request_nbr++) {  
  18.         zmq::message_t request (6);  
  19.         memcpy ((void *) request.data (), "Hello", 5);  
  20.         std::cout << "Sending Hello " << request_nbr << "..." << std::endl;  
  21.         socket.send (request);  
  22.         // Get the reply.  
  23.         zmq::message_t reply;  
  24.         socket.recv (&reply);  
  25.         std::cout << "Received World " << request_nbr << std::endl;  
  26.     }  
  27.     return 0;  
  28. }  

很简单吧。对比其他MQ要么需要部署Server(ActiveMQ,RabbitMQ),要么复杂Boost::ASIO。

作为反面教材,Boost::ASIO中简单的echo-server例子如下:

Server

  1. //  
  2. // blocking_tcp_echo_server.cpp  
  3. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~  
  4. //  
  5. // Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)  
  6. //  
  7. // Distributed under the Boost Software License, Version 1.0. (See accompanying  
  8. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)  
  9. //  
  10.   
  11. #include <cstdlib>  
  12. #include <iostream>  
  13. #include <boost/bind.hpp>  
  14. #include <boost/smart_ptr.hpp>  
  15. #include <boost/asio.hpp>  
  16. #include <boost/thread/thread.hpp>  
  17.   
  18. using boost::asio::ip::tcp;  
  19.   
  20. const int max_length = 1024;  
  21.   
  22. typedef boost::shared_ptr<tcp::socket> socket_ptr;  
  23.   
  24. void session(socket_ptr sock)  
  25. {  
  26.   try  
  27.   {  
  28.     for (;;)  
  29.     {  
  30.       char data[max_length];  
  31.   
  32.       boost::system::error_code error;  
  33.       size_t length = sock->read_some(boost::asio::buffer(data), error);  
  34.       if (error == boost::asio::error::eof)  
  35.         break// Connection closed cleanly by peer.  
  36.       else if (error)  
  37.         throw boost::system::system_error(error); // Some other error.  
  38.   
  39.       boost::asio::write(*sock, boost::asio::buffer(data, length));  
  40.     }  
  41.   }  
  42.   catch (std::exception& e)  
  43.   {  
  44.     std::cerr << "Exception in thread: " << e.what() << "\n";  
  45.   }  
  46. }  
  47.   
  48. void server(boost::asio::io_service& io_service, unsigned short port)  
  49. {  
  50.   tcp::acceptor a(io_service, tcp::endpoint(tcp::v4(), port));  
  51.   for (;;)  
  52.   {  
  53.     socket_ptr sock(new tcp::socket(io_service));  
  54.     a.accept(*sock);  
  55.     boost::thread t(boost::bind(session, sock));  
  56.   }  
  57. }  
  58.   
  59. int main(int argc, char* argv[])  
  60. {  
  61.   try  
  62.   {  
  63.     if (argc != 2)  
  64.     {  
  65.       std::cerr << "Usage: blocking_tcp_echo_server <port>\n";  
  66.       return 1;  
  67.     }  
  68.   
  69.     boost::asio::io_service io_service;  
  70.   
  71.     using namespace std; // For atoi.  
  72.     server(io_service, atoi(argv[1]));  
  73.   }  
  74.   catch (std::exception& e)  
  75.   {  
  76.     std::cerr << "Exception: " << e.what() << "\n";  
  77.   }  
  78.   
  79.   return 0;  
  80. }  
Client

  1. //  
  2. // blocking_tcp_echo_client.cpp  
  3. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~  
  4. //  
  5. // Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)  
  6. //  
  7. // Distributed under the Boost Software License, Version 1.0. (See accompanying  
  8. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)  
  9. //  
  10.   
  11. #include <cstdlib>  
  12. #include <cstring>  
  13. #include <iostream>  
  14. #include <boost/asio.hpp>  
  15.   
  16. using boost::asio::ip::tcp;  
  17.   
  18. enum { max_length = 1024 };  
  19.   
  20. int main(int argc, char* argv[])  
  21. {  
  22.   try  
  23.   {  
  24.     if (argc != 3)  
  25.     {  
  26.       std::cerr << "Usage: blocking_tcp_echo_client <host> <port>\n";  
  27.       return 1;  
  28.     }  
  29.   
  30.     boost::asio::io_service io_service;  
  31.   
  32.     tcp::resolver resolver(io_service);  
  33.     tcp::resolver::query query(tcp::v4(), argv[1], argv[2]);  
  34.     tcp::resolver::iterator iterator = resolver.resolve(query);  
  35.   
  36.     tcp::socket s(io_service);  
  37.     boost::asio::connect(s, iterator);  
  38.   
  39.     using namespace std; // For strlen.  
  40.     std::cout << "Enter message: ";  
  41.     char request[max_length];  
  42.     std::cin.getline(request, max_length);  
  43.     size_t request_length = strlen(request);  
  44.     boost::asio::write(s, boost::asio::buffer(request, request_length));  
  45.   
  46.     char reply[max_length];  
  47.     size_t reply_length = boost::asio::read(s,  
  48.         boost::asio::buffer(reply, request_length));  
  49.     std::cout << "Reply is: ";  
  50.     std::cout.write(reply, reply_length);  
  51.     std::cout << "\n";  
  52.   }  
  53.   catch (std::exception& e)  
  54.   {  
  55.     std::cerr << "Exception: " << e.what() << "\n";  
  56.   }  
  57.   
  58.   return 0;  
  59. }  
其实Boost算是简单的了,只是有对比才有进步嘛,ZeroMQ的代码仅有一半(不过这似乎说明不了什么问题)。

3)效率高(其实这个不是最重要的,最重要的是1、2)

为了让大伙儿有一个感性的认识,俺特地找来了消息队列软件的性能测评。这是某老外写的一篇帖子(在"这里"),不懂洋文的同学可以看"这里"。连帖子都懒得看的同学,可以直接看下图。

C/C++网络编程总结与ZeroMQ

从图中可以明显看出,ZMQ 相比其它几款MQ,简直是鹤立鸡群啊!性能根本不在一个档次嘛。

分类:

技术点:

相关文章: