用命名的管道实现局域网两台主机的文件拷贝

发布时间:2017-03-22 16:34

能实现局域网上两台主机间文件拷贝的方法有很多种,这里由小编介绍的“命名管道”(Named Pipe )是一种比较可靠的进程间通信机制,可用在同一台计算机不同进程间,也可用在不同计算机的不同进程间,可以是单向的,也可以是双向的,希望对你有帮助!

用命名的管道实现局域网两台主机的文件拷贝方法:

命名管道使用了MSNP(网络提供者)重定向器,这样应用程序便可以不用了解网络协议的细节而利用该机制实现网络上的数据传输。它采用“命名管道文件系统”(Named Pipe File System)接口,其命名是采用UNC(通用命名规范)格式的:

]

指明命名管道是在那个服务器上创建的,ServerName既可以是一个实际的计算机名,也可以是小数点(“.”)以指明是在本机上创建;Pipe是一个硬编码(Hardcode)不用区分大小写的字符串用以指明这是一个管道名,该文件名从属于NPFS;[pipename]是实际的自定义的管道名,该名称在前面指定的服务器上必须是唯一的,该名称可以包含多级目录,但目录名必须不是已经创建的管道名,例:

这是一个合法管道名

这不是一个合法的管道名,因为前面的目录是一个已经创建的管道名了。

这也是一个合法的文件名

命名管道有两种基本通信模式:字节模式和消息模式。在字节模式中,数据是以字节流的形式在管道种传输,数据之间没有边界,在管道写入和读出操作中是以字节流即数据块为基本单位操作的,这适合传输大容量数据;在消息模式中,数据是以一条条不连续的消息为基本传输单元,消息和消息之间有边界,在管道写入和读出操作中也是以消息为单位进行操作的,这种方式适合传输量小的数据。因为现在的文件大小常常有几百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 = ""

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 = "所有文件(*.*)|*.*"

.Flags = cdlOFNExplorer Or cdlOFNFileMustExist Or cdlOFNPathMustExist

.InitDir = "d:"

End With

SendFile.Enabled = False

CloseNamePipe.Enabled = False

End Sub

Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)

DisconnectNamedPipe hNamePipe

CloseHandle hFile

CloseHandle hNamePipe

End Sub

Private Sub SendFile_Click()

On Error Resume Next

Dim strFileName$, lpFileSize&, lpFileSizeHigh&, lpFileSizeLeast&, byteEnd() As Byte

Dim strShortName$

CDlg1.ShowOpen

If Err.Number = 32755 Then Exit Sub

strFileName = CDlg1.filename

strShortName = CDlg1.FileTitle

hFile = CreateFile(strFileName, GENERIC_READ, FILE_SHARE_READ Or FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0)

If hFile = -1 Then

MsgBox "无法打开文件" & strFileName, vbInformation Or vbOKOnly

Exit Sub

End If

lpFileSize = GetFileSize(hFile, lpFileSizeHigh)

If lpFileSize = 0 Then

MsgBox "该文件大小为零,不用发送!", vbInformation Or vbOKOnly

CloseHandle hFile

Exit Sub

End If

lpFileSizeLeast = lpFileSize

byteEnd() = StrConv(strShortName, vbFromUnicode)

ReDim outBuffer(UBound(byteEnd))

ByteCopy byteEnd, outBuffer

WriteFile hNamePipe, byteEnd(0), UBound(byteEnd) + 1, BytesWrited, 0 '发送短文件名

ReDim inBuffer(5)

ReadFile hNamePipe, inBuffer(0), 6, BytesReaded, 0 '读取客户端对话信息

If StrConv(inBuffer, vbUnicode) = "Cancel" Then

MsgBox "客户端保存时选择了取消,发送终止!", vbInformation Or vbOKOnly

CloseHandle hFile

Exit Sub

End If

Label1.Caption = "正在传输中…"

While lpFileSize > 0

If lpFileSize > BufferSize Then

ReDim outBuffer(BufferSize - 1)

ReadFile hFile, outBuffer(0), BufferSize, BytesReaded, 0

WriteFile hNamePipe, outBuffer(0), BytesReaded, BytesWrited, 0

Else

ReDim outBuffer(lpFileSize - 1)

ReadFile hFile, outBuffer(0), lpFileSize, BytesReaded, 0

WriteFile hNamePipe, outBuffer(0), lpFileSize, BytesWrited, 0

End If

lpFileSize = lpFileSize - BytesReaded

ReadFile hNamePipe, inBuffer(0), 6, BytesReaded, 0

Wend

byteEnd() = StrConv("EOF", vbFromUnicode)

ReDim outBuffer(UBound(byteEnd))

ByteCopy byteEnd, outBuffer

