none
請問二個有關TCPListener及TCPClient的問題 RRS feed

  • 問題

  •  

    各位先進大家好, 小弟寫了一個Class物件, 並且宣告成可序列化, 目前可以成功把已經實體化的物件, 寫入檔案中,

     

    也可以由檔案讀出來, 經過反序列化後的產生實體物件, 現在我想建立一個Windows服務放在Server端

     

    把物件從用戶端傳去, 在Server端還原後, 在Server端進行處理, 我是使用TCPListener搭配TCPClient製作這個功能的

     

    想請問各位幾個問題:

     

    1. 不論Client端或Server端, 在接收資料時, 我參考的範例程式都是先建立一個Buffer as Byte()這樣的變數

    而且只要是用來接收用的, 就會先把陣列的大小宣告好, 比如Big Smileim Data = New [Byte](256), 我的問題是

    如果傳過來的資料大小超過257時, 我該怎麼做處理呢? 而且, 接收端事先並不知道總共要接收多少資料

    才能把資料完整接收完, 如果要像 璉璉大大說的 分段接收, 那我程式碼應該怎麼樣撰寫呢?

    以下是我目前使用的讀取程式

                Dim Client As TcpClient = Server.AcceptTcpClient()
                Dim Data = New [Byte](256) {}
                Dim Stream As Sockets.NetworkStream = Client.GetStream

                Dim bytes As Int32 = Stream.Read(Data, 0, Data.Length)
    2. 我將由檔案讀出來產生的FileStream物件, 還原成我原來的實體物件時, 程式不會出問題

    可是當我嘗試使用NetworkStream來還原成實體物件時, 卻一直發生SerializationException "在完成剖析之前已達資料流末端。"這樣的例外狀況, 請問這個例外狀況我該怎麼解決呢??, 底下付上我將物件序列化及反序列化的二個方法

     

    <Serializable()> Public Class ReplicationCommand

        '將收回來的NetStream物件或是FileStream物件做反序列化

        Public Shared Function DeSerializeInstance(ByVal stream As Stream) As List(Of ReplicationCommand)
            Dim formatter As BinaryFormatter = New BinaryFormatter

            DeSerializeInstance = CType(formatter.Deserialize(stream), List(Of ReplicationCommand))
            stream.Close()
        End Function

       

        '將物件集合序列化後傳回byte陣列

        Public Shared Function GetByte(ByVal Commands As List(Of ReplicationCommand)) As [Byte]()
            Dim Formatter As Formatters.Binary.BinaryFormatter = New Formatters.Binary.BinaryFormatter
            Dim Stream As New FileStream("C:\" & Guid.NewGuid.ToString & ".Dat", FileMode.Create)
            Formatter.Serialize(Stream, Commands)
            Stream.Close()
            Dim R As Byte() = File.ReadAllBytes(Stream.Name)
            'File.Delete(Stream.Name)
            Return R
        End Function

     

    3. 最後一個問題, 就是當SERVER和一個Client正在進行傳送時, 另一個Client連進來也嘗試要傳送接收資料

    請問這個Client會收到例外狀況嗎?

    2008年11月10日 上午 07:47

解答

  • 2. 開個 MemoryStream 來讀 NetworkStream 的內容,等到東西讀完後再用 MemoryStream 還原你的物件。NetworkStream 裡面是緩衝區的東西,一方面你所有東西沒辦法一次收到,一方面 NetworkStream 裡面的東西是讀了就沒有,不能用來反序列化。

     

    3. 看你 Server 怎樣寫。一般網路程式是允許多線連入,若是你這樣寫,就可以再連一個。

     

    2008年11月10日 下午 04:50

所有回覆

  •  

    目前問題1 已解決, 煩請各位大大指教第2及第3個問題, 感謝
    2008年11月10日 上午 08:11
  • 2. 開個 MemoryStream 來讀 NetworkStream 的內容,等到東西讀完後再用 MemoryStream 還原你的物件。NetworkStream 裡面是緩衝區的東西,一方面你所有東西沒辦法一次收到,一方面 NetworkStream 裡面的東西是讀了就沒有,不能用來反序列化。

     

    3. 看你 Server 怎樣寫。一般網路程式是允許多線連入,若是你這樣寫,就可以再連一個。

     

    2008年11月10日 下午 04:50
  •  

    先感謝您的回答, 在您第三個問題的回答中, 我還是有一點疑問, 希望您能給小弟一些指教

    如果:

    Client A 連入Server, 並且開始上傳資料, 秏時約需3分鐘, 在a連入的1分鐘後, Client B連入了, 此時Server 會Accept這個Client嗎??

     

    我server上的程式碼如下:

    Protected Overrides Sub OnStart(ByVal args() As String)
            ep = New IPEndPoint(IPAddress.Parse(My.Settings.BindingUri), My.Settings.BindPort)
            Server = New TcpListener(ep)
            Server.Start()
                    ContinueListen = True
            While ContinueListen
                Try
                    Dim Client As TcpClient = Server.BeginAcceptTcpClient(New AsyncCallback( _

                                                          AddressOf BeginAcceptClientCallBack), Server)
                 

                Catch ex As Exception

                      Me.Eventlog.WriteEntry("ERROR!!")

                End Try

              End While

    End Sub

     


        Public Sub BeginAcceptClientCallBack(ByVal ar As IAsyncResult)
            Dim Server As TcpListener = CType(ar.AsyncState, TcpListener)
            Try
                Dim Client As TcpClient = Server.EndAcceptTcpClient(ar)
                Me.EventLog.WriteEntry("Client Connected At" + Now.ToString + vbCrLf)
                Dim Data() As Byte = New Byte(Client.ReceiveBufferSize) {}
                Dim Stream As Stream = Client.GetStream
                Stream.Read(Data, 0, Data.Length - 1)
                System.IO.File.WriteAllBytes(Application.StartupPath & "\Temp.Dat", Data)
                'Dim FS As Stream = File.Open(Application.StartupPath & "\Temp.Dat", FileMode.Open)
                Dim L As List(Of ReplicationCommand.ReplicationCommand) = ReplicationCommand.ReplicationCommand.DeSerializeInstance(Stream)
            Catch ex As Exception
                Me.EventLog.WriteEntry("Download Fail !")
            End Try
        End Sub

    我目前將程式修改成使用非同步執行下載, 可是改成這樣後, server變的沒有閒置時間了, 一直不斷的在acceptclient

     

    好像也不太對, 而且打開事件檢視器來看, 裡面全都是""Download Fail !"的記錄, 可見每個被BeginAcceptClient的執行

    都會跑去BeginAcceptClientCallBack這個委派裡, 就算根本沒用戶端連入, 也會呼叫這個委派

    可以提示小弟如何修改這部份的程式嗎??目前還不是很完全了解非同步作業的執行流程

    2008年11月11日 下午 12:42
  • 詹姆士大哥你好,

    小弟是C#的新手,

    關於詹姆士大哥遇到的第一點問題,

    我也有相同的疑惑,

    目前也在這點上遭遇瓶頸,

     

    由於不知道傳送端傳送過來的檔案大小,

    所以宣告陣列都大小也無法確定,

     

    不知道詹姆士大哥是否方便給小弟一個方向或訊息,

    讓小弟能夠有機會學習?

     

    有勞了,

    謝謝各位!

    2008年12月3日 上午 09:02
  • server是一個已經Start的TcpListener

    Dim Client As TcpClient = Server.AcceptTcpClient
    Dim Stream As NetworkStream = Client.GetStream

    '在server上預先建立的buffer, 在建立前先問客戶端要傳多大過來, 依客戶端的大小來建立本地端的buffer

    Dim Data() As Byte = New Byte(Client.ReceiveBufferSize) {}

    Stream.Read(Data, 0, Data.Length - 1)

     

    我是用vb寫的, 沒辦法幫您轉成C#

     

    我改成這樣之後, 就沒再遇到Buffer不足的情況了

    這做法要考量一下客戶端傳來的東西有多大的問題

    萬一太大, 我在想會不會引響到SERVER的效能,

    如果你要傳的是一般檔案, 我想這方法可能不太適合

    2008年12月3日 上午 09:15
  • 多謝詹姆士大哥的回應,

    小弟真是十分感謝!

     

    受益良多,

    謝謝 !

     

     

     

     

    2008年12月3日 上午 09:23
  • 我發現我原來的做法並不適合, 因為Client.ReciveBufferSize永遠只會設定為8192, 傳送的資料如果超過的話,

     

    一樣會遺落資料, 剛又再看了一遍論譠上的各篇文章, 有一張貼璉璉大有提到

     

    「傳不用分次傳, 接收一定要分次接收」, 我仔細想了一下, 問題不是出在buffer, 而是要

     

    好好利用Stream.write這個方法, 以及NetWorkStream.DataAvailable屬性, 把程式修改成

     

    類似下列(msdn找來的)的迴圈, 把緩衝區裡的資料不斷寫入MemoryStream裡(或是FileStream)

     

    Do           
               numberOfBytesRead = myNetworkStream.Read(Buffer, 0, Buffer.Length)
               MemoryStream.Write(Buffer, 0, numberOfBytesRead)

     Loop While myNetworkStream.DataAvailable

     

    此時的Buffer陣列大小, 就不用宣告的太大, 可以使用256或8192均可, 

     

    概念要轉換成真正的暫存區是在MemoryStream裡, 而不是在Buffer裡

    2008年12月4日 上午 03:48
  • 謝謝 詹姆士 王 大哥的提醒,

     

    小弟目前處理也是用類似的方式解決,

    小弟是判斷NetworkStream.Read不等於零時,

    便將NetworkStream.Read讀到的資料寫入Filestream裡,

     

    詹姆士 王 大哥提到的方式是相同的,

     

    至於Buffer的大小,

    以預設的8192來跑沒有發現問題,

    實際情況還要多測試才知道,

     

    再次謝謝詹姆士 王 大哥不吝指點!

    謝謝!

    2008年12月4日 上午 06:27