明輝手游網(wǎng)中心:是一個(gè)免費(fèi)提供流行視頻軟件教程、在線學(xué)習(xí)分享的學(xué)習(xí)平臺(tái)!

用命名管道完成局域網(wǎng)上2臺(tái)主機(jī)間的文件拷貝

[摘要]作者: 徐原 能實(shí)現(xiàn)局域網(wǎng)上兩臺(tái)主機(jī)間文件拷貝的方法有很多種,這里介紹的“命名管道”(Named Pipe )是一種比較可靠的進(jìn)程間通信機(jī)制,可用在同一臺(tái)計(jì)算機(jī)不同進(jìn)程間,也可用在不同計(jì)算機(jī)的不...
作者: 徐原

  能實(shí)現(xiàn)局域網(wǎng)上兩臺(tái)主機(jī)間文件拷貝的方法有很多種,這里介紹的“命名管道”(Named Pipe )是一種比較可靠的進(jìn)程間通信機(jī)制,可用在同一臺(tái)計(jì)算機(jī)不同進(jìn)程間,也可用在不同計(jì)算機(jī)的不同進(jìn)程間,可以是單向的,也可以是雙向的,Windows NT、Windows 2000、Windows 95和Windows 98均提供了對(duì)它的支持,而且在Unix下也有類(lèi)似的概念。它是在Microsoft LAN管理器和IBMLAN服務(wù)器網(wǎng)絡(luò)操作系統(tǒng)上實(shí)現(xiàn)的。
命名管道使用了MSNP(微軟網(wǎng)絡(luò)提供者)重定向器,這樣應(yīng)用程序便可以不用了解網(wǎng)絡(luò)協(xié)議的細(xì)節(jié)而利用該機(jī)制實(shí)現(xiàn)網(wǎng)絡(luò)上的數(shù)據(jù)傳輸。它采用“命名管道文件系統(tǒng)”(Named Pipe File System)接口,其命名是采用UNC(通用命名規(guī)范)格式的:
\\ServerName\Pipe\[pipename]
\\ServerName指明命名管道是在那個(gè)服務(wù)器上創(chuàng)建的,ServerName既可以是一個(gè)實(shí)際的計(jì)算機(jī)名,也可以是小數(shù)點(diǎn)(“.”)以指明是在本機(jī)上創(chuàng)建;\Pipe是一個(gè)硬編碼(Hardcode)不用區(qū)分大小寫(xiě)的字符串用以指明這是一個(gè)管道名,該文件名從屬于NPFS;[pipename]是實(shí)際的自定義的管道名,該名稱(chēng)在前面指定的服務(wù)器上必須是唯一的,該名稱(chēng)可以包含多級(jí)目錄,但目錄名必須不是已經(jīng)創(chuàng)建的管道名,例:
\\.\Pipe\xyPipe’這是一個(gè)合法管道名
\\.\Pipe\xyPipe\Pipe’這不是一個(gè)合法的管道名,因?yàn)榍懊娴哪夸沑\.\Pipe\xyPipe是一個(gè)已經(jīng)創(chuàng)建的管道名了。
\\.\Pipe\xxyPipe\Pipe’這也是一個(gè)合法的文件名
命名管道有兩種基本通信模式:字節(jié)模式和消息模式。在字節(jié)模式中,數(shù)據(jù)是以字節(jié)流的形式在管道種傳輸,數(shù)據(jù)之間沒(méi)有邊界,在管道寫(xiě)入和讀出操作中是以字節(jié)流即數(shù)據(jù)塊為基本單位操作的,這適合傳輸大容量數(shù)據(jù);在消息模式中,數(shù)據(jù)是以一條條不連續(xù)的消息為基本傳輸單元,消息和消息之間有邊界,在管道寫(xiě)入和讀出操作中也是以消息為單位進(jìn)行操作的,這種方式適合傳輸量小的數(shù)據(jù)。因?yàn)楝F(xiàn)在的文件大小常常有幾百K甚至更大,所以程序中選擇使用字節(jié)模式。
下面詳細(xì)介紹一下CreateNamedPipe()這個(gè)函數(shù),該函數(shù)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:為命名管道打開(kāi)的模式,有PIPE_ACCESS_DUMPLEX(雙向)、PIPE_ACCESS_INBOUND(輸入)、PIPE_ACCESS_OUTBOUND(輸出)這三種,這些標(biāo)志還可以和一些附加的I/O控制和安全模式的常數(shù)組合使用,詳細(xì)可參考MSDN。
dwPipeMode:為管道傳輸模式,有前面所述的PIPE_TYPE_BYTE(字節(jié)模式)和PIPE_TYPE_MESSAGE(消息模式)兩種,可以和PIPE_READMODE_BYTE和PIPE_READMODE_MESSAGE常數(shù)組合使用以限定客戶(hù)端的讀取模式?梢允褂肞IPE_TYPE_MESSAGE 和 PIPE_READMODE_BYTE組合來(lái)指定發(fā)送者以消息模式向管道發(fā)送數(shù)據(jù),而接收者一次可以讀取任意數(shù)量的字節(jié)。注意不可將PIPE_TYPE_BYTE和PIPE_READMODE_MESSAGE組合使用,這樣會(huì)導(dǎo)致CreateNamedPipe()函數(shù)調(diào)用失敗,因?yàn)樽止?jié)模式?jīng)]有邊界,在接收端用消息模式讀取的時(shí)候無(wú)法判斷消息的邊界。
nMaxInstances:管道最大的連接實(shí)例句柄,其范圍在1到255之間。
nOutBufferSize和nInBufferSize分別指明管道輸出和輸入緩沖區(qū)的大小,如設(shè)為0則使用系統(tǒng)默認(rèn)大小。
nDefaultTimeOut以毫秒為單位設(shè)定客戶(hù)機(jī)等待同命名管道建立連接的最長(zhǎng)時(shí)間。
LpSecurityAttruibutes為一個(gè)安全描述符,設(shè)為Null表示使用系統(tǒng)默認(rèn)的描述符,同時(shí)句柄不可繼承。
要注意的是在程序中命名管道的寫(xiě)操作中一次最大只能寫(xiě)64K字節(jié)的數(shù)據(jù),
下面是服務(wù)器端程序:
(模塊中):
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中有三個(gè)按鈕,分別是“創(chuàng)建命名管道”(CreateNPipe)、“發(fā)送文件”(SendFile)、“關(guān)閉命名管道”(CloseNamePipe),窗口中還有一個(gè)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 "管道無(wú)法等待客戶(hù)端的連接!", vbInformation Or vbOKOnly
Unload Me
Else
Label1 = "已同客戶(hù)機(jī)連接上!"
End If
CreateNPipe.Enabled = False
SendFile.Enabled = True
CloseNamePipe.Enabled = True
Else
MsgBox "無(wú)法創(chuàng)建命名管道!", vbInformation Or vbOKOnly
Unload Me
End If
End Sub

