前言:

在linux系统进程间通信的方式有消息,消息队列,管道,内存映射,套接字等多种方式。

在Android系统上进行进程间通信主要是使用Binder,其它的还有共享内存,管道,RPC和Unix Domain等方式。

但是,在linux中常用的消息队列,在Android等系统上并不能直接的使用,Android上常用的Binder,在其他的系统上同样不能使用,如果要在windows,linux,android这样的不同平台上实现同一套进程间命令消息通信机制,并且有较好的移植性.

那么在进行进程间通信设计的时候,首先应该考虑socket方式,这样方便以后设备功能的扩展。

Unix domain基础:

  1. 在使用套接字进行网络连接的时候,我们常用的也就是大家熟悉的是TCP/UDP连接,它属于AF_INET(IPV4)或是AF_INET6(IPV6)地址家族,在linux系统,socket还包括其他的一些地址家族:AF_UNIX,AF_IPX,AF_NETLINK,AF_X25,AF_AX25,AF_ATMPVC,AF_APPLETALK,AF_PACKET,AF_ALG。
  2. 这里我们会使用到AF_UNIX地址家族,也就是Unix Domain Socket。可以确认的是在linux,Android,Mac和window10系统都支持AF_UNIX地址家族,这方便我们代码的移植复用。AF_UNIX类似于管道,依赖路径名标识发送方和接收方,从而实现本地进程间通信。即发送数据时,指定接收方绑定的路径名,操作系统根据该路径名可以直接找到对应的接收方,并将原始数据直接拷贝到接收方的内核缓冲区中,并上报给接收方进程进行处理。同样的接收方可以从收到的数据包中获取到发送方的路径名,并通过此路径名向其发送数据。
  3. AF_INET(TCP/UDP)需经过多个协议层的编解码,消耗系统cpu,并且数据传输需要经过网卡,受到网卡带宽的限制。AF_UNIX数据到达内核缓冲区后,由内核根据指定路径名找到接收方socket对应的内核缓冲区,直接将数据拷贝过去,不经过协议层编解码,节省系统cpu,并且不经过网卡,因此不受网卡带宽的限制。
  4. AF_UNIX的传输速率远远大于AF_INET。

设计思路:

(1)协议选择

由于Unix Domain传输速率大消耗资源少,但它只适用于本地之间传输,AF_INET Domain 消耗资源多,传输相对速率较低。为了高效实现跨设备多进程间通讯,可以同时使用Unix Domain 和 AF_INET Domain进程系统进程间通信。本地进程使用Unix Domain传输,远程设备使用AF_INET传输,为保证命令消息的可靠发送和接收,可以选择TCP传输。

(2)多进程间通信实现

要现实某个进程往其他任意一个进程间发送数据,中间必须建立一个服务端,服务端用来做数据路由,用来减少网络端口的连接同时简化各进程间数据的接收和发送。另外还可以通过路由实现广播的功能。

(3)命令消息格式设计

在实际应用中,多进程间的通信一般是用来实现各进程间的命令消息交互,为了保证命令消息的正确和完整,一般会对命令消息进行封装,这样也方便接收端在接收到数据的时候进行命令的解析。该命令消息的格式可以定义如下:

基本的通信网络模型如下:

功能实现:

(1)服务端(Router)

  1. 与客户端建立映射

        服务端与每一个客户端之间,只建立了一个连接。那么服务端要怎么知道当前是哪个客户端与服务端建立连接的呢?服务端又是如何知道要将接收到的数据发到对应的那个网络连接中去呢?这里我们需要为每一个客户端分配一个固定的地址,服务端通过这个地址来判断是哪个客户端请求建立连接。

对于TCP连接,可以通过IP和端口来确定绑定地址,对于Unix Domain客户端,可以通过路径名来绑定地址。地址定义和绑定如下,这里预定义了20本地进程(客户端),两个远程设备,每个设备10个进程(客户端):

