zmq多帧消息
zmq可以发送多帧消息,也就是说一个消息可以包含多个帧,这么说可能不太好理解。举一个例子,比如要传输一个文件,如果文件很大,这时候把这个文件传输成一个消息,这个消息肯定会是很多次的传输,那么每一次传输的就是一帧。这就不用规定特殊的协议去传输文件了,所以用zmq传输文件就不用类似ftp那样复杂的文件传输协议了。
多帧消息
现在可以这么处理,比如发送一个多帧消息,这个消息有五个帧,可以把这五个帧封装成一个结构体处理,也可以这样做:
zmq_send (socket, &message, ZMQ_SNDMORE);
...
zmq_send (socket, &message, ZMQ_SNDMORE);
...
zmq_send (socket, &message, 0);
这样发送就是一个有五帧的多帧消息,你也可以发送更多。
当接收一个多帧消息的时候也是很简单的。
while (1) {
zmq_msg_t message;
zmq_msg_init (&message);
zmq_recv (socket, &message, 0);
// 处理一帧消息
zmq_msg_close (&message);
int64_t more;
size_t more_size = sizeof (more);
zmq_getsockopt (socket, ZMQ_RCVMORE, &more, &more_size);
if (!more)
break; // 已到达最后一帧
}
下面是一些注意点:
-
发送端发送多帧消息的时候,只有最后一帧被提交,整个消息才会被发送出去,之前是存在内存中。
-
使用zmq_poll()函数,接收端在接收第一帧的时候,其实整个消息已经都收到了。所以多帧消息是整体传输的。
-
多帧消息每一帧其实就是一个zmq_msg结构对象。
-
应该使用ZMQ_RECVMORE去判断是否还有消息了。
现在可以利用多帧消息做一个文件上传下载工具:
客户端file_send.cpp:
/***************************************************************
*文件传输的客户端
****************************************************************/
#include <zmq.hpp>
#include <string>
#include <iostream>
#include <unistd.h>
#include <stdio.h>
#include <unistd.h>
#include <arpa/inet.h>
#define FILE_FRAME_SIZE 1024
struct file_route_msg{
unsigned int path_len;
char path[255];
};
int read_file_frame(char* filename,int pos,char* buffer,int length)
{
FILE* fp = fopen(filename,"rb");
if (NULL == fp)
{
std::cout<<"fopen failed!"<<std::endl;
return -1;
}
//调整文件指针
fseek(fp,pos,SEEK_SET);
int size = fread(buffer,1,length,fp);
if(size != length)
{
std::cout << "read over!" << std::endl;
}
fclose(fp);
return size;
}
int main(int argc, char const *argv[])
{
//获取文件的路径
if(argc != 2)
{
std::cout << "please input like this:" << argv[0] << " <filepath>"<<std::endl;
}
//判断文件是否存在
if(0 != access(argv[1],R_OK))
{
std::cout<<"file is not exists,please check:"<<argv[1]<<std::endl;
return -1;
}
//开始使用zmq
zmq::context_t context(1);
zmq::socket_t socket(context,ZMQ_PUSH);
socket.setsockopt(ZMQ_SNDTIMEO,0);
socket.connect("tcp://localhost:9000");
//开始发送文件
file_route_msg head;
memset(head,0,sizeof(head));
head.path_len = strlen(argv[1]);
head.path_len = htonl(head.path_len);//转换成网络字节序
memcpy(head.path_len,argv[1],head.path_len);
//发送文件路径
socket.send(&head.path_len,4,ZMQ_SNDMORE);
socket.send(head.path,strlen(head.path),ZMQ_SNDMORE);
int pos = 0;
char buffer[FILE_FRAME_SIZE] = {0};
while(1)
{
buffer[FILE_FRAME_SIZE] = {0};
//从文件中读取一帧
int size = read_file_frame(argv[1],pos,buffer,FILE_FRAME_SIZE);
pos += size;
if(size < 0)
{
std::cout << "read file failed!" << std::endl;
socket.send(NULL,0,0);
break;
}
else if(size == FILE_FRAME_SIZE)
{//完整读取一帧
zmq::message_t msg_frame(buffer,size);
socket.send(msg_frame,ZMQ_SNDMORE);
}
else if(size < FILE_FRAME_SIZE)
{
std::cout << "reaf file content over" <<pos<<" bytes"<<std::endl;
zmq::message_t msg_frame(buffer,size);
socket.send(msg_frame,0);
break;
}
}
socket.close();
content.close();
return 0;
}
服务端:
总结:
使用zmq的多帧消息进行文件传输可以避免定义复杂的传输协议,真香。