WriteFile hNamePipe, outBuffer(0), 3, BytesWrited, 0

CloseHandle hFile

Label1 = "传送文件完毕!"

End Sub

Public Sub ByteCopy(bySrc() As Byte, byDes() As Byte)

Dim I As Long

For i = LBound(bySrc) To UBound(bySrc)

byDes(i) = bySrc(i)

Next

End Sub

客户端程序(模块中程序和服务器端是一样的,这里省略不写了),Form中有一个Text框,用以输入要打开连接的服务器端的命名管道的名称,一个CommonDialog(CDlg1)控件,另还有一“连接命名管道”(Connect)按钮和“断开连接”(Disconnect)按钮,程序如下:

Dim inBuffer() As Byte, BytesRead&, BytesReaded&, BytesWrited&, strFileName$

Private Sub Connect_Click()

Dim hRes&

strNamePipe = Text1

hRes = WaitNamedPipe(strNamePipe, -1)

If hRes = 0 Then

MsgBox "没有可用的命名管道以供连接!", vbInformation Or vbOKOnly

Exit Sub

End If

hNamePipe = CreateFile(strNamePipe, GENERIC_READ Or GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)

If hNamePipe = 0 Then

MsgBox "无法打开指定的命名管道进行读写!", vbInformation Or vbOKOnly

Exit Sub

End If

FileSave

End Sub

Private Sub Disconnect_Click()

CloseHandle hFile

CloseHandle hNamePipe

End Sub

Private Sub Form_Load()

With CDlg1

.CancelError = True

.DialogTitle = "保存为:"

.FileName = ""

' .Filter = "所有文件(*.*)|*.*"

.Flags = cdlOFNExplorer Or cdlOFNOverwritePrompt

.InitDir = "d:"

End With

End Sub

Private Sub FileSave()

BytesRead = 51200

Dim AckByte() As Byte

ReDim inBuffer(BytesRead - 1)

On Error Resume Next

Do

ReadFile hNamePipe, inBuffer(0), BytesRead, BytesReaded, 0

If BytesReaded < 258 Then

strFileName = Trim(StrConv(inBuffer, vbUnicode))

strFileName = Left(strFileName, InStr(strFileName, Chr(0)) - 1)

If strFileName Like "EOF*" And BytesReaded = 3 Then

CloseHandle hFile

MsgBox "文件接收完毕!", vbInformation Or vbOKOnly Or vbSystemModal

Exit Sub

Else

CDlg1.Filter = UCase(GetExtension(strFileName)) & "文件(*." & GetExtension(strFileName) & ")|*." & GetExtension(strFileName)

CDlg1.FileName = Left(strFileName, InStr(strFileName, ".") - 1)

ReSelect: CDlg1.ShowSave

If Err.Number = 32755 Then

AckByte() = StrConv("Cancel", vbFromUnicode)

WriteFile hNamePipe, AckByte(0), UBound(AckByte()) + 1, BytesWrited, 0

MsgBox "你选择了取消键!", vbInformation Or vbOKOnly

Exit Sub

End If

hFile = CreateFile(CDlg1.FileName, GENERIC_READ Or GENERIC_WRITE, 0, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0)

If hFile = -1 Then

MsgBox "无法创建指定文件,请重新选择文件名!", vbInformation Or vbOKOnly

GoTo ReSelect

End If

AckByte() = StrConv("RecvOk", vbFromUnicode)

WriteFile hNamePipe, AckByte(0), UBound(AckByte()) + 1, BytesWrited, 0

End If

Else

WriteFile hFile, inBuffer(0), BytesReaded, BytesWrited, 0

AckByte() = StrConv("RecvOk", vbFromUnicode)

WriteFile hNamePipe, AckByte(0), UBound(AckByte()) + 1, BytesWrited, 0

End If

Loop Until 1 = 0

End Sub

Private Function GetExtension(ByVal FileName$) As String

GetExtension = Right(FileName, Len(FileName) - InStr(FileName, "."))

End Function

Public Sub ByteCopy(bySrc() As Byte, byDes() As Byte)

Dim i&

For i = LBound(bySrc) To UBound(bySrc)

byDes(i) = bySrc(i)

Next

End Sub

Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)

CloseHandle hFile

CloseHandle hNamePipe

End Sub

该程序在VB5、Windows NT 4.0上调试通过。

在处理网络事务上,命名管道接口比Net BIOS要好,而且只需使用一个简单的调用就可达到目的,而无需通过Net BIOS执行许多操作。然而,命名管道接口并不提供Net BIOS的一些特征,如无连接数据报服务和允许向一个组发送消息的命名功能。

希望小编的整理对你有帮助,谢谢!

用命名的管道实现局域网两台主机的文件拷贝的评论条评论