/************************************************************
*Copyright (C),lcb0281at163.com lcb0281atgmail.com
*FileName: ipc_common.h
*BlogAddr: https://blog.csdn.net/li_wen01
*Description:
socket进程间通信数据结构及参数定义
包括跨设备间TCP进程间通信和本地Unix Domain
Socket进程间通信
*Date: 2019-08-03
*Author: Caibiao Lee
*Version: V1.0
*Others:
*History:
***********************************************************/
#ifndef _IPC_COMMON_H_
#define _IPC_COMMON_H_ /**跨设备TCP socket进程间通信**/
#define MAX_EXTARN_DEV_NUM 2 /**最大连接的网络设备数**/
#define EACH_TCP_DEV_MAX_CLIENT_NUM 10 /**每个外接网络设备客户端数**/
#define TCP_SERVER_PORT 6666 /**TCP连接服务端IP端口**/ /**TCP连接最大支持客户端数**/
#define TCP_SERVER_LISTEN_MAX_NUM ((MAX_EXTARN_DEV_NUM)*(EACH_TCP_DEV_MAX_CLIENT_NUM)) #define TCP_SERVER_IP "192.168.1.111"
#define TCP_CLIENT_DEVICE1_IP "192.168.1.111" /**网络设备1 IP地址**/
#define TCP_CLIENT_DEVICE2_IP "192.168.1.112" /**网络设备2 IP地址**/
#define TCP_CLIENT_DEVICE1_NO 1
#define TCP_CLIENT_DEVICE2_NO 2 /**网络设备各客户端TCP端口**/
#define TCP_DEVICE_CLIENT0_POART 9000
#define TCP_DEVICE_CLIENT1_POART 9001
#define TCP_DEVICE_CLIENT2_POART 9002
#define TCP_DEVICE_CLIENT3_POART 9003
#define TCP_DEVICE_CLIENT4_POART 9004
#define TCP_DEVICE_CLIENT5_POART 9005
#define TCP_DEVICE_CLIENT6_POART 9006
#define TCP_DEVICE_CLIENT7_POART 9007
#define TCP_DEVICE_CLIENT8_POART 9008
#define TCP_DEVICE_CLIENT9_POART 9009
#define TCP_DEVICE_CLIENT_MAX_POART 9009 /**本地Unix Domain Socket进程间通信**/
#define MAX_UDS_CLIENT_NUM 20 /**最大socket域客户端数,对应最大本地通讯进程数**/
#define SERVER_PATH "../tmp/server_socket" /**socket域服务端文件**/
#define CLIENT_PACHT "../tmp/client_socket" /**socket域客户端文件前缀**/ /**本地客户端模块序号定义**/
#define CLIENT_MIN_ADDR 1
#define LOCAL_CLIENT_1_ADDR 1
#define LOCAL_CLIENT_2_ADDR 2
#define LOCAL_CLIENT_3_ADDR 3
#define LOCAL_CLIENT_4_ADDR 4
#define LOCAL_CLIENT_5_ADDR 5
#define LOCAL_CLIENT_6_ADDR 6
#define LOCAL_CLIENT_7_ADDR 7
#define LOCAL_CLIENT_8_ADDR 8
#define LOCAL_CLIENT_9_ADDR 9
#define LOCAL_CLIENT_10_ADDR 10
#define LOCAL_CLIENT_11_ADDR 11
#define LOCAL_CLIENT_12_ADDR 12
#define LOCAL_CLIENT_13_ADDR 13
#define LOCAL_CLIENT_14_ADDR 14
#define LOCAL_CLIENT_15_ADDR 15
#define LOCAL_CLIENT_16_ADDR 16
#define LOCAL_CLIENT_17_ADDR 17
#define LOCAL_CLIENT_18_ADDR 18
#define LOCAL_CLIENT_19_ADDR 19
#define LOCAL_CLIENT_MAX_ADDR 19 /**网络设备1客户端模块序号定义**/
#define DEV_CLIENT_MIN_ADDR 20
#define DEV1_CLIENT_0_ADDR 20
#define DEV1_CLIENT_1_ADDR 21
#define DEV1_CLIENT_2_ADDR 22
#define DEV1_CLIENT_3_ADDR 23
#define DEV1_CLIENT_4_ADDR 24
#define DEV1_CLIENT_5_ADDR 25
#define DEV1_CLIENT_6_ADDR 26
#define DEV1_CLIENT_7_ADDR 27
#define DEV1_CLIENT_8_ADDR 28
#define DEV1_CLIENT_9_ADDR 29 /**网络设备1客户端模块序号定义**/
#define DEV2_CLIENT_0_ADDR 30
#define DEV2_CLIENT_1_ADDR 31
#define DEV2_CLIENT_2_ADDR 32
#define DEV2_CLIENT_3_ADDR 33
#define DEV2_CLIENT_4_ADDR 34
#define DEV2_CLIENT_5_ADDR 35
#define DEV2_CLIENT_6_ADDR 36
#define DEV2_CLIENT_7_ADDR 37
#define DEV2_CLIENT_8_ADDR 38
#define DEV2_CLIENT_9_ADDR 39
#define DEV_CLIENT_MAX_ADDR 39
#define CLIENT_MAX_ADDR 39 extern const char * gc_au8DeviceNoMap[MAX_EXTARN_DEV_NUM];
extern const unsigned int gc_as32DeviceModuleMap[TCP_SERVER_LISTEN_MAX_NUM][3];
  1. 客户端与socket连接ID映射

        服务端(Router)程序起来之后,建立两个线程,分别去监听TCP和Unix Domain客户端的连接请求。与客户端建立连接后,将相应的socket ID保存到一个数组中去,将数组地址与客户端地址设计成相同,这样在服务端发送数据的时候,可以通过数组地址直接快速查找到对应客户端的socket ID 。通过直接地址映射会比遍历数组会快很多,可以节省系统开销。

  2. 数据路由

        服务端接收到数据之后,直接获取该命令消息的目的地址,然后再将该命令消息包发送到对应的进程。在使用socket转发数据的时候,需要注意,在socket接收一次数据,可能不是一个完整的数据包,也有可能该数据包里面有多条命令消息,这里在做数据处理的时候需要特别注意。