Private Sub Form_Load()
With CDlg1
.CancelError = True
.DialogTitle = "請(qǐng)選擇要傳輸?shù)奈募?quot;
.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 "無(wú)法打開(kāi)文件" & strFileName, vbInformation Or vbOKOnly
Exit Sub
End If
lpFileSize = GetFileSize(hFile, lpFileSizeHigh)
If lpFileSize = 0 Then
MsgBox "該文件大小為零,不用發(fā)送!", 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 '發(fā)送短文件名
ReDim inBuffer(5)
ReadFile hNamePipe, inBuffer(0), 6, BytesReaded, 0 '讀取客戶(hù)端對(duì)話(huà)信息
If StrConv(inBuffer, vbUnicode) = "Cancel" Then
MsgBox "客戶(hù)端保存時(shí)選擇了取消,發(fā)送終止!", 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

客戶(hù)端程序(模塊中程序和服務(wù)器端是一樣的,這里省略不寫(xiě)了),F(xiàn)orm中有一個(gè)Text框,用以輸入要打開(kāi)連接的服務(wù)器端的命名管道的名稱(chēng),一個(gè)CommonDialog(CDlg1)控件,另還有一“連接命名管道”(Connect)按鈕和“斷開(kāi)連接”(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 "沒(méi)有可用的命名管道以供連接!", 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 "無(wú)法打開(kāi)指定的命名管道進(jìn)行讀寫(xiě)!", 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 "無(wú)法創(chuàng)建指定文件,請(qǐng)重新選擇文件名!", 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上調(diào)試通過(guò)。
在處理網(wǎng)絡(luò)事務(wù)上,命名管道接口比Net BIOS要好,而且只需使用一個(gè)簡(jiǎn)單的調(diào)用就可達(dá)到目的,而無(wú)需通過(guò)Net BIOS執(zhí)行許多操作。然而,命名管道接口并不提供Net BIOS的一些特征,如無(wú)連接數(shù)據(jù)報(bào)服務(wù)和允許向一個(gè)組發(fā)送消息的命名功能。