65.9K
CodeProject 正在变化。 阅读更多。
Home

使用VB.NET构建客户端/服务器应用程序,实现安全的私有文件共享

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.70/5 (13投票s)

2005年11月24日

7分钟阅读

viewsIcon

195949

downloadIcon

15608

如何使用免费SDK构建安全的私有文件共享客户端/服务器

引言

如今,大多数加密文件共享解决方案都使用数字证书,这些证书适用于用户群体庞大且事先未知的应用程序。但是,如果有人想避免获取和维护证书的成本和麻烦,那么还有另一种可行的解决方案。

本文介绍了一种用于安全私有文件共享的简单客户端/服务器解决方案。安全性通过对称加密算法(AES 128)和共享密钥来实现。当建立小型群组时,这在许多情况下可能更有效和实用。

开始之前

下载并安装文件共享SDK的免费版本,该版本可免费用于非商业用途(两个连接)。

客户端/服务器组件采用128位AES加密,无需SSL证书。它使用共享密钥,从而创建了一个基于应用程序的虚拟专用网络。除了标准的FTP操作(如上传、下载、重命名、删除等)之外,该库还提供了许多高级功能,如在服务器上打开可查找流、远程搜索文件和文本、远程zip压缩和解压缩。

服务器

加载示例项目FileServer.sln。可以通过其名为FileSrv的实例来访问服务器组件。让我们回顾一下它的主要属性和方法。

启动服务器

为了开始接受连接,我们需要执行以下步骤

  1. 将属性ListenningPort设置为监听端口号。我们从Settings窗体中获取该值。
  2. 如果我们要建立加密连接,则必须将属性SecurityMode设置为2,并将SecretKey设置为设置窗体中指定的加密密钥的值。

    如果我们不需要加密,则必须将SecurityMode设置为0。

  3. 如果我们想使用数据包压缩,则必须将UseCompression设置为True
  4. 调用方法Start()
      'Set the component properties
      Private Sub SetProperties()
        FileSrv.ListeningPort = Val(fSettings.txtPort.Text)
        If fSettings.txtKey.Text > "" Then
          FileSrv.SecurityMode = 2  'shared secret key
          FileSrv.SecretKey = fSettings.txtKey.Text
        Else
          FileSrv.SecurityMode = 0  'no encryption
        End If
        FileSrv.UseCompression = fSettings.chkCompress.Checked
      End Sub
    
    
      'Start the server
      Private Sub btnStart_Click(ByVal sender As System.Object, _
                  ByVal e As System.EventArgs) Handles btnStart.Click
        If FileSrv.Start Then
          LogMsg("Server started")
        Else
          Call MsgBox("Cannot start the server!", , "Error")
        End If
        UpdateStatus()
      End Sub

停止服务器

为了停止接受连接,我们需要调用方法Stop()。如果我们当前有客户端连接,则应使用方法RemoveClient()将其移除。

  'Stop the server
  Private Sub btnStop_Click(ByVal sender As System.Object, _
              ByVal e As System.EventArgs) Handles btnStop.Click
    Dim LI As ListViewItem
    Dim i As Integer

    FileSrv.Stop()

    For i = lvClients.Items.Count - 1 To 0 Step -1
      FileSrv.RemoveClient(lvClients.Items(i).Tag)
      lvClients.Items.Remove(lvClients.Items(i))
    Next i

    UpdateStatus()
    LogMsg("Server stopped")
  End Sub

添加新连接

当新的客户端连接时,将触发OnNewClient事件。此时,我们应该获取客户端信息(地址/端口)并将其句柄存储起来以备后用。

  'A new connection is available
  Private Sub FileSrv_OnNewClient(ByVal sender As Object, _
              ByVal e As AxbsFileServerSDK.IBSFileSrvXEvents_OnNewClientEvent) _
              Handles FileSrv.OnNewClient
    Dim LI As ListViewItem

    LI = lvClients.Items.Add("Not signed in")
    LI.Tag = e.aHandle
    LI.SubItems.Add(FileSrv.GetClientAddress(e.aHandle))
    LI.SubItems.Add(Str(FileSrv.GetClientPort(e.aHandle)))
    LI.SubItems.Add(VB6.Format(Now, "hh:mm:ss"))
    LI.SubItems.Add("Connected")

    UpdateStatus()
    LogMsg("New connection from " + LI.SubItems.Item(1).Text + _
                               ":" + LI.SubItems.Item(2).Text)
  End Sub