服务端server.c实现代码比较长,这里就不贴出来了,有兴趣的可以到我GitHub上查看:https://github.com/licaibiao/IPC_Socket

(2)客户端(各进程)

为方便客户端对消息命令的处理,客户端设计成每次接收到的数据是一个完整的命令消息,并且只有一条命令消息。为方便客户端的使用,将客户端的网络连接和网络状态检测同时封装到客户端数据接收和数据发送两个接口里面。比如在数据发送的时候,会去判断连接是否建立获取连接已经断开,如果网络异常则重新建立连接。

客户端ipc_interface.h接口定义如下

/************************************************************
*Copyright (C),lcb0281at163.com lcb0281atgmail.com
*FileName: ipc_interface.c
*BlogAddr: https://blog.csdn.net/li_wen01
*Description:客户端网络连接,数据收发,消息解析函数接口定义实现
*Date: 2019-08-03
*Author: Caibiao Lee
*Version: V1.0
*Others:
*History:
***********************************************************/
#ifndef _IPC_INTERFACE_H_
#define _IPC_INTERFACE_H_ #include "ipc_common.h"
#include "ipc_msgstruct.h" /**获取流水号**/
int IPCP_Arch_Msg_AnalyzeGetFlowNum(); /**获取应答流水号**/
int IPCP_Arch_Msg_AnalyzeGetACKResult(ARCH_MSG_S *pstMsg); /**获取流水号地址**/
int IPCP_Arch_Msg_AnalyzeGetRecFlow(ARCH_MSG_S *pstMsg); /**获取源地址**/
int IPCP_Arch_Msg_ChangeSrcAddr(ARCH_MSG_S *pstMsg,unsigned char SrcAddr); /**获取目标地址**/
int IPCP_Arch_Msg_ChangeTargAddr(ARCH_MSG_S *pstMsg,unsigned char TargAddr); /**获取源地址**/
int IPCP_Arch_Msg_AnalyzeGetSrcAddr(ARCH_MSG_S *pstMsg); /**获取目标地址**/
int IPCP_Arch_Msg_AnalyzeGetTargAddr(ARCH_MSG_S *pstMsg); /**获取消息ID**/
int IPCP_Arch_Msg_AnalyzeGetCmdID(ARCH_MSG_S *pstMsg); /**获取内容长度**/
int IPCP_Arch_Msg_AnalyzeGetLen(ARCH_MSG_S *pstMsg); /**获取消息内容开始位置**/
void IPCP_Arch_Msg_PlatformStartP(unsigned char ** p, unsigned char* Data); /**发送数据**/
int IPCP_Arch_Msg_PackSend(int s32ModuleAddr, MSG_PACK_S *pstMsg); /**读取数据**/
int IPCP_Arch_Msg_Recv(int s32ModuleAddr,ARCH_MSG_S *pstMsg); #endif

(3)命令消息封装

为了更好地发送和接收命令消息,应该将命令消息基于网络传输协议之上再进行一层消息的封装,添加消息头标签,源地址目的地址和校验等信息。为了各进程间消息的更好识别和传输,应该对每条命令的数据结构进行定义,这样在解析的时候才不会出现参数对应不上的问题。我这里预定义了几个命令消息,用来测试该方法的稳定性。

ipc_msgstruct.h

