两个程序之间的通行(邮槽)

Posted 芬乐

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了两个程序之间的通行(邮槽)相关的知识,希望对你有一定的参考价值。

*Windows环境下,实现进程间的通信方式消息、命名管道(Name Pipes)、剪贴板(ClipBoard)等,邮槽(MailSlot)也是其中一种。

1.邮槽

  • 邮槽(Mailslot)也称为邮件槽,它是 Windows 提供的一种用来实现进程间通信的手段,是单向数据传输的服务。
  • 邮槽的弊端:邮槽是单向通信。服务器端只能读取,客户端只能写入。如果要双向通信就需要服务端写一个邮槽,客户端在写一个邮槽。创建两个邮槽。
  • 服务端和客户端的区分:创建邮槽的为服务端,已存在的邮槽的哪一端。
  • 客户端在使用邮槽发送数据的时候只有当数据的长度<425字节的时候,才能被广播给多个服务器,如果消息长度>425字节,邮槽不支持广播通信。
  • 邮槽最大的缺点是只允许从客户机到服务器,建立一种不可靠的单向数据通信。而邮槽最大的一个优点在于,它们使客户机应用能够非常容易地将广播消息发送给一个或多个服务器应用。

  • 将邮槽理解为文件系统,那么其实就是创建文件、写入文件和读取文件的关系。两台计算机使用邮槽通信,其实就是在服务端上创建一个文件供写入,当其他计算机(或进程)需要给它发送消息时,就是打开这个文件,将内容写入。当目的计算机进程发现文件内容非空,就读取文件进行分析显示。

2.实现流程

  • 调用CreateMailslot函数,将创建邮槽的请求传递给内核的系统服务。
  • 请求传递给NtcreatMailslot函数,这个函数会到达底层的邮槽驱动程序。
  • 底层驱动程序msfs.sys,然后一些创建邮槽的工作就交给邮槽驱动程序来完成了.

3.油槽的文件名

  • 邮槽的文件名。对于邮槽文件,格式为\\ComputerName\mailslot\[path]name
  • 对于本地邮槽文件,ComputerName使用“.”来代替,就是“\\.\MailSlot\路径\文件名”
  • 对于写入的目标计算机的邮槽文件,就是\\目标计算机\MailSlot\路径\文件名。
  • 邮槽支持群发,如果向默认工作组/域发送(就是当前发送计算机所在工作组/域),可以使用\\*\MailSlot\路径\文件名。如果向指
  • 定工作组/域发送,可以使用\\工作组/域名\MailSlot\路径\文件名

4.用到的函数

Windows API函数 Visual Basic 语法格式声明

  • CreateMailslot()
 
Declare Function CreateMailslot Lib "kernel32" Alias "CreateMailslotA" (ByVal lpName As String, ByVal nMaxMessageSize As Long, ByVal lReadTimeout As Long, lpSecurityAttributes As SECURITY_ATTRIBUTES) As Long
Windows API函数功能说明
创建一个邮路。返回的句柄由邮路服务器使用(收件人)
Windows API函数出口返回值
Long,如执行成功,返回邮路的句柄;INVALID_HANDLE_VALUE表示失败。会设置GetLastError
Windows API函数入口参数表
Windows API函数入口参数 类型及说明
lpName String,指定邮路的名字,采用的形式如下:\\.\邮路\[路径\]邮路名
nMaxMessageSize Long,指定一个邮路消息的最大长度。零表示无限长。请注意,对于穿越一个网络域到多个邮路的广播消息,最大长度是400
lReadTimeout Long,等待指定的数据时,用这个参数指定邮路使用的默认超时设置,以毫秒为单位。零表示不等待。常数MAILSLOT_WAIT_FOREVER表示一直等到数据到达
lpSecurityAttributes SECURITY_ATTRIBUTES,指定一个结构,或传递零值(将参数声明为ByVal As Long,并传递零值),表示使用不允许继承的默认描述符

 

  • GetMailslotInfo()
 
Declare Function GetMailslotInfo Lib "kernel32" Alias "GetMailslotInfo" (ByVal hMailslot As Long, lpMaxMessageSize As Long, lpNextSize As Long, lpMessageCount As Long, lpReadTimeout As Long) As Long
Windows API函数功能说明
获取与一个邮路有关的信息
Windows API函数出口返回值
Long,非零表示成功,零表示失败。会设置GetLastError
Windows API函数入口参数表
Windows API函数入口参数 类型及说明
hMailslot Long,指定一个邮路的句柄
lpMaxMessageSize Long,指定一个长整数变量,用于装载这个邮路的最大消息长度
lpNextSize Long,指定一个长整数变量,用于装载下一条消息的长度。如没有消息准备好,则可设为常数MAILSLOT_NO_MESSAGE
lpMessageCount Long,指定一个长整数变量,用于装载邮路中准备好的消息数量
lpReadTimeout Long,指定一个长整数变量,用于装载邮路的默认阅读超时

 

  • readfile()
 
