最近的项目需要和外面运动控制卡的公司一起完成,出于方便考虑使用进程通讯让软件协同工作,在windows平台。
进程间通信(IPC,Inter-Process Communication),指至少两个进程或线程间传送数据或信号的一些技术或方法。使用进程间通信的两个应用可以被分为客户端和服务器(见主从式架构),客户端进程请求数据,服务端响应客户端的数据请求。
管道
管道均是只能单向传输,分为匿名管道和命名管道,匿名管道需要更少的开销,但提供更少的服务。匿名管道通常用在父进程与子进程之间的传输,只能在本地传输。命名管道允许无亲缘关系的进程间的通信,以及在网络通讯。
信号(英语:Signals)
信号是一种异步的通知机制,用来提醒进程一个事件已经发生。当一个信号发送给一个进程,操作系统中断了进程正常的控制流程,此时,任何非原子操作都将被中断。如果进程定义了信号的处理函数,那么它将被执行,否则就执行默认的处理函数。
消息队列(英语:Message queue)
是一种进程间通信或同一进程的不同线程间的通信方式,软件的贮列用来处理一系列的输入,通常是来自用户。消息队列提供了异步的通信协议,每一个贮列中的纪录包含详细说明的数据,包含发生的时间,输入设备的种类,以及特定的输入参数,也就是说:消息的发送者和接收者不需要同时与消息队列交互。消息会保存在队列中,直到接收者取回它。消息队列常常保存在链表结构中。拥有权限的进程可以向消息队列中写入或读取消息。
和信号相比,消息队列能够传递更多的信息。与管道相比,消息队列提供了有格式的数据,这可以减少开发人员的工作量。但消息队列仍然有大小限制。 消息队列除了可以当不同线程或进程间的缓冲外,更可以透过消息队列当前消息数量来侦测接收线程或进程性能是否有问题。
内存映射文件(Memory-mapped file)
或称“文件映射”、“映射文件”,是一段虚内存逐字节对应于一个文件或类文件的资源,使得应用程序处理映射部分如同访问主内存。最快的IPC方式,它是针对其他进程间通信方式运行效率低而专门设计的。本项目本来打算用此方式,但考虑到双方需要讨论上锁,协议等繁琐的操作,出于开发效率的考虑暂时不进行这方案。
BSD 套接字(BSD sockets)
是一种应用程序接口(API),用于网际套接字( socket)与Unix域套接字,包括了一个用C语言写成的应用程序开发库,主要用于实现进程间通讯,在计算机网络通讯方面被广泛使用。
经过考虑决定使用了socket实现IPC。下面把需要的客户端和服务器端都封装到一类。
/**
*@projectName
*@documentname socketipc.h
*@author zzJun
*@date 20180806
*@brief use socket to communicate
**/
#ifndef SOCKETIPC_H
#define SOCKETIPC_H
#include <string>
#include <iostream>
#include <winsock2.h>
#include <process.h>
#pragma comment(lib,"ws2_32.lib")
#include <windows.h>
using namespace std;
class socketipc
{
public:
/**
* @brief socketipc
* @param hton
* @param model
* @param ip
*/
socketipc(int hton,bool model=false,string ip="127.0.0.1");
/**
* @brief initservesock
* @param hton
* @param ip
* @return 服务器初始化
*/
int initservesock(int hton,string ip);
/**
* @brief initclientsock
* @param hton
* @param ip
* @return 客户端初始化
*/
int initclientsock(int hton,string ip);
/**
* @brief write
* @param str
* @return 输出字符串
*/
int write(string str);
/**
* @brief clientconnect
* @param hton
* @return 让客户端连接
*/
int clientconnect(int hton);
//打印错误
void ShowError(string error);
//扫描输入线程 以及 扫描请求线程
static DWORD WINAPI readthread(LPVOID lpParameter);
static DWORD WINAPI connectthread(LPVOID lpParameter);
string recstr() const;
private:
//socket模式 true:server flase:client
bool m_model;
//client
bool m_bconnect=false;
//server
bool m_bconnected=false;
//地址
sockaddr_in addr;
//client只需要用这个 server两者均用
SOCKET sock;
SOCKET sockconnect;
//输入输出的字符串
string m_recstr;
string m_sendstr;
//端口
int port;
};
#endif // SOCKETIPC_H
#include "socketipc.h"
void socketipc::ShowError(string error)
{
cout<<error<<endl;
}
int socketipc::clientconnect(int hton)
{
addr.sin_port=htons(hton);
if(connect(sock,(sockaddr*)&addr,sizeof(sockaddr))!= SOCKET_ERROR)
{
m_bconnect=true;
_beginthreadex(NULL, 0,(unsigned int (__stdcall *)(void *)) readthread, this, 0, NULL);
return 1;
}
return 0;
}
socketipc::socketipc(int hton,bool model,string ip):m_model(model),port(hton)
{
//选择模式
if(model){
initservesock(hton,ip);
}else{
initclientsock(hton,ip);
}
}
int socketipc::initclientsock(int hton ,string ip){
WSADATA wsaData;
if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0)
{
cout<<"WSAStartup failed"<<endl;
return 0;
}
sock=socket(AF_INET,SOCK_STREAM,0);
addr.sin_addr.S_un.S_addr=inet_addr(ip.c_str());
addr.sin_family=AF_INET;
addr.sin_port=htons(hton);
return 1;
}
int socketipc::initservesock(int hton,string ip)
{
WSADATA wsaData;
if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0)
{
ShowError("WSAStartup failed");
}
sock=socket(AF_INET,SOCK_STREAM,0);
if(INVALID_SOCKET==sock)
{
return 0;
}
char name[MAX_PATH]={0};
//获取主机名字
if(SOCKET_ERROR==gethostname(name,MAX_PATH))
{
ShowError("gethostname failed");
return 0;
}
//根据名字获取ip
hostent *ht=gethostbyname(name);
if(ht==NULL)
{
ShowError("gethostbyname failed");
return 0;
}
addr.sin_addr.S_un.S_addr=inet_addr(ip.c_str());
addr.sin_family=AF_INET;
addr.sin_port=htons(hton);
if(SOCKET_ERROR==bind(sock,(sockaddr*)&addr,sizeof(sockaddr)))
{
ShowError("bind failed");
return 0;
}
listen(sock,1);
_beginthreadex(NULL, 0,(unsigned int (__stdcall *)(void *)) connectthread, this, 0, NULL);
}
DWORD WINAPI socketipc::readthread(LPVOID lpParameter)
{
socketipc* pthis=(socketipc*)lpParameter;
char buf[100]={0};
for(;;)
{
memset(buf,0,100);
int ret;
if(pthis->m_model)
{
ret=recv(pthis->sockconnect,buf,100,0);
}else{
ret=recv(pthis->sock,buf,100,0);
}
if(ret>0)
{
pthis->m_recstr=buf;
cout<<pthis->port<<" :"<<buf<<endl;
}
if(ret<=0&&errno != EAGAIN){
cout<<"error"<<endl;
break;
}
}
//退出循环,断开连接
pthis->m_bconnect=false;
pthis->m_bconnected=false;
return 1;
}
DWORD WINAPI socketipc::connectthread(LPVOID lpParameter)
{
//SOCKET soc;
int len=sizeof(sockaddr);
sockaddr_in sa;
socketipc* psocket=(socketipc*)lpParameter;
while(1)
{
//已连接不需要等待
if(psocket->m_bconnected){
Sleep(0);
continue;
}
cout<<"wait connect"<<endl;
//此处阻塞线程,等待连接
psocket->sockconnect=accept(psocket->sock,(sockaddr*)&sa,&len);
if(INVALID_SOCKET!=psocket->sockconnect)
{
psocket->m_bconnected=true;
cout<<"new connect"<<endl;
_beginthreadex(NULL, 0,(unsigned int (__stdcall *)(void *)) readthread, psocket, 0, NULL);
}
}
}
string socketipc::recstr() const
{
return m_recstr;
}
int socketipc::write(string str)
{
m_sendstr=str;
if(m_bconnected){
send(sockconnect,str.c_str(),strlen(str.c_str())+1,0);
return 1;
}
if(m_bconnect){
send(sock,str.c_str(),strlen(str.c_str())+1,0);
return 1;
}
return 0;
}
https://download.csdn.net/download/aterrior/10586528 demo的下载地址