移除断开连接的客户端

当已连接的客户端断开连接时,将触发OnClientDisconnected事件。此时,我们应该释放与该客户端相关的所有数据。

  'A connection is broken
  Private Sub FileSrv_OnClientDisconnected(ByVal sender As Object, ByVal e As _
          AxbsFileServerSDK.IBSFileSrvXEvents_OnClientDisconnectedEvent) _
          Handles FileSrv.OnClientDisconnected
    Dim LI As ListViewItem
    LI = ItemFromHandle(e.aHandle)
    If LI Is Nothing Then Exit Sub
    LogMsg("Disconnected " + LI.Text + " " + _
            FileSrv.GetClientAddress(e.aHandle) + _
            ":" + Str(FileSrv.GetClientPort(e.aHandle)))
    FileSrv.RemoveClient(LI.Tag)
    lvClients.Items.Remove(LI)
  End Sub

提供用户密码

建立连接后,客户端立即发送登录请求。在我们简单的实现中,我们只接受默认用户(Guest)。因此,在OnNeedPassword事件的处理程序中,所询问的用户名应始终为空,并且我们始终为密码值传递一个空字符串。

  'A request for a password
  Private Sub FileSrv_OnNeedPassword1(ByVal sender As Object, ByVal e As _
          AxbsFileServerSDK.IBSFileSrvXEvents_OnNeedPasswordEvent) _
          Handles FileSrv.OnNeedPassword
    If e.aUsername = "" Then
      'this is the user Guest
      e.aOkay = True
    Else
      'Unknown user, we accept only Guest
      e.aOkay = False
    End If
  End Sub

登录

在检查密码后,服务器组件将触发OnSignin事件。从那时起,客户端就可以执行各种文件操作了。

  'A user is signed-in
  Private Sub FileSrv_OnSignin(ByVal sender As Object, ByVal e As _
          AxbsFileServerSDK.IBSFileSrvXEvents_OnSigninEvent) _
          Handles FileSrv.OnSignin
    Dim S As String
    Dim LI As ListViewItem

    S = GetUsername(e.aHandle)
    LI = ItemFromHandle(e.aHandle)
    If Not (LI Is Nothing) Then
      LI.Text = S
    End If

    S = S + " " + FileSrv.GetClientAddress(e.aHandle) + _
        ":" + FileSrv.GetClientPort(e.aHandle)
    If e.aCode = 0 Then
      S = S + " signed in successfully"
    Else
      S = S + " failed sign-in"
    End If

    LogMsg(S)
  End Sub

列出文件夹内容

客户端登录后首先执行的操作是获取(主)文件夹内容。OnNeedListFolder事件的处理程序负责授予或拒绝操作的权限。有两个输入参数和两个输出参数:

  • aHandle - 触发事件的客户端对象的句柄。
  • aPath - 相关文件夹的相对路径名。
  • aOkay - 将此变量设置为True以允许操作。
  • aRoot - 如果允许操作,请在此处写入分配给该用户的根文件夹。
  'A request to list the folder contents
  Private Sub FileSrv_OnNeedListFolder(ByVal sender As Object, ByVal e As _
          AxbsFileServerSDK.IBSFileSrvXEvents_OnNeedListFolderEvent) _
          Handles FileSrv.OnNeedListFolder
    e.aOkay = True
    e.aRoot = App_Path()
    LogMsg(GetUsername(e.aHandle) + ": list folder " + PreSlash(e.aPath))
  End Sub

列表操作的结束将通过OnListFolderDone事件发出信号。

处理“创建文件夹”请求

当从客户端接收到“创建文件夹”请求时,将触发OnNeedCreateFolder事件。处理程序必须授予或拒绝操作的权限。参数是:

  • aHandle - 触发事件的客户端对象的句柄。
  • aPath - 要压缩的文件的相对路径名。
  • aOkay - 将此变量设置为True以允许操作。
  • aRoot - 如果允许操作,请在此处写入分配给该用户的根文件夹。
  'A request to create a folder
  Private Sub FileSrv_OnNeedCreateFolder(ByVal sender As Object, ByVal e As _
          AxbsFileServerSDK.IBSFileSrvXEvents_OnNeedCreateFolderEvent) _
          Handles FileSrv.OnNeedCreateFolder
    e.aOkay = True
    e.aRoot = App_Path()
    LogMsg(GetUsername(e.aHandle) + ": create folder " + PreSlash(e.aPath))
  End Sub

