由于Unix Domain传输速率大消耗资源少,但它只适用于本地之间传输,AF_INET Domain 消耗资源多,传输相对速率较低。为了高效实现跨设备多进程间通讯,可以同时使用Unix Domain 和 AF_INET Domain进程系统进程间通信。本地进程使用Unix Domain传输,远程设备使用AF_INET传输,为保证命令消息的可靠发送和接收,可以选择TCP传输。
要现实某个进程往其他任意一个进程间发送数据,中间必须建立一个服务端,服务端用来做数据路由,用来减少网络端口的连接同时简化各进程间数据的接收和发送。另外还可以通过路由实现广播的功能。
在实际应用中,多进程间的通信一般是用来实现各进程间的命令消息交互,为了保证命令消息的正确和完整,一般会对命令消息进行封装,这样也方便接收端在接收到数据的时候进行命令的解析。该命令消息的格式可以定义如下:
(资料图)
基本的通信网络模型如下:
对于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_NO1#define TCP_CLIENT_DEVICE2_NO2/**网络设备各客户端TCP端口**/#define TCP_DEVICE_CLIENT0_POART9000#define TCP_DEVICE_CLIENT1_POART9001#define TCP_DEVICE_CLIENT2_POART9002#define TCP_DEVICE_CLIENT3_POART9003#define TCP_DEVICE_CLIENT4_POART9004#define TCP_DEVICE_CLIENT5_POART9005#define TCP_DEVICE_CLIENT6_POART9006#define TCP_DEVICE_CLIENT7_POART9007#define TCP_DEVICE_CLIENT8_POART9008#define TCP_DEVICE_CLIENT9_POART9009#define TCP_DEVICE_CLIENT_MAX_POART 9009/**本地Unix Domain Socket进程间通信**/#define MAX_UDS_CLIENT_NUM 20 /**最大socket域客户端数,对应最大本地通讯进程数**/#defineSERVER_PATH"../tmp/server_socket" /**socket域服务端文件**/#define CLIENT_PACHT "../tmp/client_socket" /**socket域客户端文件前缀**//**本地客户端模块序号定义**/#define CLIENT_MIN_ADDR1#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 39extern const char * gc_au8DeviceNoMap[MAX_EXTARN_DEV_NUM]; extern const unsigned int gc_as32DeviceModuleMap[TCP_SERVER_LISTEN_MAX_NUM][3];
服务端server.c实现代码比较长,这里就不贴出来了,有兴趣的可以到我GitHub上查看:https://github.com/licaibiao/IPC_Socket
为方便客户端对消息命令的处理,客户端设计成每次接收到的数据是一个完整的命令消息,并且只有一条命令消息。为方便客户端的使用,将客户端的网络连接和网络状态检测同时封装到客户端数据接收和数据发送两个接口里面。比如在数据发送的时候,会去判断连接是否建立获取连接已经断开,如果网络异常则重新建立连接。
客户端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
为了更好地发送和接收命令消息,应该将命令消息基于网络传输协议之上再进行一层消息的封装,添加消息头标签,源地址目的地址和校验等信息。为了各进程间消息的更好识别和传输,应该对每条命令的数据结构进行定义,这样在解析的时候才不会出现参数对应不上的问题。我这里预定义了几个命令消息,用来测试该方法的稳定性。
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_FALSE1#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 0x5a5atypedef 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; /* 经度,百万分之一度 */ WORDu16Altitude; /* 高程,米 */WORDu16SpeedX10; /* 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{BYTEu8ParaNum; /* 参数总数 */}__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 #include #include #include #include #include #include #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: noneReturn: 0 成功;非0 异常Others:Author: Caibiao LeeDate:2019-08-03*********************************************************/int ClientSendMsg(int s32TargModuleAddr){int l_s32Res = 0;MSG_PACK_S l_stMsg = {0};MSG_0X0001_S l_stMsgSend = {0};if((s32TargModuleAddrLOCAL_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: noneReturn: 0 成功;非0 异常Others:Author: Caibiao LeeDate: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小时后未检测到异常。
实际客户端在接收和发送,应该是需要非阻塞的模式,正常的数据接收和发送不能影响客户端原本的业务逻辑处理。在非阻塞状态下数据的接收和发送都需要特别的注意。
在发送的时候,因为是非阻塞的,当对方接收缓存满了的时候,发送端会收到EAGAIN的错误码,该错误码是让你进行数据的重新发送。在我这里我是设计发送失败会重新发送3遍,然后3次发送后还是失败,那么这包数据就会被丢弃。
在接收的时候,我们需要去判断对方网络是否还处于连接的状态,可以使用select实现,也可以通过接收PIPE信号来判断,为了更好的数据处理,在我这里是使用select来判断对方网络是否已经断开连接。
每建立一个socket,在连接或是绑定之前,我们都可以设置该socket连接的收发缓存大小,可以根据实际应用的发送峰值来判断应该设置多大的缓存。在我Ubuntu16.04系统下,默认的TCP和UnixDomain收发缓存大小如下:
biao@ubuntu:~/test/ipcp_socket/server$ ./servercreate unix domin socket lpthread successcreate TCP socket lpthread successcreate read write lpthread seccessTCP SO_RCVBUF = 87380TCP SO_SNDBUF = 16384server waiting for tcp client connectUnix Domain SO_RCVBUF = 212992Unix Domain SO_SNDBUF = 212992server 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_socket5 directories, 17 filesbiao@ubuntu:~/test/ipcp_socket$
在 liwen01
公众号中回复 网络编程
获取工程代码,本章代码工程名为:ipcp_socket_20190815_V2.tar.gz
本文来源网络,免费传达知识,版权归原作者所有。如涉及作品版权问题,请联系我进行删除。
猜你喜欢:
谈谈嵌入式软件的兼容性!
我是韦东山老师的忠实粉丝!
标签:
Copyright @ 2015-2018 华东it网版权所有 备案号:京ICP备2022016840号-41 联系邮箱:2 913 236 @qq.com