/************************************************************
*Copyright (C),lcb0281at163.com lcb0281atgmail.com
*FileName: ipc_msgstruct.h
*BlogAddr: https://blog.csdn.net/li_wen01
*Description:命令消息结构体定义和解析
*Date: 2019-08-03
*Author: Caibiao Lee
*Version: V1.0
*Others:
*History:
***********************************************************/
#ifndef _IPC_MSGSTRUCT_H_
#define _IPC_MSGSTRUCT_H_ /**数据结构重定义**/
typedef unsigned char byte;
typedef signed char INT8S;
typedef signed int INT32S;
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned int DWORD;
typedef unsigned char INT8U;
typedef unsigned short INT16U;
typedef unsigned int INT32U; #define IPCP_TRUE 0
#define IPCP_FALSE 1 #define ARRAY_SIZE(_A) (sizeof(_A) / sizeof((_A)[0])) /**通讯协议位定义**/
/************************************************************************************/
/** | 2Byet | Byet | Byet | 2Byet | 2*Byet |2Byet |N*Byet | Byet | 2Byet | **/
/** | 0xa5a5 | 源地址| 目标地址| 流水号 | 消息ID |消息长度 |消息体 | 校验 | 0x5a5a | **/
/** | 0 1 | 2 | 3 | 4 5 | 6 7 | 8 9 |10 | 10+N | 10+N+1 | **/
/************************************************************************************/
#define MSG_SRC_ADDR (2)
#define MSG_TAR_ADDR (3)
#define MSG_SERIAL_NUM_ADDR (4)
#define MSG_CMID_ADDR (6)
#define MSG_MSG_LEN_ADDR (8)
#define MSG_ACK_CMID_ADDR (10)
#define MSG_ACK_SERIAL_NUM_ADDR (12)
#define MSG_ACK_RES_ADDR (14)
#define MSG_CONTENT_OFFSET (10) /**消息内容开始位置**/ #define MSG_HEAD_LEN (10)
#define MSG_END_LEN (3)
#define MSG_ARCH_MSG_LEN (13) #define QUEUE_MSG_HEAD 0xa5a5
#define QUEUE_MSG_END 0x5a5a typedef struct MsgPack
{
unsigned char SrcAddr;
unsigned char TargAddr;
unsigned short Len;
unsigned char *Data;
unsigned short CmdId;
}MSG_PACK_S; #define MSG_TEXT_SIZE (2048) typedef struct ARCH_MSG
{
unsigned int MsgLen;
unsigned char SomeText[MSG_TEXT_SIZE];
}ARCH_MSG_S; /*通用IPCP指令*/
#define QUEUE_DEBUG_CMD1 (0x0001)
#define QUEUE_DEBUG_CMD2 (0x0002)
#define QUEUE_DEBUG_CMD3 (0x0003)
#define QUEUE_DEBUG_CMD4 (0x0004)
#define QUEUE_DEBUG_CMD5 (0x0005)
#define QUEUE_DEBUG_CMD6 (0x0006)
#define QUEUE_DEBUG_CMD7 (0x0007)
#define QUEUE_DEBUG_CMD8 (0x0008)
#define QUEUE_DEBUG_CMD9 (0x0009)
#define QUEUE_DEBUG_CMDA (0x000A)
#define QUEUE_DEBUG_MAX_CMD (0x000A) /**消息定义**/
typedef struct
{
DWORD u32Alarm; /* 报警 */
DWORD u32Status; /* 状态 */
DWORD u32Latitude; /* 纬度,百万分之一度 */
DWORD u32Longtitude; /* 经度,百万分之一度 */
WORD u16Altitude; /* 高程,米 */
WORD u16SpeedX10; /* gps速度,1/10 km/h */
WORD u16Direct; /* 方向 */
BYTE arrCardNo[16]; /* 机动车牌号码 */
WORD u16SensorSpeed; /* 脉冲速度,1/10 km/h */
BYTE u8CarCor; /* 车牌颜色 */
}__attribute__ ((__packed__))MSG_0X0001_S; typedef struct
{
unsigned int u32FileId; /**文件id,不为0时,按文件id查找**/
unsigned char u8DeleteFlag; /**删除标志:0:保留;1:删除**/
unsigned char u8StopFlag; /**停止上传标志:0:保留;1:停止上传 **/
unsigned char u8srcPlat; /**下发命令的平台地址**/
}__attribute__ ((__packed__))MSG_0X0002_S; typedef struct
{
unsigned char u8Type; /**0人脸识别结果,1人头个数识别**/
unsigned char u8Result; /**type为0时,0成功,1失败**/
}__attribute__ ((__packed__))MSG_0X0003_S; typedef struct
{
unsigned char u8Interval; /**时间间隔**/
unsigned int u32Duration; /**持续时间**/
}__attribute__ ((__packed__))MSG_0X0004_S; typedef struct
{
DWORD u32MultiId; /**多媒体ID,小端内存**/
WORD u16TotalNum; /**下发拍照的总数**/
WORD u16CurNum; /**当前图片序号**/
BYTE u8PlatAddr; /**平台地址**/
}__attribute__ ((__packed__))MSG_0X0005_S; typedef struct
{
DWORD u32ModeEvent; /**休眠唤醒事件:1:休眠,2:唤醒*/
}__attribute__ ((__packed__))MSG_0X0006_S; typedef struct
{
DWORD u32ParaId; /* 参数id: 0x1000010C*/
BYTE u8ParaLen; /* 参数长度: 4 */
unsigned long long u64AlarmFlag;/* bit*/ }__attribute__ ((__packed__))MSG_0X0007_S; typedef struct
{
DWORD u32ParaId; /* 参数id: 0x1000010C*/
BYTE u8ParaLen; /* 参数长度: 4 */
DWORD u32TimeOut; /* 单位为秒*/
}__attribute__ ((__packed__))MSG_0X0008_S; typedef struct
{
DWORD u32ParaId; /* 参数id: 0x30000007*/
BYTE u8ParaLen; /* 参数长度: 4 */
DWORD u32Payload; /* 参数值:96:h264编码, 265:h265编码*/
}__attribute__ ((__packed__))MSG_0X0009_S; typedef struct
{
BYTE u8ParaNum; /* 参数总数 */
}__attribute__ ((__packed__))MSG_0X000A_S; typedef int (*HandleHook_Func)(unsigned char*, unsigned short,unsigned char); typedef struct
{
unsigned int u32MsgID;
HandleHook_Func pFuncHandle;
}MSG_HANDLE_HOOK_S; int IPCPMsg_Debug_Cmd1(unsigned char *pu8SomeText, unsigned short u16Len, unsigned char u8SrcAddr);
int IPCPMsg_Debug_Cmd2(unsigned char *pu8SomeText, unsigned short u16Len, unsigned char u8SrcAddr);
int IPCPMsg_Debug_Cmd3(unsigned char *pu8SomeText, unsigned short u16Len, unsigned char u8SrcAddr);
int IPCPMsg_Debug_Cmd4(unsigned char *pu8SomeText, unsigned short u16Len, unsigned char u8SrcAddr);
int IPCPMsg_Debug_Cmd5(unsigned char *pu8SomeText, unsigned short u16Len, unsigned char u8SrcAddr);
int IPCPMsg_Debug_Cmd6(unsigned char *pu8SomeText, unsigned short u16Len, unsigned char u8SrcAddr);
int IPCPMsg_Debug_Cmd7(unsigned char *pu8SomeText, unsigned short u16Len, unsigned char u8SrcAddr);
int IPCPMsg_Debug_Cmd8(unsigned char *pu8SomeText, unsigned short u16Len, unsigned char u8SrcAddr);
int IPCPMsg_Debug_Cmd9(unsigned char *pu8SomeText, unsigned short u16Len, unsigned char u8SrcAddr);
int IPCPMsg_Debug_CmdA(unsigned char *pu8SomeText, unsigned short u16Len, unsigned char u8SrcAddr); #endif