处理“重命名文件”请求

当从客户端接收到“重命名文件”请求时,将触发OnNeedRenameFile事件。处理程序必须授予或拒绝操作的权限。参数是:

  • aHandle - 触发事件的客户端对象的句柄。
  • aPath - 要重命名的文件的相对路径名。
  • aOkay - 将此变量设置为True以允许操作。
  • aRoot - 如果允许操作,请在此处写入分配给该用户的根文件夹。
  'A request to rename a file
  Private Sub FileSrv_OnNeedRenameFile(ByVal sender As Object, ByVal e As _
          AxbsFileServerSDK.IBSFileSrvXEvents_OnNeedRenameFileEvent) _
          Handles FileSrv.OnNeedRenameFile
    e.aOkay = True
    e.aRoot = App_Path()
    LogMsg(GetUsername(e.aHandle) + ": rename file " + PreSlash(e.aPath))
  End Sub

处理“删除文件”请求

当从客户端接收到“删除文件”请求时,将触发OnNeedDeleteFile事件。处理程序必须授予或拒绝操作的权限。参数是:

  • aHandle - 触发事件的客户端对象的句柄。
  • aPath - 要删除的文件的相对路径名。
  • aOkay - 将此变量设置为True以允许操作。
  • aRoot - 如果允许操作,请在此处写入分配给该用户的根文件夹。
  'A request to delete a file
  Private Sub FileSrv_OnNeedDeleteFile(ByVal sender As Object, ByVal e As _
          AxbsFileServerSDK.IBSFileSrvXEvents_OnNeedDeleteFileEvent) _
          Handles FileSrv.OnNeedDeleteFile
    e.aOkay = True
    e.aRoot = App_Path()
    LogMsg(GetUsername(e.aHandle) + ": delete file " + PreSlash(e.aPath))
  End Sub

处理下载请求

当从客户端接收到下载请求时,将触发OnNeedDownload事件。处理程序必须授予或拒绝操作的权限。参数是:

  • aHandle - 触发事件的客户端对象的句柄。
  • aPath - 要下载的文件的相对路径名。
  • aOkay - 将此变量设置为True以允许操作。
  • aRoot - 如果允许操作,请在此处写入分配给该用户的根文件夹。
  'A request to download
  Private Sub FileSrv_OnNeedDownload(ByVal sender As Object, ByVal e As _
          AxbsFileServerSDK.IBSFileSrvXEvents_OnNeedDownloadEvent) _
          Handles FileSrv.OnNeedDownload
    e.aOkay = True
    e.aRoot = App_Path()
    LogMsg(GetUsername(e.aHandle) + ": start downloading " + PreSlash(e.aPath))
  End Sub

OnDownloadDone事件将通知操作的完成。

  'The download operation is completed
  Private Sub FileSrv_OnDownloadDone(ByVal sender As Object, ByVal e As _
          AxbsFileServerSDK.IBSFileSrvXEvents_OnDownloadDoneEvent) _
          Handles FileSrv.OnDownloadDone
    LogMsg(GetUsername(e.aHandle) + ": finish downloading ")
  End Sub

处理上传请求

当从客户端接收到上传请求时,将触发OnNeedUpload事件。处理程序必须授予或拒绝操作的权限。参数是:

  • aHandle - 触发事件的客户端对象的句柄。
  • aPath - 相关文件的相对路径名。
  • aOkay - 将此变量设置为True以允许操作。
  • aRoot - 如果允许操作,请在此处写入分配给该用户的根文件夹。
  'A request to upload
  Private Sub FileSrv_OnNeedUpload(ByVal sender As Object, ByVal e As _
          AxbsFileServerSDK.IBSFileSrvXEvents_OnNeedUploadEvent) _
          Handles FileSrv.OnNeedUpload
    e.aOkay = True
    e.aRoot = App_Path()
    LogMsg(GetUsername(e.aHandle) + ": start uploading " + PreSlash(e.aPath))
  End Sub

