作者: 徐原
能实现局域网上两台主机间文件拷贝的方法有很多种,这里介绍的“命名管道”(Named Pipe )是一种比较可靠的进程间通信机制,可用在同一台计算机不同进程间,也可用在不同计算机的不同进程间,可以是单向的,也可以是双向的,Windows NT、Windows 2000、Windows 95和Windows 98均提供了对它的支持,而且在Unix下也有类似的概念。它是在Microsoft LAN管理器和IBMLAN服务器网络操作系统上实现的。 命名管道使用了MSNP(微软网络提供者)重定向器,这样应用程序便可以不用了解网络协议的细节而利用该机制实现网络上的数据传输。它采用“命名管道文件系统”(Named Pipe File System)接口,其命名是采用UNC(通用命名规范)格式的: \\ServerName\Pipe\[pipename] \\ServerName指明命名管道是在那个服务器上创建的,ServerName既可以是一个实际的计算机名,也可以是小数点(“.”)以指明是在本机上创建;\Pipe是一个硬编码(Hardcode)不用区分大小写的字符串用以指明这是一个管道名,该文件名从属于NPFS;[pipename]是实际的自定义的管道名,该名称在前面指定的服务器上必须是唯一的,该名称可以包含多级目录,但目录名必须不是已经创建的管道名,例: \\.\Pipe\xyPipe’这是一个合法管道名 \\.\Pipe\xyPipe\Pipe’这不是一个合法的管道名,因为前面的目录\\.\Pipe\xyPipe是一个已经创建的管道名了。 \\.\Pipe\xxyPipe\Pipe’这也是一个合法的文件名 命名管道有两种基本通信模式:字节模式和消息模式。在字节模式中,数据是以字节流的形式在管道种传输,数据之间没有边界,在管道写入和读出操作中是以字节流即数据块为基本单位操作的,这适合传输大容量数据;在消息模式中,数据是以一条条不连续的消息为基本传输单元,消息和消息之间有边界,在管道写入和读出操作中也是以消息为单位进行操作的,这种方式适合传输量小的数据。因为现在的文件大小常常有几百K甚至更大,所以程序中选择使用字节模式。 下面详细介绍一下CreateNamedPipe()这个函数,该函数C原型如下: HANDLE CreateNamedPipe( LPCTSTR lpName, // pointer to pipe name DWORD dwOpenMode, // pipe open mode DWORD dwPipeMode, // pipe-specific modes DWORD nMaxInstances, // maximum number of instances DWORD nOutBufferSize, // output buffer size, in bytes DWORD nInBufferSize, // input buffer size, in bytes DWORD nDefaultTimeOut, // time-out time, in milliseconds LPSECURITY_ATTRIBUTES lpSecurityAttributes // pointer to security attributes ); lpName:为前面所述的命名管道名。 dwOpenMode:为命名管道打开的模式,有PIPE_ACCESS_DUMPLEX(双向)、PIPE_ACCESS_INBOUND(输入)、PIPE_ACCESS_OUTBOUND(输出)这三种,这些标志还可以和一些附加的I/O控制和安全模式的常数组合使用,详细可参考MSDN。 dwPipeMode:为管道传输模式,有前面所述的PIPE_TYPE_BYTE(字节模式)和PIPE_TYPE_MESSAGE(消息模式)两种,可以和PIPE_READMODE_BYTE和PIPE_READMODE_MESSAGE常数组合使用以限定客户端的读取模式。可以使用PIPE_TYPE_MESSAGE 和 PIPE_READMODE_BYTE组合来指定发送者以消息模式向管道发送数据,而接收者一次可以读取任意数量的字节。注意不可将PIPE_TYPE_BYTE和PIPE_READMODE_MESSAGE组合使用,这样会导致CreateNamedPipe()函数调用失败,因为字节模式没有边界,在接收端用消息模式读取的时候无法判断消息的边界。 nMaxInstances:管道最大的连接实例句柄,其范围在1到255之间。 nOutBufferSize和nInBufferSize分别指明管道输出和输入缓冲区的大小,如设为0则使用系统默认大小。 nDefaultTimeOut以毫秒为单位设定客户机等待同命名管道建立连接的最长时间。 LpSecurityAttruibutes为一个安全描述符,设为Null表示使用系统默认的描述符,同时句柄不可继承。 要注意的是在程序中命名管道的写操作中一次最大只能写64K字节的数据, 下面是服务器端程序: (模块中): Public Declare Function CreateNamedPipe Lib "kernel32" Alias "CreateNamedPipeA" (ByVal lpName As String, ByVal dwOpenMode As Long, ByVal dwPipeMode As Long, ByVal nMaxInstances As Long, ByVal nOutBufferSize As Long, ByVal nInBufferSize As Long, ByVal nDefaultTimeOut As Long, ByVal lpSecurityAttributes As Long) As Long Public Declare Function ConnectNamedPipe Lib "kernel32" (ByVal hNamedPipe As Long, ByVal lplong As Long) As Long Public Declare Function ReadFile Lib "kernel32" (ByVal hFile As Long, lpBuffer As Any, ByVal nNumberOfBytesToRead As Long, lpNumberOfBytesRead As Long, ByVal lplong As Long) As Long Public Declare Function WriteFile Lib "kernel32" (ByVal hFile As Long, lpBuffer As Any, ByVal nNumberOfBytesToWrite As Long, lpNumberOfBytesWritten As Long, ByVal lplong As Long) As Long Public Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long Public Declare Function WaitNamedPipe Lib "kernel32" Alias "WaitNamedPipeA" (ByVal lpNamedPipeName As String, ByVal nTimeOut As Long) As Long Public Declare Function CreateFile Lib "kernel32" Alias "CreateFileA" (ByVal lpFileName As String, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, ByVal lpSecurityAttributes As Long, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long Public Declare Function DisconnectNamedPipe Lib "kernel32" (ByVal hNamedPipe As Long) As Long Public Declare Function GetFileSize Lib "kernel32" (ByVal hFile As Long, lpFileSizeHigh As Long) As Long
Public Const PIPE_ACCESS_DUPLEX = &H3 Public Const PIPE_ACCESS_INBOUND = &H1 Public Const PIPE_ACCESS_OUTBOUND = &H2 Public Const PIPE_CLIENT_END = &H0 Public Const PIPE_NOWAIT = &H1 Public Const PIPE_READMODE_BYTE = &H0 Public Const PIPE_READMODE_MESSAGE = &H2 Public Const PIPE_SERVER_END = &H1 Public Const PIPE_TYPE_BYTE = &H0 Public Const PIPE_TYPE_MESSAGE = &H4 Public Const PIPE_UNLIMITED_INSTANCES = 255 Public Const PIPE_WAIT = &H0 Public Const FILE_SHARE_READ = &H1 Public Const FILE_SHARE_WRITE = &H2 Public Const GENERIC_READ = &H80000000 Public Const GENERIC_WRITE = &H40000000 Public Const GENERIC_EXECUTE = &H20000000 Public Const GENERIC_ALL = &H10000000 Public Const OPEN_EXISTING = 3 Public Const ERROR_PIPE_BUSY = 231& Public Const ERROR_PIPE_CONNECTED = 535& Public Const ERROR_PIPE_LISTENING = 536& Public Const ERROR_PIPE_NOT_CONNECTED = 233& Public Const ERROR_NO_DATA = 232&
Public Const BufferSize& = 51200 Public hNamePipe&, hFile&, strNamePipe$
Form中有三个按钮,分别是“创建命名管道”(CreateNPipe)、“发送文件”(SendFile)、“关闭命名管道”(CloseNamePipe),窗口中还有一个CommonDialog控件,命名为“CDlg1”。Form中代码: Dim outBuffer() As Byte, inBuffer() As Byte, BytesRead As Long, BytesWrite As Long, BytesReaded As Long, BytesWrited As Long Private Sub CloseNamePipe_Click() DisconnectNamedPipe hNamePipe CloseHandle hNamePipe CreateNPipe.Enabled = True SendFile.Enabled = False CloseNamePipe.Enabled = False End Sub
Private Sub CreateNPipe_Click() Dim hReturn& strNamePipe = "\\.\pipe\xyvanPipe" hNamePipe = CreateNamedPipe(strNamePipe, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE Or PIPE_READMODE_BYTE, 1, 0, 0, 0, 0) If hNamePipe <> -1 Then hReturn = ConnectNamedPipe(hNamePipe, 0) If hReturn = 0 Then MsgBox "管道无法等待客户端的连接!", vbInformation Or vbOKOnly Unload Me Else Label1 = "已同客户机连接上!" End If CreateNPipe.Enabled = False SendFile.Enabled = True CloseNamePipe.Enabled = True Else MsgBox "无法创建命名管道!", vbInformation Or vbOKOnly Unload Me End If End Sub
Private Sub Form_Load() With CDlg1 .CancelError = True .DialogTitle = "请选择要传输的文件:" .filename = "" .Filter = "所有文件(*.*) |