测试:

为了测试该方法的可靠和稳定性,有新建2个本地进程和2个远程设备进程,让他们随机时间随机往某个进程发送命令,看是否会出现命令消息丢失或是解析错误的问题。其中本地一个客户端的实现如下:

/************************************************************
*Copyright (C),lcb0281at163.com lcb0281atgmail.com
*FileName: local_client1.c
*BlogAddr: https://blog.csdn.net/li_wen01
*Description:本地模块进程1
*Date: 2019-08-03
*Author: Caibiao Lee
*Version: V1.0
*Others:
*History:
***********************************************************/
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <sys/un.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h> #include "ipc_interface.h"
#include "ipc_msgstruct.h"
#include "ipc_common.h" #define CLIENT_MODULE_ADDR LOCAL_CLIENT_1_ADDR
#define LOCAL1_DELAY_FOR_DEBUG_US (1000*1000) /**消息处理**/
MSG_HANDLE_HOOK_S g_astLocalMsgTable[] =
{
{QUEUE_DEBUG_CMD1, IPCPMsg_Debug_Cmd1},
{QUEUE_DEBUG_CMD2, IPCPMsg_Debug_Cmd2},
{QUEUE_DEBUG_CMD3, IPCPMsg_Debug_Cmd3},
{QUEUE_DEBUG_CMD4, IPCPMsg_Debug_Cmd4},
{QUEUE_DEBUG_CMD5, IPCPMsg_Debug_Cmd5},
{QUEUE_DEBUG_CMD6, IPCPMsg_Debug_Cmd6},
{QUEUE_DEBUG_CMD7, IPCPMsg_Debug_Cmd7},
{QUEUE_DEBUG_CMD8, IPCPMsg_Debug_Cmd8},
{QUEUE_DEBUG_CMD9, IPCPMsg_Debug_Cmd9},
{QUEUE_DEBUG_CMDA, IPCPMsg_Debug_CmdA},
{0,NULL},
}; /**********************************
linux ctrl + C 会产生 SIGINT信号
接收到SIGINT 信号进入该函数
**********************************/
void stop(int signo)
{
int i = 0;
printf(" stop \n"); _exit(0);
} /*********************************************
当客户端断开连接的时候,
在服务端socket send进程可以收到收到信号SIGPIPE,
收到SIGPIPE信号进入该函数结束创建的线程。
**********************************************/
void signal_pipe(int signo)
{ } /********************************************************
Function: ClientSendMsg
Description: 客户端(某一进程)发送数据包
Input:
s32TargModuleAddr : 需要发送到的模块地址
OutPut: none
Return: 0 成功;非0 异常
Others:
Author: Caibiao Lee
Date: 2019-08-03
*********************************************************/
int ClientSendMsg(int s32TargModuleAddr)
{
int l_s32Res = 0;
MSG_PACK_S l_stMsg = {0};
MSG_0X0001_S l_stMsgSend = {0}; if((s32TargModuleAddr<LOCAL_CLIENT_1_ADDR)&&(s32TargModuleAddr>LOCAL_CLIENT_MAX_ADDR))
{
printf("%s %d input para error %d \n",__FUNCTION__,__LINE__,s32TargModuleAddr);
return -1;
} l_stMsgSend.arrCardNo;
l_stMsgSend.u16Altitude = 0x66;
l_stMsgSend.u16Direct = 0x11;
l_stMsgSend.u16SensorSpeed= 0x12;
l_stMsgSend.u16SpeedX10 = 0x13;
l_stMsgSend.u32Alarm = 0xbb;
l_stMsgSend.u32Latitude = 0xaa;
l_stMsgSend.u32Longtitude = 0x15;
l_stMsgSend.u32Status = 0x01;
l_stMsgSend.u8CarCor = 0x08; l_stMsg.CmdId = QUEUE_DEBUG_CMD1;
l_stMsg.Data = (unsigned char*)&l_stMsgSend;
l_stMsg.Len = sizeof(MSG_0X0001_S);
l_stMsg.SrcAddr = CLIENT_MODULE_ADDR;
l_stMsg.TargAddr = s32TargModuleAddr;
IPCP_Arch_Msg_PackSend(CLIENT_MODULE_ADDR,&l_stMsg); return 0; } /********************************************************
Function: ClientRecvMsg
Description: 客户端(某一进程)接收数据包,并对数据包进行解析
Input:
OutPut: none
Return: 0 成功;非0 异常
Others:
Author: Caibiao Lee
Date: 2019-08-03
*********************************************************/
int ClientRecvMsg(void)
{
int i = 0;
int l_s32RecvLen = 0;
int l_s32SrcAddr = 0;
int l_s32CmdId = 0;
int l_s32TextLen = 0;
int l_s32FlowNum = 0;
int l_u32IPCPMsgNum = 0; unsigned char *l_pu8MsgBody = NULL;
ARCH_MSG_S l_stMsg = {0}; l_s32RecvLen = IPCP_Arch_Msg_Recv(CLIENT_MODULE_ADDR,&l_stMsg); if(l_s32RecvLen>0)
{
IPCP_Arch_Msg_PlatformStartP(&l_pu8MsgBody, l_stMsg.SomeText); l_u32IPCPMsgNum = ARRAY_SIZE(g_astLocalMsgTable);
l_s32SrcAddr = IPCP_Arch_Msg_AnalyzeGetSrcAddr(&l_stMsg);
l_s32CmdId = IPCP_Arch_Msg_AnalyzeGetCmdID(&l_stMsg);
l_s32TextLen = IPCP_Arch_Msg_AnalyzeGetLen(&l_stMsg);
l_s32FlowNum = IPCP_Arch_Msg_AnalyzeGetRecFlow(&l_stMsg); printf("l_s32SrcAddr = %d \n",l_s32SrcAddr);
printf("l_s32CmdId = %d \n",l_s32CmdId);
printf("l_s32TextLen = %d \n",l_s32TextLen);
printf("l_s32FlowNum = %d \n",l_s32FlowNum); for(i = 0; i < l_u32IPCPMsgNum; i++)
{
if(l_s32CmdId == g_astLocalMsgTable[i].u32MsgID)
{
if(NULL!=g_astLocalMsgTable[i].pFuncHandle)
{
g_astLocalMsgTable[i].pFuncHandle(l_pu8MsgBody, l_s32TextLen,l_s32SrcAddr);
}
break;
}
}
}
return 0;
} int main(int argc,char *argv[])
{
int l_s32Delay = 0;
int l_s32Addr = 0;
int l_as32SendAddr[3] ={0}; l_as32SendAddr[0] = LOCAL_CLIENT_2_ADDR;
l_as32SendAddr[1] = DEV1_CLIENT_1_ADDR,
l_as32SendAddr[2] = DEV1_CLIENT_2_ADDR; /**注册 SIGPIPE信号**/
signal(SIGPIPE,signal_pipe); /**注册SIGINT 信号**/
signal(SIGINT,stop); while(1)
{ l_s32Addr = IPCP_GetRandomReal(0,3);
if((l_s32Addr>=0)&&(l_s32Addr<=2))
{
ClientSendMsg(l_as32SendAddr[l_s32Addr]);
} ClientRecvMsg(); l_s32Delay = IPCP_GetRandomReal(50,100);
usleep(l_s32Delay*1000);
}
return 0;
}