OnUploadDone事件将通知操作的完成。

  'The upload operation is completed
  Private Sub FileSrv_OnUploadDone(ByVal sender As Object, ByVal e As _
          AxbsFileServerSDK.IBSFileSrvXEvents_OnUploadDoneEvent) _
          Handles FileSrv.OnUploadDone
    LogMsg(GetUsername(e.aHandle) + ": finish uploading ")
  End Sub

客户端

加载示例项目FileClient.sln。可以通过其名为FileCln的实例来访问客户端组件。让我们回顾一下它的主要属性和方法。

连接到服务器

为了建立与服务器的新连接,我们需要执行以下步骤:

  1. 如果我们想要加密连接,则必须将属性SecurityMode设置为2,并将SecretKey设置为“Connect”窗体上指定的加密密钥的值。

    如果我们不需要加密,则只需将SecurityMode设置为0。

  2. 如果我们想使用数据包压缩,则必须将属性UseCompression设置为True
  3. 调用方法Connect(Addr, Port),并将服务器的IP地址/域名和监听端口作为参数传递。
    'Initiate a connection request
    Private Sub btnConnect_Click(ByVal sender As System.Object, _
            ByVal e As System.EventArgs) Handles btnConnect.Click
      If fConnect.ShowDialog <> DialogResult.OK Then Exit Sub
    
      If fConnect.txtKey.Text() > "" Then
        FileCln.SecurityMode = 2  'shared secret key
        FileCln.SecretKey = fConnect.txtKey.Text
      Else
        FileCln.SecurityMode = 0  ' no encryption
      End If
      If Not FileCln.Connect(fConnect.txtHost.Text, _
                     Val(fConnect.txtPort.Text)) Then
        MsgBox("Error Code: " + Str(FileCln.LastError))
      End If
    
      UpdateStatus()
    End Sub

与服务器断开连接

为了结束与服务器的连接,我们需要调用方法Disconnect()

  'Disconnect from the server
  Private Sub btnDisconnect_Click(ByVal sender As System.Object, _
              ByVal e As System.EventArgs) Handles btnDisconnect.Click
    If Not FileCln.Connected Then Exit Sub
    FileCln.Disconnect()
    Call DoDisconnected()
  End Sub

列出文件夹内容

方法ListFolder()发送一个请求以获取文件夹内容。

'Request the folder contents
Private Sub ListFolder()
  If NowList Then Exit Sub
  If FileCln.ListFolder(txtFolder.Text) Then
    NowList = True
    lvFiles.Items.Clear()
    btnList.Enabled = False
    If txtFolder.Text > "\" Then
      Call DoHaveListItem("..", True, 0, 0, 0, 0)
    End If
  Else
    CheckError(FileCln.LastError)
  End If
End Sub

对于每个可用的文件夹项,将触发OnHaveListItem事件。

'Process a list item
Private Sub DoHaveListItem(ByVal aName As String, _
            ByVal aFolder As Boolean, ByVal aLoSize As Long, _
            ByVal aHiSize As Long, _
            ByVal aLoTime As Long, ByVal aHiTime As Long)
    Dim LI As ListViewItem
    Dim ft As FILETIME
    Dim Dt As Date

    LI = lvFiles.Items.Add(aName)
    If aFolder Then
      LI.ImageIndex = FolderImgIdx
      LI.SubItems.Add(" ")
    Else
      LI.ImageIndex = FileImgIdx
      LI.SubItems.Add(Str(aLoSize))
    End If

    ft.dwHighDateTime = aHiTime
    ft.dwLowDateTime = aLoTime
    Dt = FileTimeToDate(ft)
    LI.SubItems.Add(Dt)
End Sub

列表的结束将通过OnListFolderDone()事件发出信号。

'End of the list
Private Sub FileCln_OnListFolderDone(ByVal sender As Object, ByVal e As _
        AxbsFileClientSDK.IBSFileClnXEvents_OnListFolderDoneEvent) _
        Handles FileCln.OnListFolderDone
  NowList = False
  'CheckError(aCode)
  UpdateButtons()
End Sub

创建新文件夹