Declare Function ReadFile Lib "kernel32" Alias "ReadFile" (ByVal hFile As Long, lpBuffer As Any, ByVal nNumberOfBytesToRead As Long, lpNumberOfBytesRead As Long, lpOverlapped As OVERLAPPED) As Long
Windows API函数功能说明
从文件中读出数据。与lread函数相比,这个函数要明显灵活的多。该函数能够操作通信设备、管道、套接字以及邮槽
Windows API函数出口返回值
Long,非零表示成功,零表示失败。会设置GetLastError。如启动的是一次异步读操作,则函数会返回零值,并将ERROR_IO_PENDING设置成GetLastError的结果。如结果不是零值,但读入的字节数小于nNumberOfBytesToRead参数指定的值,表明早已抵达了文件的结尾
Windows API函数入口参数表
Windows API函数入口参数 类型及说明
hFile Long,文件的句柄
lpBuffer Any,用于保存读入数据的一个缓冲区
nNumberOfBytesToRead Long,要读入的字符数
lpNumberOfBytesRead Long,从文件中实际读入的字符数
lpOverlapped OVERLAPPED,如文件打开时指定了FILE_FLAG_OVERLAPPED,那么必须用这个参数引用一个特殊的结构。那个结构定义了一次异步读取操作。否则,应将这个参数设为NULL(将函数声明成ByVal As Long,并传递零值)
注解
并非每种操作系统都支持对每种设备进行异步操作。Windows 95不支持对一个磁盘文件进行异步读操作(重复读)
  • WriteFile()
 
Declare Function WriteFile Lib "kernel32" Alias "WriteFile" (ByVal hFile As Long, lpBuffer As Any, ByVal nNumberOfBytesToWrite As Long, lpNumberOfBytesWritten As Long, lpOverlapped As OVERLAPPED) As Long
Windows API函数功能说明
将数据写入一个文件。该函数比lwrite函数要灵活的多。也可将这个函数应用于对通信设备、管道、套接字以及邮槽的处理
Windows API函数出口返回值
Long,TRUE(非零)表示成功,否则返回零。会设置GetLastError
Windows API函数入口参数表
Windows API函数入口参数 类型及说明
hFile Long,一个文件的句柄
lpBuffer Any,要写入的一个数据缓冲区
nNumberOfBytesToWrite Long,要写入数据的字节数量。如写入零字节,表示什么都不写入,但会更新文件的“上一次修改时间”。针对位于远程系统的命名管道,限制在65535个字节以内
lpNumberOfBytesWritten Long,实际写入文件的字节数量
lpOverlapped OVERLAPPED,倘若在指定FILE_FLAG_OVERLAPPED的前提下打开文件,这个参数就必须引用一个特殊的结构。那个结构定义了一次异步写操作。否则,该参数应置为空(将声明变为ByVal As Long,并传递零值)
注解
并不是每种操作系统都支持在任何类型的设备上进行异步操作。windows 95不支持对磁盘文件的重叠读取操作

 

例子:

(1)服务端程序

#include<stdio.h>
#include<stdlib.h>
#include<Windows.h>

#define MAILSLOT "\\\\.\\mailslot\\sjx"   //给邮槽命名为sjx

void main()
{
                                   //名称,数量    等待时间   安全属性
    HANDLE hmailslot = CreateMailslotA(MAILSLOT, 0, MAILSLOT_WAIT_FOREVER, NULL);
    if (hmailslot == NULL)
    {
        printf("创建失败");
    }
    else
    {

        while (1)
        {
            system("pause");
            DWORD dxNextsize = 0;//标识下一个的大小
            DWORD dxmsgcount = 0;//消息数量
            DWORD readcount = 0;//读取成功的数量


                               //文件名          标记下一个字符串长度多少,  多少条消息
            if (GetMailslotInfo(hmailslot, NULL, &dxNextsize,    &dxmsgcount, NULL))
            {

                for (int i = 0; i < dxmsgcount;i++)
                {
                    //    LPBYYE  unsigned char *
                    LPBYTE lpbuf = malloc(dxNextsize + 1);//分配内存

                    //读取             //首地址, 大小      //读的个数
                    ReadFile(hmailslot, lpbuf, dxNextsize, &readcount, NULL);
                    printf("\n%s", lpbuf);

                }

            }
            else
            {
                printf("error");
            }

        }

    }

    system("pause");
}

(2)客户端程序

#define  _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<Windows.h>

#define MAILSLOT "\\\\.\\mailslot\\sjx"

//#define GENERIC_READ                     (0x80000000L)
//#define GENERIC_WRITE                    (0x40000000L)
//#define GENERIC_EXECUTE                  (0x20000000L)
//#define GENERIC_ALL                      (0x10000000L)
void main()
{

    //第一个名称,第二个写入,第三个共享读,打开已经存在
    HANDLE hmailslot = CreateFileA(MAILSLOT,         GENERIC_WRITE,FILE_SHARE_READ,NULL,OPEN_EXISTING,0,NULL);
    if (hmailslot ==INVALID_HANDLE_VALUE)
    {
        printf("打开失败");
    }
    while (1)
    {
        //system("pause");
        char str[10] = "123456789";
        scanf("%s", str);
        LPBYTE lpmsg = (LPBYTE)str;//转化了指针类型
        int wok = 0;
        //写入信息,第一个句柄,第二个写入内存首地址,第三个长度,第四个写入成功保存wok
        WriteFile(hmailslot, lpmsg,10,&wok,NULL);
        
    }
    CloseHandle(hmailslot);//关闭

    system("pause");
}

 

以上是关于两个程序之间的通行(邮槽)的主要内容,如果未能解决你的问题,请参考以下文章

win32邮槽和进程的概念

这两个代码片段之间有区别吗?如果有,那又如何? [复制]

kotlin:如何在两个片段之间传递数据

如何在导航抽屉活动模板中的片段之间传递字符串变量

Reload Activity 以在 Fragment 之间重新切换

如何在嵌套片段内的两个子片段之间进行通信