实际运行结果如下:

上面测试是以50~100ms随机发送一包数据来测试,运行24小时后未检测到异常。

总结:

(1)阻塞与非阻塞问题

实际客户端在接收和发送,应该是需要非阻塞的模式,正常的数据接收和发送不能影响客户端原本的业务逻辑处理。在非阻塞状态下数据的接收和发送都需要特别的注意。

在发送的时候,因为是非阻塞的,当对方接收缓存满了的时候,发送端会收到EAGAIN的错误码,该错误码是让你进行数据的重新发送。在我这里我是设计发送失败会重新发送3遍,然后3次发送后还是失败,那么这包数据就会被丢弃。

在接收的时候,我们需要去判断对方网络是否还处于连接的状态,可以使用select实现,也可以通过接收PIPE信号来判断,为了更好的数据处理,在我这里是使用select来判断对方网络是否已经断开连接。

(2)缓存大小问题

每建立一个socket,在连接或是绑定之前,我们都可以设置该socket连接的收发缓存大小,可以根据实际应用的发送峰值来判断应该设置多大的缓存。在我Ubuntu16.04系统下,默认的TCP和UnixDomain收发缓存大小如下:

biao@ubuntu:~/test/ipcp_socket/server$ ./server
create unix domin socket lpthread success
create TCP socket lpthread success
create read write lpthread seccess
TCP SO_RCVBUF = 87380
TCP SO_SNDBUF = 16384
server waiting for tcp client connect
Unix Domain SO_RCVBUF = 212992
Unix Domain SO_SNDBUF = 212992
server waiting for unix domain client connect

