首页 > 系统相关 >一种基于Unix Domain和TCP连接的跨设备多进程间通信的方法

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

时间:2023-04-20 19:48:08浏览次数:53  
标签:Domain ADDR int unsigned TCP 间通信 CLIENT MSG define

前言:

在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 公众号

标签:Domain,ADDR,int,unsigned,TCP,间通信,CLIENT,MSG,define
From: https://www.cnblogs.com/liwen01/p/17337905.html

相关文章

  • ONVIF网络摄像头(IPC)客户端开发—RTSP RTCP RTP加载H264视频流
    前言:RTSP,RTCP,RTP一般是一起使用,在FFmpeg和live555这些库中,它们为了更好的适用性,所以实现起来非常复杂,直接查看FFmpeg和Live555源代码来熟悉这些协议非常吃力,这里将它们独立出来实现,以便更好的理解协议。本文主要介绍RTSP,RTCP,RTP加载H264数据流。说明:(1)大华IPC摄像头作为服......
  • ONVIF网络摄像头(IPC)客户端开发—RTSP RTCP RTP加载AAC音频流
    前言:RTSP,RTCP,RTP一般是一起使用,在FFmpeg和live555这些库中,它们为了更好的适用性,所以实现起来非常复杂,直接查看FFmpeg和Live555源代码来熟悉这些协议非常吃力,这里将它们独立出来实现,以便更好的理解协议。本文主要介绍RTSP,RTCP,RTP加载AAC音频流。说明:(1)大华IPC摄像头作为服......
  • QUIC协议 对比 TCP/UDP 协议
    QUIC协议是HTTP3引入的,所以需要了解HTTP的版本迭代。HTTP1.x队头阻塞:下个请求必须在前一个请求返回后才能发出,导致带宽无法被充分利用,后续请求被阻塞(HTTP1.1尝试使用流水线(Pipelining)技术,但先天FIFO(先进先出)机制导致当前请求的执行依赖于上一个请求执行的完成,容易引起队头阻......
  • HTTP和TCP协议的队头阻塞
    队头阻塞(Head-of-lineblocking)其实有两种,一种是 TCP队头阻塞,另一种是 HTTP队头阻塞,而这两者之前其实还存在一定的联系,毕竟 HTTP1/2是建立在TCP协议之上的应用层协议,另外还有HTTP3对队头阻塞的解决。 1、HTTP/1.x的队头阻塞HTTP/1.x有个问题叫队头阻塞,即一个连接同......
  • C++ - TCP通信
    前言socket编程分为TCP和UDP两个模块,其中TCP是可靠的、安全的,常用于发送文件等,而UDP是不可靠的、不安全的,常用作视频通话等。如下图:头文件与库:#include<WinSock2.h>​#pragmacomment(lib,"ws2_32.lib")准备工作:创建工程后,首先右键工程,选择属性然后选择C/C++-预......
  • TCP长连接的连接池、容量控制与心跳保活
    一、长连接与短连接TCP本身并没有长短连接的区别,长短与否,完全取决于我们怎么用它。短连接:每次通信时,创建Socket;一次通信结束,调用socket.close()。这就是一般意义上的短连接,短连接的好处是管理起来比较简单,存在的连接都是可用的连接,不需要额外的控制手段。长连接:每次通信完毕......
  • linux Irq domain
    文章引用:https://blog.csdn.net/longwang155069/article/details/105812097为什么会引入IRQ_domain?早期中断数量较少,所以可以分布在一个interrupt_controler,中断映射也很简单,每个中断号对应一个interrupt_controler。 而当一个系统中有多个interrupt-controller的时候,而且中......
  • tcpreplay常用命令
    tcpdump-ianyport1280-s0-wtest.pcap//抓包知道端口的流量--将包分离成客户端服务器回放的命令tcpprep-aclient-itest.pcap-otest.cache//对报文的通信流量的两方(服务器/客户端)进行分离tcprewrite-e192.168.2.100:192.168.3.101--enet_dmac=28:51:32:0b:......
  • 基于TCP协议的Socket通信
    上节中我们给大家接触了Socket的一些基本概念以及使用方法,相信大家对Socket已经有了初步的掌握。本节我们学习使用Socket来实现大文件的断点续传!在这里我们以他人的案例进行讲解,这是别人写好的一个Socket上传大文件的例子,不要求我们自己可以写出来,需要的时候会用就好!1.运行效果图1.......
  • tcp报文
    TCP报文详解_测试开发-海励的博客-CSDN博客_tcp报文1、介绍tcp,transmissioncontrolprotocol传输控制协议是一种传输层协议,向上为http/https等服务,向下由ip协议支持。2、数据格式(1)源端口,2字节(2)目的端口,2字节(3)序列号,4字节,表示本报文数据部分第一个字节的编号。在tcp连接中,......