none
Socket TcpClient 大量連線 運行一段時間會當住 RRS feed

  • 問題

  • WINXPP+VS2008

    TO 各位大大
    最近一直遇到一個問題困擾很久
        Public Client(XX) As TcpClient 
        Public Stream(XX) As NetworkStream
    因為我要同時對很多網路設備連線
    我開了一個TcpClient 的陣列
    同時對這些設備下一命令
    但是遇到一個問題

    常常在程式執行一段時間後
    程式就莫名奇妙當住了

    發現一個地方有問題
    都是在傳送完後等待回應的地方卡住
    ↓↓傳送的方式

    Do
    
                    For Ai = 1 To G_Num
                        If WantRead(Ai) = True And Send_Ok(Ai) = False Then
                            IP_Index = Ai
                            If Client(Ai).Connected = False Then
                                Do '====修正====
                                    RConnect += 1
                                    Call TCPConnect(True, Ai)
    
                                    tim1 = Microsoft.VisualBasic.Timer
                                    Do
                                        tim2 = Microsoft.VisualBasic.Timer
                                        If tim2 < tim1 Then tim2 = tim2 + 64000
                                        Application.DoEvents()
                                    Loop Until Client(Ai).Connected = True Or tim2 - tim1 >= 0.5 Or G_Quit = True
                                    If G_Quit = True Then Exit Sub
                                Loop Until Client(Ai).Connected = True Or RConnect > 3 Or G_Quit = True
                            End If
                            If G_Quit = True Then Exit Sub
                            Call SendData(SendStr(Aj), Ai) '/////送資料
                        End If
                    Next
    
                    SendAll_OK = True
    
                    Delay(0.5) '停一下 就可以收了^^
                    If G_Quit = True Then Exit Sub
                    For Ai = 1 To G_Num
                        If WantRead(Ai) = True Then
                            Call Receive_Materials(Ai)
                            SendAll_OK = SendAll_OK And Send_Ok(Ai)
                        End If
                    Next
                    RetryCount += 1
                    If RetryCount > 0 Then
                        For Ai = 1 To G_Num
                            If Send_Ok(Ai) = False Then
                                Call TCPConnect(True, Ai)
                                WantRead(Ai) = False
                            End If
                        Next
                    End If
                Loop Until SendAll_OK = True Or G_Quit = True Or RetryCount > 0
    ↓↓↓↓接收的方式

                tim1 = Microsoft.VisualBasic.Timer
                Do
                    tim2 = Microsoft.VisualBasic.Timer
                    If tim2 < tim1 Then tim2 = tim2 + 64000
                    Application.DoEvents()
                Loop Until Client(s_Index).Available = ResponseBytes Or tim2 - tim1 >= 3 Or G_Quit = True '新的 delay 秒
    
                If G_Quit = True Then Exit Sub
    
                Dim bytes(Client(s_Index).Available - 1) As Byte
    
                Try
                    Stream(s_Index).Read(bytes, 0, Client(s_Index).Available) '讀取固定的位元組
                Catch ex As Exception
                    If G_Quit = True Then Exit Sub
                    Call TCPConnect(True, s_Index)
                    Exit Sub
                End Try
    可是當我在DEBUG MODE下 卻都沒有這個狀況
    但是我想 會不會是在接收的時候出問題
    所以在Stream(s_Index).Read(bytes, 0, Client(s_Index).Available)
    加上TRY...CATCH
    但是狀況還是一樣

    不知道各位大大
    有啥麼樣的想法幫忙一下小弟







    2010年1月14日 上午 09:12