工程下载:

以上,完整代码文件如下:

biao@ubuntu:~/test/ipcp_socket$ tree
.
├── common
│   ├── ipc_common.c
│   ├── ipc_common.h
│   ├── ipc_interface.c
│   ├── ipc_interface.h
│   ├── ipc_msgstruct.c
│   └── ipc_msgstruct.h
├── device1
│   ├── device1_client1.c
│   ├── device1_client2.c
│   └── Makefile
├── local
│   ├── local_client1.c
│   ├── local_client2.c
│   └── Makefile
├── server
│   ├── Makefile
│   └── server.c
└── tmp
├── client_socket1
├── client_socket2
└── server_socket 5 directories, 17 files
biao@ubuntu:~/test/ipcp_socket$

liwen01 公众号中回复 网络编程 获取工程代码,本章代码工程名为:ipcp_socket_20190815_V2.tar.gz

---------------------------End---------------------------
长按识别二维码
关注 liwen01 公众号

一种基于Unix Domain和TCP连接的跨设备多进程间通信的方法的更多相关文章

  1. 一种基于重载的高效c#上图片添加文字图形图片的方法

    在做图片监控显示的时候,需要在图片上添加文字,如果用graphics类绘制图片上的字体,实现图像上添加自定义标记,这种方法经验证是可行的,并且在visual c#2005 编程技巧大全上有提到,但是, ...

  2. Base64就是一种 基于64个可打印字符来表示二进制数据的表示方法

    Base64编码是从二进制到字符的过程. Base64编码主要用在传输.存储.表示二进制等领域,还可以用来加密,但是这种加密比较简单. byte[] byteArray = Encoding.UTF8 ...

  3. UNIX网络编程——TCP连接的建立和断开、滑动窗口

    一.TCP段格式: TCP的段格式如下图所示: 源端口号与目的端口号:源端口号和目的端口号,加上IP首部的源IP地址和目的IP地址唯一确定一个TCP连接. 序号:序号表示在这个报文段中的第一个数据字节 ...

  4. nginx和php-fpm通信的两种方式 unix socket和TCP

    nginx和fastcgi的通信方式有两种,一种是TCP 一种是unix socket TCP使用的是 127.0.0.1:9000端口,将fastcgi_pass参数修改为127.0.0.1:900 ...

  5. nginx、php-fpm默认配置与性能–TCP socket还是unix domain socket【转】

    原文地址:https://www.cnxct.com/default-configuration-and-performance-of-nginx-phpfpm-and-tcp-socket-or-u ...

  6. Nginx 中 fastcgi_pass 监听端口 unix socket和tcp socket差

    Nginx 中 fastcgi_pass 监听端口 unix socket和tcp socket差别   Nginx连接fastcgi的方式有2种:unix domain socket和TCP,Uni ...

  7. php, hhvm与odp & Unix domain Socket方式

    接上一篇,复习一下 启动php或hhvm: php/sbin/php-fpm start hhvm/bin/hhvm_control start 启动nginx或lighttpd: webserver ...

  8. 一种基于Qt的可伸缩的全异步C/S架构服务器实现(流浪小狗,六篇,附下载地址)

    本文向大家介绍一种基于Qt的伸缩TCP服务实现.该实现针对C/S客户端-服务集群应用需求而搭建.连接监听.数据传输.数据处理均在独立的线程池中进行,根据特定任务不同,可安排负责监听.传输.处理的线程数 ...

  9. 一种基于Qt的可伸缩的全异步C/S架构server实现(一) 综述

    本文向大家介绍一种基于Qt的伸缩TCP服务实现.该实现针对C/Sclient-服务集群应用需求而搭建. 连接监听.传输数据.数据处理均在独立的线程池中进行,依据特定任务不同,可安排负责监听.传输.处理 ...

  10. libpqxx接口的在linux下的使用,解决psql:connections on Unix domain socket "/tmp/.s.PGSQL.5432"错误

    在项目中使用postgresql数据库时要求在windows和linux双平台兼容.于是在windows下使用的接口在linux下爆出异常: psql:connections on Unix doma ...

