孙鑫 第十七课进程间通信之三 命名管道
说明
命名管道可以用在本地的进程之间,也可以用在跨网络的主机进程之间,孙鑫老师在视频里讲的如果跨网络的话管道名字中要有对方主机名,那么到底是局域网内还是可用于广域网呢? 暂时不知道
我在练习命名管道时容易犯的错误
服务器进程创建命名管道并等待客户进程连接,在服务器进程中,读写管道时的文件句柄为管道句柄,而不是创建文件,即不能调用 CreateFile。相反在客户进程中,客户进程连接上服务进程创建的管道后需要创建以管道名为文件名的文件,用返回的文件句柄进行 读写操作。
服务器进程步骤
①创建命名管道(CreateNamePipe)
②等待客户进程连接管道(ConnectNamedPipe)
③客户进程连接到命名管道
④开始读 / 写操作 (ReadFile / WriteFile)
⑤关闭管道(CloseHandle)
说明:
第②步中等待客户进程连接是一个费时(time-consumming)操作,即当没有客户进程来连接的时候会一直停在此处(同步操作),因为可以使用重叠操作,即重叠操作时:函数会立即返回而不管是不是执行完毕。
在命名管道编程中(服务器端),实现重叠操作可以有两种方式,都是在CreateNamePipe函数中指定相应标志位,一是在第二个参数中指定FILE_FLAG_OVERLAPPED标识, 这样遇到函数ConnectNamePipe函数就会立即返回,可以用WaitForSingleObject函数等待OVERLAPPED结构体中的hEvent成员有无信号判断是不是有客户进程来连接,有信号则说明有客户进程连接,否则没有。
第二种实现重叠操作的标识是在CreateNamePipe函数的第三个参数中指定PIPE_NOWAIT标识,一旦指定了该标识 ,ConnectNamedPipe, ReadFile, WriteFile函数都编程重叠操作,都可以立即返回。 而用第一种标识只能使ConnectNamedPipe 函数为重叠操作。
同时要注意:通过实验发现,如果同时指定上述两种标识,那么可能会有未知的情况发生,因此推荐第二种。
读写句柄用的是管道句柄, 而非新创建文件句柄。
在客户进程编程中,连接服务进程的函数WaitNamedPipe在MSDN中没有看到如何实现重叠操作,因此没有实验,如果超时值为0,在没有可用连接的管道的情况下也可以立即返回。
而在客户进程中需要调用CreateFile函数创建文件,可以在此函数中指定重叠操作标识FILE_FLAG_OVERLAPPED使ReadFile / WriteFile
函数实现重叠操作, 仅仅是可以,至于是不是只要,或者是否开新的线程来读写从而避免阻塞主线程就要按照实际情况来。
客户进程步骤
①等待连接服务进程(WaitNamedPipe)
②连接好后,创建文件进行读写(CreateFile)
③开始读 / 写操作(ReadFile / WriteFile)
说明:
WaitNamedPipe函数是可指定一个超时值。
函数和数据结构
①创建命名管道
HANDLE CreateNamePipe(
LPCTSTR lpName, //管道名,格式:\\.\pipe\pipename,在引号中反斜杠条数加倍,其中pipename可变,其他不变。<256字符
DWORD dwOpenMode, //管道打开模式,可指定单向读/写,双向,重叠操作等
DWORD dwPipeMode, //管道标识模式,读 / 写 的 字节 / 消息模式,重叠标识
DWORD nMaxInstance, //该名字管道实例的最大个数,从1到PIPE_UNLIMITED_INSTANCES。
DWORD nOutBufferSize, //输出缓冲区大小
DWORD nInBufferSize, //输入缓冲区大小
DWORD nDefaultTimeout, //超时值,客户进程中函数WaitNamedPipe如果指定了 //NMPWAIT_USE_DEFAULT_WAIT标识,那么则使用此超时值
LPSECURITY_ATTRIBUTES lpSecurityAttributes //安全属性,可为NULL,默认属性
);
eg.
HANDLE hPipe = INVALID_HANDLE_VALUE;
const char* pipeName = "\\\\.\\pipe\\MyPipe"; //其中"."标识本地主机
hPipe = CreateNamePipe(
pipeName,
PIPE_ACCESS_DUPLEX, //双向, 读 / 写
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_NOWAIT, //消息模式,ConnectNamedPipe/ReadFile/WriteFile
//重叠操作
PIPE_UNLIMITED_INSTANCES, //最大个数
1024, //输出缓冲区大小
1024, //输入缓冲区大小
0, //超时值
NULL //默认安全属性
);
if(INVALID_HANDLE_VALUE == hPipe)
{
MessageBox("创建命名管道错误!");
return;
}
②等待客户进程连接
BOOL ConnectNamedPipe(
HANDLE hNamedPipe, //命名管道句柄
LPOVERLAPPED lpOverlapped //OVERLAPPED结构体指针
);
说明:由于在CreateNamePipe函数中使用了PIPE_NOWAIT标识,那么这不再使用重叠结构体,下面还是对该结构体的一点使用
简单介绍。
在使用时第二个参数为NULL即可。
typedef struct _OVERLAPPED{
ULONG_PTR Internal;
ULONG_PTR InternalHigh;
DWORD offset;
DWORD offsetHigh;
HANDLE hEvent; //人工重置事件,初始无信号。当重叠操作完成时该事件被置为有信号。
}OVERLAPPED;
如果ConnectNamedPipe函数使用该结构体来实现重叠操作,那么方法如下:
OVERLAPPED ol;
ZeroMemory(&ol, sizeof(OVERLAPPED)); //结构体成员全清零
HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); //创建一个默认安全属性,无名,人工重置,初始无信号的事件
BOOL ret = ConnectNamedPipe(
hPipe,
&ol
);
//如果该函数返回FALSE
if(!ret)
{
if(ERROR_IO_PENDING == GetLastError())
{
//如果该函数返回FALSE,而且返回这个错误,表示重叠操作未完成,属于正常,并不是真正的错误,不影响程序执行,可以按
//没有错误对待。
}
else if(ERROR_PIPE_CONNECTED == GetLastError())
{
//如果客户进程连接服务进程管道是在这样一个时机:CreateNamePipe和ConnectNamedPipe之间,就会返回这样的错误,
//但实际情况是已经连上了管道,也不影响程序执行,可以按照没有错误对待。
//由于本人知识所限,暂时认为只有这样一种情况才会发生这个错误。
}
else
{
//影响程序执行的错误发生!
CloseHandle(hPipe);
hPipe = INVALID_HANDLE_VALUE;
return;
}
}
③连接服务进程管道
BOOL WaitNamedPipe(
LPCTSTR lpNamePipeName, //管道名
DWORD nTimeOut //超时值,如果为NMPWAIT_USE_DEFAULT_WAIT则使用的超时值为CreateNamePipe函数中
//最后一个参数的值,如果为NMPWAIT_WAIT_FOREVER则是永远等待,直到可以连接。 也可以指定一个值。
);
eg.
BOOT ret = WaitNamedPipe(
"\\\\.\\pipe\\MyPipe", //管道名,必须同服务进程管道名
NMPWAIT_USE_DEFAULT_WAIT //默认超时值,在此同前为0
);
实例
//服务进程
//创建命名管道
HANDLE m_hPipe = INVALID_HANDLE_VALUE;
void CMy80NamedPipeFirstDlg::OnBtnCreatepipe()
{
char* pipeName = _T("\\\\.\\pipe\\MyPipe");
m_hPipe = CreateNamedPipe(
pipeName, PIPE_ACCESS_DUPLEX /* | FILE_FLAG_OVERLAPPED*/, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_NOWAIT, PIPE_UNLIMITED_INSTANCES, 1024, 1024, 0, NULL);
if (INVALID_HANDLE_VALUE == m_hPipe)
{
MessageBox(_T("创建命名管道失败!"));
return;
}
HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
ol.hEvent = hEvent;
BOOL ret = ConnectNamedPipe(m_hPipe, NULL);
if (!ret)
{
if (ERROR_PIPE_CONNECTED == GetLastError())
{
MessageBox(_T("返回错误,但客户进程已经连接!"));
GetDlgItem(IDC_BTN_READ)->EnableWindow(TRUE);
GetDlgItem(IDC_BTN_WRITE)->EnableWindow(TRUE);
}
else if (ERROR_NO_DATA == GetLastError())
{
MessageBox(_T("ERROR_NO_DATA错误!"));
GetDlgItem(IDC_BTN_READ)->EnableWindow(FALSE);
GetDlgItem(IDC_BTN_WRITE)->EnableWindow(FALSE);
CloseHandle(hEvent);
CloseHandle(m_hPipe);
m_hPipe = INVALID_HANDLE_VALUE;
return;
}
else if (ERROR_IO_PENDING)
{
MessageBox(_T("ERROR_IO_PENDING错误!"));
GetDlgItem(IDC_BTN_READ)->EnableWindow(TRUE);
GetDlgItem(IDC_BTN_WRITE)->EnableWindow(TRUE);
}
else
{
MessageBox(_T("未知错误!"));
GetDlgItem(IDC_BTN_READ)->EnableWindow(FALSE);
GetDlgItem(IDC_BTN_WRITE)->EnableWindow(FALSE);
CloseHandle(hEvent);
CloseHandle(m_hPipe);
m_hPipe = INVALID_HANDLE_VALUE;
return;
}
}
else
{
GetDlgItem(IDC_BTN_READ)->EnableWindow(TRUE);
GetDlgItem(IDC_BTN_WRITE)->EnableWindow(TRUE);
}
}
//写管道
void CMy80NamedPipeFirstDlg::OnBtnWrite()
{
char writeBuf[] = "hello client!";
DWORD length = 0;
WriteFile(m_hPipe, writeBuf, sizeof(writeBuf), &length, NULL);
}
//读管道
void CMy80NamedPipeFirstDlg::OnBtnRead()
{
char readBuf[200];
memset(readBuf, '\0', sizeof(readBuf));
DWORD length = 0;
ReadFile(m_hPipe, readBuf, sizeof(readBuf), &length, NULL);
MessageBox(readBuf);
}
//客户进程
//连接管道并创建文件
HANDLE hFile = INVALID_HANDLE_VALUE;
void CMy80NamedPipeSecondDlg::OnBtnConnect()
{
if(WaitNamedPipe(_T("\\\\.\\pipe\\MyPipe"), NMPWAIT_WAIT_FOREVER))
{
m_hFile = CreateFile(
_T("\\\\.\\pipe\\MyPipe"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL
);
if (INVALID_HANDLE_VALUE != m_hFile)
{
GetDlgItem(IDC_BTN_WRITE)->EnableWindow(TRUE);
GetDlgItem(IDC_BTN_READ)->EnableWindow(TRUE);
}
}
else
{
GetDlgItem(IDC_BTN_WRITE)->EnableWindow(FALSE);
GetDlgItem(IDC_BTN_READ)->EnableWindow(FALSE);
}
}
//读管道
void CMy80NamedPipeSecondDlg::OnBtnRead()
{
char readBuf[200];
memset(readBuf, '\0', sizeof(readBuf));
DWORD length = 0;
ReadFile(m_hFile, readBuf, sizeof(readBuf), &length, NULL);
MessageBox(readBuf);
}
//写管道
void CMy80NamedPipeSecondDlg::OnBtnWrite()
{
char writeBuf[] = "hello server!";
DWORD length = 0;
WriteFile(m_hFile, writeBuf, sizeof(writeBuf), &length, NULL);
}
孙鑫 第十七课进程间通信之三 命名管道相关推荐
- python命名管道通讯_Python进程间通信的命名管道详解(Windows)
本文和大家分享的主要是windows系统下,python进程间通信的命名管道相关内容 ,一起来看看吧,希望对大家学习python有所帮助. 在 Windows 上的命名管道主要是通过调用win32 a ...
- 进程间通信(5) 命名管道
可以在本机上实现两个进程间的通信 支持跨网络进程间的通信. 在创建管道时,可以指定具有访问权限的用户,而其他用户则不能访问这个管道. 将命名管道作为一种网络编程方案时,它实际上建立了一个客户机/服务器 ...
- 进程间通信之命名管道
命名管道(FIFO)是进程间通信的一种方式. API: int mkfifo(const char *pathname, mode_t mode); DEMO: // 写进程 int main(int ...
- 【Linux系统编程】进程间通信之命名管道
00. 目录 文章目录 00. 目录 01. 命名管道概述 02. 命名管道创建 03. 命名管道特性 04. 命名管道非阻塞 05. 附录 01. 命名管道概述 无名管道,由于没有名字,只能用于亲缘 ...
- Linux进程间通信——使用命名管道
在前一篇文章-- Linux进程间通信--使用匿名管道中,我们看到了如何使用匿名管道来在进程之间传递数据,同时也看到了这个方式的一个缺陷,就是这些进程都由一个共同的祖先进程启动,这给我们在不相关的的进 ...
- 【Linux】进程间通信 - 匿名/命名管道与System V共享内存
目录 前言 一.管道 0.什么是管道 1).管道的概念 2).管道的本质 3).管道指令: "|" 1.匿名管道 1).如何创建匿名管道 2).如何使用匿名管道进行通信 3).匿名 ...
- Linux系统编程——进程间通信:命名管道(FIFO)
命名管道的概述 无名管道,由于没有名字,只能用于亲缘关系的进程间通信(更多详情,请看<无名管道>).为了克服这个缺点,提出了命名管道(FIFO),也叫有名管道.FIFO 文件. 命名管道( ...
- python 命名管道_详解Python进程间通信之命名管道
管道是一种简单的FIFO通信信道,它是单向通信的. 通常启动进程创建一个管道,然后这个进程创建一个或者多个进程子进程接受管道信息,由于管道是单向通信,所以经常需要创建两个管道来实现双向通信. 命名管道 ...
- 管道实现进程间通信之命名管道
进程间通信管道篇之命名管道 文章目录 1.1命名管道 1.2命名管道的创建 1.3命名管道和匿名管道的区别 代码演示(用命名管道实现进程间通信) 2.1 通过进程间通信将客户端发送的字符串转化为命令让 ...
最新文章
- Python基础之逻辑运算符
- Java中AudioFileStream_AudioFileStream学习
- 使用Poi读取xlsx类型的Excel
- python使用函数的优点-Python函数的特点
- xargs的简单解析
- java poi excel无法添加水印替代方法
- 想跑次高频策略?快来看看Numpy处理真格量化tick数据的技巧
- java接口回调学习
- linux snmp 限制ip_Windows/Linux服务器监控软件推荐
- 年轻设计师之于《未来产品的设计》
- DVM 和 JVM 的区别?
- springcahce集成redis 设置过期时间
- Spoon工具的使用
- C#窗体标准计算器(上) 初级新手请多担待。
- 用Raspberry Pi作AirPlay服务端
- 服务器:RAID、AHCI、IDE
- CAE软件技术现状调研
- 代码比较工具DiffMerge的下载和使用(转)
- 如何描述缺陷(Defect)?
- 常用字写法(粤语)--(1)