Imports System.Net.NetworkInformation
Imports System.Net.Sockets
Imports System.Text.Encoding
Public Module FileTransfer
Private Const BufferSize As Integer = 16384
Private Const Port As Integer = 2070
'''
''' 捕獲外來訊息的伺服器類別
'''
'''
Public Class FileServer
Public Shared Mainform As Form1
Public Port As Integer = FileTransfer.Port
Private ServerSocket As Socket
Public Sub New(ByRef form As Form)
Mainform = form
ServerSocket = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
Dim ipen As IPHostEntry = Dns.GetHostEntry(Dns.GetHostName)
Dim endpoint = New IPEndPoint(IPAddress.Any, Port)
ServerSocket.Bind(endpoint)
ServerSocket.Listen(1000)
ServerSocket.BeginAccept(AddressOf CatchSocket, ServerSocket)
End Sub
'捕捉新連線
Public Sub CatchSocket(ByVal result As IAsyncResult)
'重新導向到轉接器
Dim TIO As New TempInterObject(ServerSocket.EndAccept(result))
TIO.Sock.ReceiveBufferSize = BufferSize
'開始處理基本傳輸資料
TIO.Sock.BeginReceive(TIO.Buffer, 0, TIO.Buffer.Length, 0, AddressOf InfoWorker, TIO)
ServerSocket.BeginAccept(AddressOf CatchSocket, ServerSocket)
End Sub
'處理開頭資料
Public Sub InfoWorker(ByVal result As IAsyncResult)
Dim TIO As TempInterObject = CType(result.AsyncState, TempInterObject)
Dim len As Integer = TIO.Sock.EndReceive(result)
Dim Src As String = UTF8.GetString(TIO.Buffer, 0, len)
Dim Command As String = Src.Substring(0, Src.IndexOf(" "))
Dim File As String = Src.Substring(Src.IndexOf(" ") + 1)
Dim nTask As TransferTask
If Command = "GET" Then '對方要求檔案
nTask = New TransferTask(File, TIO.Sock, TransferTask.Method.SEND) '發送檔案資訊->傳輸檔案
Else '對方要求傳送檔案給本機
Dim Sign(0) As Byte
Sign(0) = IIf(My.Computer.FileSystem.FileExists(File), 0, 1) '如果檔案存在則不允許
TIO.Sock.Send(Sign, 1, SocketFlags.None) '傳送准許憑證
nTask = New TransferTask(File, TIO.Sock, TransferTask.Method.GET) '開始下載
nTask.FileSize = Val(Command.Substring(Command.IndexOf("[") + 1, Command.IndexOf("]") - Command.IndexOf("["))) '分析檔案大小
End If
End Sub
End Class
'''
''' 轉接Socket用的暫時類別
'''
'''
Public Class TempInterObject
Public Buffer(BufferSize - 1) As Byte
Public Sock As Socket
Public Sub New(ByVal SocketObject As Socket)
Me.Sock = SocketObject
End Sub
End Class
'''
''' 傳送檔案 控制類別("下載工作"物件)
'''
'''
Public Class TransferTask
Public Enum Method
[GET] = 0
[SEND] = 1
End Enum
Public FilePath As String = ""
Public Host As String = "", Port As Integer = FileTransfer.Port
Private Data(BufferSize - 1) As Byte
Private Reader As IO.FileStream
Private Sock As Socket
Private WriteStream As IO.FileStream
Protected Friend FileSize As UInteger = 0
Private ReadSize As UInteger = 0
Private LastProgress As Integer = 0
Private mType As Method = 0
Private Delegate Sub InvokeFinished(ByVal [Error] As Boolean)
Private FinishedInvoker As New InvokeFinished(AddressOf onProgressFinished)
'接收檔案(主動)
Private Sub GetNew(ByVal File As String, ByVal LocalFile As String, ByVal Host As String)
Sock = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
Sock.Connect(Host, Port)
FilePath = LocalFile
Sock.SendBufferSize = BufferSize
SendString("GET " & File)
Sock.BeginReceive(Data, 0, Data.Length, SocketFlags.None, AddressOf GetSize, Me)
End Sub
'傳送檔案(主動)
Private Sub SendNew(ByVal File As String, ByVal RemoteFile As String, ByVal Host As String)
Sock = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
Sock.Connect(Host, Port)
FilePath = File
Sock.SendBufferSize = BufferSize
FileSize = GetFileSize(File)
SendString("SEND[" & FileSize & "] " & RemoteFile)
'偵測憑證
Sock.BeginReceive(Data, 0, 1, SocketFlags.None, AddressOf Accepted, Me)
End Sub
'傳送或接收檔案(主動)
Public Sub New(ByVal FileA As String, ByVal FileB As String, ByVal Host As String, ByVal Type As Method, Optional ByVal Port As Integer = 2070)
mType = Type
Me.Host = Host
Me.Port = Port
If Type = Method.GET Then GetNew(FileA, FileB, Host) Else SendNew(FileA, FileB, Host)
End Sub
'傳送或接收檔案(被動)
Public Sub New(ByVal File As String, ByVal Sock As Socket, ByVal Type As Method)
mType = Type
Me.Sock = Sock
FilePath = File
Sock.SendBufferSize = BufferSize
If Type = Method.SEND Then
'傳送檔案
'送出檔案大小
FileSize = GetFileSize(File)
SendString(FileSize)
'偵測憑證
Sock.BeginReceive(Data, 0, 1, SocketFlags.None, AddressOf Accepted, Me)
Else
'接收檔案
Sock.BeginReceive(Data, 0, Data.Length, SocketFlags.None, AddressOf Recived, Me)
End If
End Sub
'對方准許我方傳送
Private Sub Accepted(ByVal Result As IAsyncResult)
Sock.EndReceive(Result)
If Data(0) = 0 Then
onProgressFinished(True)
Sock.Close()
Exit Sub
End If
Reader = New IO.FileStream(FilePath, IO.FileMode.Open, IO.FileAccess.Read, IO.FileShare.Read)
If Reader.Length < BufferSize Then
System.Array.Resize(Data, Reader.Length)
End If
Reader.BeginRead(Data, 0, Data.Length, AddressOf FileReaded, Me)
End Sub
'我方接收檔案串流
Private Sub Recived(ByVal result As IAsyncResult)
'讀取
Dim Len As Integer = 0
Try
Len = Sock.EndReceive(result)
Catch : End Try
If Len = 0 Then
Sock.Shutdown(SocketShutdown.Both)
Sock.Close()
If WriteStream IsNot Nothing Then
'下載終了
WriteStream.Close()
WriteStream.Dispose()
End If
onProgressFinished(False)
Exit Sub
End If
If WriteStream Is Nothing Then
WriteStream = New IO.FileStream(FilePath, IO.FileMode.Append)
End If
'修改進度!
ReadSize += Len
onProgressChanged()
WriteStream.Write(Data, 0, Len)
Try
Sock.BeginReceive(Data, 0, Data.Length, SocketFlags.None, AddressOf Recived, Me)
Catch
WriteStream.Close()
WriteStream.Dispose()
onProgressFinished(Not ReadSize = Len)
End Try
End Sub
'取得被動的對方傳送的檔案大小
Private Sub GetSize(ByVal result As IAsyncResult)
Dim Len As Integer = Sock.EndReceive(result)
FileSize = Val(UTF8.GetString(Data, 0, Len))
Dim Sign(0) As Byte
Sock.Send(Sign, 1, SocketFlags.None)
Sock.BeginReceive(Data, 0, Data.Length, SocketFlags.None, AddressOf Recived, Me)
End Sub
'傳送字串
Private Sub SendString(ByVal msg As String)
Dim Buffer() As Byte = System.Text.Encoding.UTF8.GetBytes(msg)
Sock.BeginSend(Buffer, 0, Buffer.Length, SocketFlags.None, AddressOf StringSended, Sock)
End Sub
'傳送字串的End偵聽函式
Private Sub StringSended(ByVal result As IAsyncResult)
Try : CType(result.AsyncState, Socket).EndSend(result) : Catch : End Try
End Sub
'傳送串流的End偵聽函式
Private Sub StreamSended(ByVal result As IAsyncResult)
Sock.EndSend(result) '結束非同步傳送Stream
If Reader.Length <= Reader.Position Then '如果讀到盡頭
Sock.Close()
Reader.Close()
Reader.Dispose()
onProgressFinished(False)
Exit Sub
End If
If Reader.Length - Reader.Position < BufferSize Then '如果剩下位傳送的位元組不到BufferSize
System.Array.Resize(Data, Reader.Length - Reader.Position)
End If
ReadSize += Data.Length '增加進度
onProgressChanged()
Reader.BeginRead(Data, 0, Data.Length, AddressOf FileReaded, Me)
End Sub
'讀取串流的End偵聽函式
Private Sub FileReaded(ByVal result As IAsyncResult)
Reader.EndRead(result) '結束非同步讀取Stream
Sock.BeginSend(Data, 0, Data.Length, SocketFlags.None, AddressOf StreamSended, Me) '開始傳送資料
End Sub
'進度改變檢測
Private Sub onProgressChanged()
If FileServer.Mainform.InvokeRequired Then
FileServer.Mainform.BeginInvoke(New MethodInvoker(AddressOf onProgressChanged))
Exit Sub
End If
If Int(Progress) - LastProgress > 0 Then
RaiseEvent ProgressChanged(Me, Progress)
End If
LastProgress = Int(Progress)
End Sub
'結束下載
Private Sub onProgressFinished(ByVal [Error] As Boolean)
If FileServer.Mainform.InvokeRequired Then
FileServer.Mainform.BeginInvoke(FinishedInvoker, [Error])
Exit Sub
End If
RaiseEvent ProgressChanged(Me, 100)
RaiseEvent ProgressFinished(Me, [Error])
End Sub
'屬性
Public ReadOnly Property Progress() As Single
Get
If FileSize > 0 Then
Return (ReadSize / FileSize) * 100
Else
Return 0
End If
End Get
End Property
Public ReadOnly Property Length() As UInteger
Get
Return FileSize
End Get
End Property
Public ReadOnly Property Type() As Method
Get
Return mType
End Get
End Property
'''
''' 當下載進度改變時呼叫
'''
''' "下載工作"物件
''' 進度(百分比)
'''
Public Event ProgressChanged(ByVal Sender As TransferTask, ByVal value As Integer)
'''
''' 下載完成時呼叫
'''
''' "下載工作"物件
''' 是否錯誤
'''
Public Event ProgressFinished(ByVal Sender As TransferTask, ByVal [Error] As Boolean)
End Class
'''
''' 取得檔案大小
'''
''' 檔案路徑
'''
'''
Public Function GetFileSize(ByVal Path As String) As Integer
Return My.Computer.FileSystem.GetFileInfo(Path).Length
End Function
'''
''' 非同步傳送檔案
'''
''' 本機檔案(被傳送的)
''' 遠端位置(被傳送後要放置的位置)
''' 遠端主機
''' 結束時呼叫
''' 進度更新呼叫
'''
Public Sub BeginTransFile(ByVal Local As String, ByVal Remote As String, ByVal Host As String, Optional ByVal onEnd As TransferTask.ProgressFinishedEventHandler = Nothing, Optional ByVal onChange As TransferTask.ProgressChangedEventHandler = Nothing)
Dim Task As New TransferTask(Local, Remote, Host, TransferTask.Method.SEND)
If onEnd IsNot Nothing Then AddHandler Task.ProgressFinished, onEnd
If onChange IsNot Nothing Then AddHandler Task.ProgressChanged, onChange
End Sub
'''
''' 非同步下載檔案
'''
''' 本機檔案(被傳送後要放置的位置)
''' 遠端位置(被傳送的)
''' 遠端主機
''' 結束時呼叫
''' 進度更新呼叫
'''
Public Sub BeginGetFile(ByVal Remote As String, ByVal Local As String, ByVal Host As String, Optional ByVal onEnd As TransferTask.ProgressFinishedEventHandler = Nothing, Optional ByVal onChange As TransferTask.ProgressChangedEventHandler = Nothing)
Dim Task As New TransferTask(Remote, Local, Host, TransferTask.Method.GET)
If onEnd IsNot Nothing Then AddHandler Task.ProgressFinished, onEnd
If onChange IsNot Nothing Then AddHandler Task.ProgressChanged, onChange
End Sub
End Module
沒有留言:
張貼留言