随机推荐

  1. IDEA创建MyBatis项目--实现简单的查操作

    IDEA创建MyBatis项目--实现简单的查操作 1.创建一个maven工程,不使用模板 2.通过maven加载Mybatis依赖包 在pom文件中导入maven坐标 <dependencie ...

  2. Windows Server 2012 R2 远程桌面服务部署指南

    著作权归作者所有:来自51CTO博客作者mabofeng的原创作品,请联系作者获取转载授权,否则将追究法律责任 01-Windows Server 2012 R2 远程桌面服务部署指南文章来源:htt ...

  3. 踩坑ffmpeg录制的mp4无法在浏览器上播放

    前言 使用ffmpeg编译好的程序在电脑上进行音视频转换,可以参考这篇:<windows电脑FFmpeg安装教程手把手详解_windows安装ffmpeg>,而我们要做的是在游戏引擎中集成 ...

  4. Mysql性能优化这5点你知道吗?简单却容易被初学者忽略!

    Mysql性能优化这5点你知道吗?简单却容易被初学者忽略! 文编|JavaBuild 哈喽,大家好呀!我是JavaBuild,以后可以喊我鸟哥,嘿嘿!俺滴座右铭是不在沉默中爆发,就在沉默中灭亡,一起加 ...

  5. 降低node版本,怎么降低node版本

    降低node版本,怎么降低node版本? 部分老旧项目需要使用低版本的node,网上很多是无效的,高版本无法直接安装低版本node,但是低版本nodejs可以安装部分高版本node,从而达到升级效果. ...

  6. 文心一言 VS chatgpt (21)-- h264编码一帧数据的流程是怎样的?

    h264编码一帧数据的流程是怎样的? 文心一言: H.264编码将一帧数据分成多个块,其中每个块可以单独进行编码.编码的过程包括预测.变换和量化等步骤. 具体流程如下: 1.帧内预测:对一帧视频进行编 ...

  7. JavaScript异步编程4——Promise错误处理

    目录 1. 概述 2. 详论 3. 参考 1. 概述 在上一篇文章<JavaScript异步编程3--Promise的链式使用>中,通过Promise的链式使用,避免程序中多次嵌套回调(回 ...

  8. 十分钟从入门到精通(上)——OBS权限配置

    [摘要]作为公有云的数据底座,大量的应用场景产生的数据都会存储到OBS对象存储服务中,如直播.电商.大数据可视化.机器学习.物联网等.作为公有云的海量存储基础服务, OBS提供了灵活的权限配置功能,解 ...

  9. 【华为云技术分享】解密如何使用昇腾AI计算解决方案构建业务引擎

    摘要:昇腾AI计算解决方案以极致算力,端边云融合.全栈创新,开放生态的硬核实力.用户可以使用标准的Matrix接口实现业务引擎,对外释放昇腾AI加速能力. 从卷积神经网络中的矩阵乘法(GEMM)说起 ...

  10. 一文读懂GaussDB(openGauss) 的六大关键技术特性

    摘要:更为深入地介绍了GaussDB(openGauss)的关键特性.成功案例. GaussDB(openGauss)是深度融合华为在数据库领域多年的经验,结合企业级场景需求,推出的新一代企业级分布式 ...