方法CreateFolder()发送一个请求来创建一个新文件夹。

'Request a new folder
Private Sub btnNew_Click(ByVal sender As System.Object, _
            ByVal e As System.EventArgs) Handles btnNew.Click
  Dim Nm As String
  Nm = InputBox("Folder name:", "Create folder")
  If Nm = "" Then Exit Sub
  If FileCln.CreateFolder(AddSlash(txtFolder.Text) + Nm) Then
    NowCreate = True
  Else
    CheckError(FileCln.LastError)
  End If
  UpdateButtons()
End Sub

文件夹创建完成后,将触发OnCreateFolderDone()事件。

'A new folder is created
Private Sub FileCln_OnCreateFolderDone(ByVal sender As Object, ByVal e As _
        AxbsFileClientSDK.IBSFileClnXEvents_OnCreateFolderDoneEvent) _
        Handles FileCln.OnCreateFolderDone
  NowCreate = False
  CheckError(e.aCode)
  UpdateButtons()
  If e.aCode > 0 Then Exit Sub
  Call ListFolder()
End Sub

重命名文件

方法RenameFile()发送一个请求以在服务器上重命名文件。

'Send rename request
Private Sub btnRename_Click(ByVal sender As System.Object, _
            ByVal e As System.EventArgs) Handles btnRename.Click
  Dim Nm As String

  If lvFiles.SelectedItems.Count = 0 Then Exit Sub
  If lvFiles.SelectedItems(0).ImageIndex = FolderImgIdx Then
    'it is a folder
    Nm = InputBox("New name:", "Rename folder")
    If Nm = "" Then Exit Sub
    If FileCln.RenameFolder(AddSlash(txtFolder.Text) + _
               lvFiles.SelectedItems(0).Text, _
               AddSlash(txtFolder.Text) + Nm) Then
      NowRenameFolder = True
    Else
      CheckError(FileCln.LastError)
    End If
  Else
    'it is a file
    Nm = InputBox("New name:", "Rename file")
    If Nm = "" Then Exit Sub
    If FileCln.RenameFile(AddSlash(txtFolder.Text) + _
               lvFiles.SelectedItems(0).Text, _
               AddSlash(txtFolder.Text) + Nm) Then
      NowRenameFile = True
    Else
      CheckError(FileCln.LastError)
    End If
  End If
  Call UpdateButtons()
End Sub

收到确认后,将触发OnRenameFileDone()事件。

'A file is renamed
Private Sub FileCln_OnRenameFileDone(ByVal sender As Object, ByVal e As _
        AxbsFileClientSDK.IBSFileClnXEvents_OnRenameFileDoneEvent) _
        Handles FileCln.OnRenameFileDone
  NowRenameFile = False
  CheckError(e.aCode)
  Call UpdateButtons()
  If e.aCode > 0 Then Exit Sub
  Call ListFolder()
End Sub

删除文件

方法DeleteFile()发送请求以删除文件。

'Send delete request
Private Sub btnDelete_Click(ByVal sender As System.Object, _
            ByVal e As System.EventArgs) Handles btnDelete.Click
  If lvFiles.SelectedItems.Count = 0 Then Exit Sub
  If lvFiles.SelectedItems(0).ImageIndex = FolderImgIdx Then
    'it is a folder
    If FileCln.DeleteFolder(AddSlash(txtFolder.Text) + _
               lvFiles.SelectedItems(0).Text) Then
      NowDeleteFolder = True
    Else
      CheckError(FileCln.LastError)
    End If
  Else
    'it is a file
    If FileCln.DeleteFile(AddSlash(txtFolder.Text) + _
                   lvFiles.SelectedItems(0).Text) Then
      NowDeleteFile = True
    Else
      CheckError(FileCln.LastError)
    End If
  End If
  Call UpdateButtons()
End Sub

收到确认后,将触发OnDeleteFileDone()事件。

'A file is deleted
Private Sub FileCln_OnDeleteFileDone(ByVal sender As Object, ByVal e As _
        AxbsFileClientSDK.IBSFileClnXEvents_OnDeleteFileDoneEvent) _
        Handles FileCln.OnDeleteFileDone
  NowDeleteFile = False
  CheckError(e.aCode)
  Call UpdateButtons()
  If e.aCode > 0 Then Exit Sub
  Call ListFolder()
