管道是两个进程之间的一种通讯方式。比如甲和乙之间有一个管道存在,则甲可以不断往管道中存入消息,而乙则可以顺序获取到消息,若没有消息时则阻塞。在项目实践中,管道是一种很实用的两个进程之间的通讯方式。
在实践中,可能需要这样一种管道,这个管道传输的是可变长度的消息,传输信息的两个进程甲、乙,传输的实际上是一个完整的消息。这个消息由头部和数据构成,头部描述当前消息的长度、类型以及其它信息,而数据则是不固定长度的。若甲写入一个消息,则乙只能获取到这么一个完整的消息或者消息未来到而阻塞等待,不可能仅仅获取到消息的一部分。
在windows环境下,这个管道可以用共享内存+事件实现。共享内存实现两个进程间消息的传输,事件则用于两个进程的交互。这个共享内存可以设置的比较大,比如128K大小,写方进程写入若干不等长的消息,当到头时再返回到共享内存起始位置重头写入。每次写入必是一个完整的消息,在共享内存里是连续存放的。读方进程则从共享内存起始位置开始读取消息,读入一个头部,再读入所有数据。这样每次获取的都是一个完整的消息。当写方进程写入一个消息时,则触发一次事件,通知读方有消息可用。读方在读取的时候需要查看是否有可用消息,若无则等待写方的事件。
这里有这样一个问题,当写方速度比读方速度快时,则可能会有写方写入的消息覆盖未读取的消息,这里可以有两种方法,写方阻塞,直到有可用内存,或者写入失败返回。
这里需要注意的就是,当写方在管道末端写入消息的时候,可能空间不足,这时候只能回到开始位置。需要注意的是,在写入消息的前方可能有进程在读取消息,这时候必须加以检查,否则会出现错误结果。
对于读取消息的进程而言,在管道末端读取最后一个消息的时候,也必须检查,是否为最后一个消息。若是则等待,否则转到起始位置读取,这时候也需要检查。
代码如下所示。
ct_connection.h
#ifndef CT_CONNECTION_H
#define CT_CONNECTION_H
/**
*说明:
*作者:***
*最近修改时间:2014-12-16
*版本:1.7
*/
#ifdef __cplusplus
extern "C"
{
#endif
/************************************************************************/
#define CT_VERSION 0x0107
//通讯关闭
#define CT_CLOSE 9527
//所有的消息类型, 按大小顺序排列,修改时必须保证顺序
#define CT_MSG_ALL {CT_TYPE_1, CT_TYPE_2,CT_TYPE_3,CT_TYPE_4}
//信息类型个数,不包括CLOSE
#define CT_TYPE_NUM 4
#define CT_TYPE_1 1
#define CT_TYPE_2 4
#define CT_TYPE_3 7
#define CT_TYPE_4 9
//消息头部
typedef struct st_CtMessage{
unsigned int type; //类型
void* buf; //数据缓冲区
unsigned long length; //数据长度
unsigned long exData1; //附加数据1
unsigned int exData2; //附加数据2
}CtMessage;
/************************************************************************/
//某类型的头部说明
#define CT_HEADER(pCtMsg, type) ((type*)(&(pCtMsg)->buf))
/************************************************************************/
/************************************************************************/
//信息接收处理函数
//参数:pCtMsg 接收到的信息
//参数:context 用户传入的参数
//返回:若有对该类型的消息进行处理则返回SUCCESS,否则返回FAIL
typedef int(*doReceiveFunc)(const CtMessage*pCtMsg, void*context);
/***/
//场景设置=>实时的通讯通道
#define C2T "c2t"
//实时=>场景设置的通讯通道
#define T2C "t2c"
/***/
//打开发送通道,参数open是通道名
int OpenCtSend(const char*open);
//打开接收通道,参数receive是接收的通道名
int OpenCtReceive(const char*receive, doReceiveFunc func, void*context);
//关闭发送通道,会导致接收方停止接收
int CloseCtSend();
//关闭接收通道
int CloseCtReceive();
int SendCtMessage(CtMessage*pCtMsg);
/*方案一,回调式, 设置接收到数据时的回调函数,第二个参数context是该函数调用时传入的第二个参数*/
int SetCtRecvCallBack(doReceiveFunc func, void*context);//设置接收回调
#ifdef __cplusplus
}
#endif
#endif
ct_connection.c
#include <windows.h>
#include <wchar.h>
#include <stdio.h>
#include <assert.h>
//#include <rtcapi.h>
#include "ct_connection.h"
#include "../commonHeader/util.h"
#include "../commonHeader/charWchar.h"
#include "ct_event_sm.h"
//打印允许