解答

  • 動不了 大大

    您的問題我也有遇過
    尤其再不熟悉  .Net 2.0 TcpClient.Connect 連線建立所需時間為 .Net 1.1 50 倍
    的狀態下用他Socket會Lock 住有點像是封包衝突卡住  所以偶後來因為自己能力不足就只用  1個 CallBack 去作 等於只用 一個 TCPClient 去作

    後來阿  我都是用傳統的 Thread + Socket 來作

    我自己遇到的問題與您分享之
    1. Socket 會 CLOSE_WAIT  Lock 等待一段時間才釋放  <<< 原因是我自己沒有釋放好 後來處理掉了  
    2. Socket 連結後透過  Bill Chung <abbr class="affil"> 大大文章的指導,一段時間沒有在送命令會自己斷線 所以我自己是像您一樣
    使用 </abbr>

      Catch
    
    
     ex As
    
    
     Exception
    If G_Quit = True Then Exit Sub
    Call TCPConnect(True , s_Index)
    Exit Sub
    End Try
    的模式且將 TCP Client DisConnect 後再作 Nothing 後重新 New TCPClient

    根據以上的小小經驗

    目前連結 16台網路設備(不同IP Port) 尚無問題

    不過如果是 相同 IP Port 佔 13台 另外3台為不同IP port則 小弟會先判斷是否為相同的 IP Port如果是就不中斷連線除非他已經自行斷線了(.Connected = False )
    然後遇到不同的IP就重新斷線重新連結新的 IP與Port


    也是建議您
    >>TO Bill Chung
    >>      使用  BeginRead(),BeginWrite() 嗎?

    如果您連接這麼多台,用非同步的會好一點點喔  我以前也是先用同步滴 也是接 20台以上的設備 作Server/Client 端集中在一隻程式 


    >>我要傳送到每一個設備都要再開啟一個執行緒,假如我有50個設備就要開50個傳送的執行緒和50個接收的執行緒嗎?
    小弟 初學者的作法是 開一個 Thread  然後利用開啟非同步Socket在利用遞迴去送命令,透過 AddressOf EndReadCallback 的方法收回資料與回應命令
    stream.BeginRead(state.Buffer, 0, state.BufferSize, AddressOf EndReadCallback, state)
    然後在該Thread內使用遞迴的方式去針對不同的TCPClient下命令


    以上提供您參考看看。 :)  Max


    PS: 另外一段時間會當住 小弟以前遇到過得經驗是   連結遠端(偶沒有找到TimeOut的設置)連結不到所以凍結住了。  他要等一段時間自己TimOut後才會釋放出來  也可能是您送的命令太快了對方還沒有完全接收完結果命令被截斷後變成後面  A傳B b回應A 的流程跳脫了,所以您加上TimeDelay可能就是讓設備正確回應後在繼續動作,小弟在寫互相通訊的狀態下也是如果中間互相回應有中斷或者發生timeOut 問題就會重新 重發命令或者先等待一段時間沒有回應就確認是否已經斷線如果斷線就重新下命令與重新連線。



    • 已標示為解答 動不了 2010年1月15日 上午 07:59
    2010年1月15日 上午 06:38