End Sub

下载文件

方法Download()发送一个请求以下载文件。

'Send download request
Private Sub GoDownload()
  If lvFiles.SelectedItems.Count = 0 Then Exit Sub
  If lvFiles.SelectedItems(0).ImageIndex = FolderImgIdx Then
      Exit Sub

  SaveDlg.FileName = lvFiles.SelectedItems(0).Text
  If SaveDlg.ShowDialog() <> DialogResult.OK Then Exit Sub
  DnldFile = lvFiles.SelectedItems(0).Text

  If FileCln.Download(AddSlash(txtFolder.Text) + _
             lvFiles.SelectedItems(0).Text, _
             ExtractFilePath(SaveDlg.FileName)) Then
    NowDownload = True
    txtDownload.Text = DnldFile + ": handshaking"
  Else
    CheckError(FileCln.LastError)
  End If
  UpdateStatus()
End Sub

OnDownloadProgress()事件将通知下载进度。

'Download progress info
Private Sub FileCln_OnDownloadProgress(ByVal sender As Object, ByVal e As _
        AxbsFileClientSDK.IBSFileClnXEvents_OnDownloadProgressEvent) _
        Handles FileCln.OnDownloadProgress
  txtDownload.Text = DnldFile + " - " + _
           Str(e.aCountLo) + "/" + Str(e.aSizeLo)
End Sub

下载完成后,将触发OnDownloadDone()事件。

'The download is completed
Private Sub FileCln_OnDownloadDone(ByVal sender As Object, _
        ByVal e As AxbsFileClientSDK.IBSFileClnXEvents_OnDownloadDoneEvent) _
        Handles FileCln.OnDownloadDone
  If e.aCode = 0 Then
    txtDownload.Text = DnldFile + " - Done."
  Else
    txtDownload.Text = DnldFile + " - Aborted: " + ErrorText(e.aCode)
  End If
  NowDownload = False
  Call UpdateButtons()
End Sub

上传文件

方法Upload()发送一个请求以上传文件。

'Send upload request
Private Sub btnUpload_Click(ByVal sender As System.Object, _
            ByVal e As System.EventArgs) Handles btnUpload.Click
  On Error GoTo IsCanceled

  OpenDlg.FileName = ""
  If OpenDlg.ShowDialog() <> DialogResult.OK Then Exit Sub

  UpldFile = OpenDlg.FileName
  If FileCln.Upload(UpldFile, txtFolder.Text) Then
    NowUpload = True
    UpldFol = txtFolder.Text
    txtUpload.Text = ExtractFileName(UpldFile) + ": handshaking"
  Else
    CheckError(FileCln.LastError)
  End If
  UpdateStatus()

IsCanceled:
End Sub

OnUploadProgress()事件将提供有关上传进度的信息。

'Upload progress info
Private Sub FileCln_OnUploadProgress(ByVal sender As Object, _
        ByVal e As AxbsFileClientSDK.IBSFileClnXEvents_OnUploadProgressEvent) _
        Handles FileCln.OnUploadProgress
  txtUpload.Text = ExtractFileName(UpldFile) + " - " + _
                   Str(e.aCountLo) + "/" + Str(e.aSizeLo)
End Sub

上传操作完成后,将触发OnUploadDone()事件。

'The upload is completed
Private Sub FileCln_OnUploadDone(ByVal sender As Object, _
        ByVal e As AxbsFileClientSDK.IBSFileClnXEvents_OnUploadDoneEvent) _
        Handles FileCln.OnUploadDone
  If e.aCode = 0 Then
    txtUpload.Text = ExtractFileName(UpldFile) + " - Done."
  Else
    txtUpload.Text = ExtractFileName(UpldFile) + _
                     " - Aborted: " + ErrorText(e.aCode)
  End If
  NowUpload = False
  Call UpdateButtons()
  If UpldFol = txtFolder.Text Then
    Call ListFolder()
  End If
End Sub

底线

如今,由于各种原因,互联网是不安全的。在许多情况下,交换敏感信息可能是一个问题。在本文中,我们介绍了一种简单且经济实惠的解决方案,用于安全私有文件共享,而无需证书。

© . All rights reserved.