所有回覆

  • hi
    確定SendData有全部送完嗎??
    如果封包沒全部送完,後面讀取接收可能就會出現這樣的狀況
    可以嘗試將Delay拉長試看看
    http://www.dotblogs.com.tw/ricochen/Default.aspx
    2010年1月14日 上午 10:12
  • 送出去要等對方處理回應後才收得到東西吧?


    論壇是網友平等互助 保證解答請至 微軟技術支援服務
    提問時,錯誤情境描述與錯誤訊息很重要,情境描述包含你做了什麼,預期的結果與實際發生的結果。一個最爛的問法範例:「我的電腦電腦怎麼不能開機?」誰知道你家是不是沒電還是你根本找不到電源鈕。
    2010年1月14日 上午 10:17
  • 兩個方向
    (1)使用同步Socket, 最好是分執行緒處理 (請適量)
    (2)用非同步Socket
    MSDN 文件庫很重要
    問題本身越具體, 越容易得到大家的回應
    回應幫助你的人是一種禮貌, 良好的禮貌有助於激發大家對你問題回應的熱情
    2010年1月14日 下午 01:17
    版主
  • TO ricoisme 
       SendData因該是有全都送完
    擷取到的封包看起來是送完的。

    TO 心冷熱情熄
    沒錯要等倒對方回應才會收到資料,回應的時間送出到收到大約在300ms以內,超過這個時間通常就是沒回應了

    TO Bill Chung
          使用  BeginRead(),BeginWrite() 嗎?

    有想說加上SendTimeout & ReceiveTimeout 不知道有沒有用


    目前測試兩台電腦
    一台雙網卡:資料到凌晨一點多,就沒有新資料(約三個多小時)。
    一台單網卡:昨天到現在,到目前還沒當掉。

    2010年1月15日 上午 12:46
  • 非同步狀態下,  TimeOut屬性是沒用的.
    你提到雙網卡, 你是如何處理 Socket執行個體建構的 ?
    MSDN 文件庫很重要
    問題本身越具體, 越容易得到大家的回應
    回應幫助你的人是一種禮貌, 良好的禮貌有助於激發大家對你問題回應的熱情
    2010年1月15日 上午 02:48
    版主
  • to Bill Chung 大大
    執行個體建構

       Public Sub Tcp_Thread()
                Client(IP_Index) = New TcpClient(IP,PORT)
       End Sub


     tcpThread(t_Index) = New Thread(New ThreadStart(AddressOf Tcp_Thread))
     tcpThread(t_Index).IsBackground = True 
     tcpThread(t_Index).Start()

    2010年1月15日 上午 03:35
  • to Bill Chung 大大
    執行個體建構

       Public Sub Tcp_Thread()
                Client(IP_Index) = New TcpClient(IP,PORT)
       End Sub


     tcpThread(t_Index) = New Thread(New ThreadStart(AddressOf Tcp_Thread))
     tcpThread(t_Index).IsBackground = True 
     tcpThread(t_Index).Start()

    你這樣的原因是為了多網卡才用陣列的對不對 ?
    其實, 你只要建一個TcpClient執行個體就好. 然後把輪詢的機制處理好
    你參考一下這一篇的Demo Code, 看一下是如何建構TcpClient
    http://www.dotblogs.com.tw/billchung/archive/2009/06/14/8817.aspx


    MSDN 文件庫很重要
    問題本身越具體, 越容易得到大家的回應
    回應幫助你的人是一種禮貌, 良好的禮貌有助於激發大家對你問題回應的熱情
    2010年1月15日 上午 03:41
    版主
  • to Bill Chung 大大
    使用陣列是因為每個設備都有自己的IP & PORT
    Client(IP_Index) = New TcpClient(IP(IP_Index),PORT(IP_Index))'不小心漏打

    所有的設備透過HUB 跟內網的網卡連接
    我個IP & PORT 都是陣列

    原本只使用一個TcpClient但是因為每問完一個設備就要斷線再連線新的設備(RUN 很久 都沒有問題)
    ※只有偶而會出現斷不了線&連不上

    後來改開TcpClient的陣列是希望所有的設備可以再同一時間回應


    2010年1月15日 上午 05:02
  • 你希望做到的功能, 就必須使用多個thread, 然後就要避免資源衝突和鎖定. 這樣你要對每一個設備都有獨立Thread(連傳送和接收的Thread都要分開)
    因為照現在你的Code看起來, 你用多執行緒建立多個TcpClient, 卻又在同一個處理緒中使用迴圈去處理各個TcpClinet, 說實話我是有點看不太懂.

    MSDN 文件庫很重要
    問題本身越具體, 越容易得到大家的回應
    回應幫助你的人是一種禮貌, 良好的禮貌有助於激發大家對你問題回應的熱情
    2010年1月15日 上午 05:22
    版主
  • to Bill Chung大大

    因為我是利用多執行緒建立多個TcpClient

    會這樣做是因為看到璉大的一篇文章
    .Net 2.0 TcpClient.Connect 連線建立所需時間為 .Net 1.1 50 倍
    http://tlcheng.spaces.live.com/blog/cns!145419920BFD55A7!1679.entry

    最原先並沒有這樣做只後來發現在Connect的時候怎麼常常停住很久
    去爬了一些文
    才會想說把連線的部分都丟到thread去處理,避免因為連線的問題讓程式整個卡住。

    其實我並沒有想用到多續的部份

    大大提到(連傳送和接收的Thread都要分開)

    意思是說
    我要傳送到每一個設備都要再開啟一個執行緒,假如我有50個設備就要開50個傳送的執行緒和50個接收的執行緒嗎?






    2010年1月15日 上午 05:57
  • 動不了 大大

    您的問題我也有遇過
    尤其再不熟悉  .Net 2.0 TcpClient.Connect 連線建立所需時間為 .Net 1.1 50 倍
    的狀態下用他Socket會Lock 住有點像是封包衝突卡住  所以偶後來因為自己能力不足就只用  1個 CallBack 去作 等於只用 一個 TCPClient 去作

    後來阿  我都是用傳統的 Thread + Socket 來作

    我自己遇到的問題與您分享之
    1. Socket 會 CLOSE_WAIT  Lock 等待一段時間才釋放  <<< 原因是我自己沒有釋放好 後來處理掉了  
    2. Socket 連結後透過  Bill Chung <abbr class="affil"> 大大文章的指導,一段時間沒有在送命令會自己斷線 所以我自己是像您一樣
    使用 </abbr>

      Catch
    
    
     ex As
    
    
     Exception
    If G_Quit = True Then Exit Sub
    Call TCPConnect(True , s_Index)
    Exit Sub
    End Try
    的模式且將 TCP Client DisConnect 後再作 Nothing 後重新 New TCPClient

    根據以上的小小經驗

    目前連結 16台網路設備(不同IP Port) 尚無問題

    不過如果是 相同 IP Port 佔 13台 另外3台為不同IP port則 小弟會先判斷是否為相同的 IP Port如果是就不中斷連線除非他已經自行斷線了(.Connected = False )
    然後遇到不同的IP就重新斷線重新連結新的 IP與Port


    也是建議您
    >>TO Bill Chung
    >>      使用  BeginRead(),BeginWrite() 嗎?

    如果您連接這麼多台,用非同步的會好一點點喔  我以前也是先用同步滴 也是接 20台以上的設備 作Server/Client 端集中在一隻程式 


    >>我要傳送到每一個設備都要再開啟一個執行緒,假如我有50個設備就要開50個傳送的執行緒和50個接收的執行緒嗎?
    小弟 初學者的作法是 開一個 Thread  然後利用開啟非同步Socket在利用遞迴去送命令,透過 AddressOf EndReadCallback 的方法收回資料與回應命令
    stream.BeginRead(state.Buffer, 0, state.BufferSize, AddressOf EndReadCallback, state)
    然後在該Thread內使用遞迴的方式去針對不同的TCPClient下命令


    以上提供您參考看看。 :)  Max


    PS: 另外一段時間會當住 小弟以前遇到過得經驗是   連結遠端(偶沒有找到TimeOut的設置)連結不到所以凍結住了。  他要等一段時間自己TimOut後才會釋放出來  也可能是您送的命令太快了對方還沒有完全接收完結果命令被截斷後變成後面  A傳B b回應A 的流程跳脫了,所以您加上TimeDelay可能就是讓設備正確回應後在繼續動作,小弟在寫互相通訊的狀態下也是如果中間互相回應有中斷或者發生timeOut 問題就會重新 重發命令或者先等待一段時間沒有回應就確認是否已經斷線如果斷線就重新下命令與重新連線。



    • 已標示為解答 動不了 2010年1月15日 上午 07:59
    2010年1月15日 上午 06:38
  • 舉個例子好了, 假設你要連設備A,B,C .......
    你就先起一個執行緒, 在這執行緒單獨產生連接A的TcpClient執行個體, 也在這執行緒做發送與接收
    接著開第二個執行緒, 在這執行緒單獨產生連接B的TcpClient執行個體, 也在這執行緒做發送與接收
    接著開第三個執行緒, 在這執行緒單獨產生連接C的TcpClient執行個體, 也在這執行緒做發送與接收
    ......但是太多執行緒還是會影響效能
    另外也可用ThreadPool來替代Thread類別來執行這些建立執行緒的工作
    我覺得你應該有用到一大堆全域的成員, 全域成員在多執行緒是非常不容易控制的.
    MSDN 文件庫很重要
    問題本身越具體, 越容易得到大家的回應
    回應幫助你的人是一種禮貌, 良好的禮貌有助於激發大家對你問題回應的熱情
    2010年1月15日 上午 07:25
    版主
  • Bill Chung <abbr class="affil">MVP  SAY


    </abbr>
    另外也可用ThreadPool來替代Thread類別來執行這些建立執行緒的工作

    http://www.wretch.cc/blog/pbnttttt/14173992
    2010年1月15日 上午 07:37
  • 謝謝 Bill Chung & Max197
    我先改寫一下程式在測試看看
    因為設備大約都在100個左右
    看起來是不能開太多執行緒

    目前改寫方式以ThreadPool & 非同步的方式 為目標了

    謝謝幾位大大了
    2010年1月15日 上午 07:58
  • 和你分享下我自己的經驗

    曾經測試每次同時開啟 10 個連接 TcpListener+TcpClient 的應用程式

    並且依照測試的結果 Thread.Sleep(n) 加大 n 的值

    當 n 值很小或者應該考慮 Sleep 而沒有的時候

    連接的成功率不到三成

    當 n 值不小 而且應該 Sleep 都有考慮到的時候

    1. 很低的機率 10 個連接都成功

    2. 可以說幾乎都能達到 5 個以上的連接成功

    3. 但是平均來說都會有 2~3 個的失敗連接

    以上皆為同步狀況下測試的結果

    結論:很明顯地,失敗的比率非常高。改用 Socket 後,當 n 值不小 而且應該 Sleep 都有考慮到的時候 幾乎都是成功...

    2011年4月4日 上午 05:30