none
TCP 同時傳送的問題 RRS feed

  • 問題

  • 最近在做傳送單一個檔案到多點WinCE 設備上的程式碼,遇到些狀況想跟各位先進請教一下!

    想到同時廣播/傳送一個檔案,小弟本想用UDP 協定去發送的。

    但實做下來,檔案超過10MB以上很容易Loss 某些資料...

    加上資料驗證發覺如果client 端台數較多時,也常常要停下來補發給Loss 資料的那幾台,

    整個過程下來,小弟想說乾脆採用TCP下去作業好了...

     

    目前的作法是,透過一個 for 迴圈,裡面建立TCP傳送檔案的執行緒下去運作。

    但是遇到問題... 假設傳給192.168.1.100 與 192.168.1.101,只有最後那台會動作。

    程式碼概略如下:

     

     private void btnSelFile_Click(object sender, EventArgs e)
     {

      FileDialog fDg = new OpenFileDialog();
         if (fDg.ShowDialog() == DialogResult.OK)
            {

                 FileStream reader = new FileStream(fDg.FileName, FileMode.Open, FileAccess.Read, FileShare.None);
                 byte[] FileByte = new byte[reader.Length];
                 reader.Read(FileByte, 0, (int)reader.Length);  // 將傳送檔案讀入一個 Byte序列中,將此Byte[] 傳送到各Client

                 foreach (string s_ip in textBox_IP.Text.Split('&'))  // 假設輸入的IP為 "192.168.1.100&192.168.1.101"
                    {
                        Thread t = new Thread(() => TCPSend(s_ip, fDg.FileName, FileByte));
                        t.IsBackground = true;
                        t.Start();
                        Thread.Sleep(5000);
                    }

             }

     }

     

    private void TCPSend(string IP, string path, byte[] FByte)
     {
          int subPort = 10000 + int.Parse(IP.Split('.')[3]);  // 發送 port 為 (10000 + IP 最末碼)
          TcpClient MyTcpClient = new TcpClient(new IPEndPoint(IPAddress.Any, 0));
                   
          IAsyncResult MyResult = MyTcpClient.BeginConnect(s_ip, subPort, null, null);
                   
          DisplayMsg1(label3, string.Format("正在傳送... {0}", s_ip));

          MyResult.AsyncWaitHandle.WaitOne(3000, true);  //只等三秒逾時
                   
          if (MyResult.IsCompleted)
          {

            透過 while 迴圈發送資料

          }

          MyTcpClient.Close();
          MyTcpClient = null;

    }

     

    不知道是小弟觀念錯誤,本以為這樣的方式處理應該會在按下傳送鍵後,

    發送給192.168.1.100 端資料,隔五秒後,在發送給第一台的同時,透過另一個執行緒再發送給192.168.1.101

    但實際執行後,畫面會 hang住約5~6後,然後只在第二台設備(192.168.1.101) 上看到接收的動作...

     

    請問以上處理過程該如何改進? 或是可以建議小弟,同時傳送檔案給數台設備,有什麼更合適的傳送法?

    • 已編輯 kireanita 2010年11月29日 上午 10:09
    2010年11月25日 下午 05:28

所有回覆

  • 有考慮用Multicast來傳以提升效率嗎?IP Multicasting in C#
    2010年11月26日 上午 01:15
  • 乍看應該不會有問題, 建議你在Listener端可以先放一個Label, 當此Listener Accepct時在Label上顯示出已被Connect的字樣.

    先確認是不是每個Listener都有被Connect上來


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。
    2010年11月26日 上午 03:43
    版主
  • This 大,

    我有試過BroadCast 的方式在192.168.1.255上廣播發送檔案,

    但如發文提到的問題,UDP 不時會發生檔案遺漏的情況...

    我應用的範為大概檔案最大不會超過40MB,CE那邊的接收速度,最快也只能有400Kb/s,

    所以才想說透過TCP 一次傳輸10台比較可以省掉驗證封包的部分...

    CodeProject 這篇我有看過,但傳檔時也是透過UDP方式...

    2010年11月26日 上午 04:52
  • Bill 大你好,

    用您提的方法測試,TCP是有連線。

    另外我在電腦上用Sniff 監看送出去的資料,192.168.1.100與192.168.1.101均有連線與傳送出資料...

    看起來應該是都沒問題才是!

     

    這樣的情況是否是Device 端比較有可能有問題?

    但兩台Device都是跑一樣的軟體...

    不曉得問題是出在哪呢?

    2010年11月29日 上午 09:39
  • (1)目前可以推估的是畫面停五秒是沒錯的, 因為你有在UI執行緒上Sleep.

    (2)我有一個小小的疑問

    為什麼你的 private void TCPSend(string IP, string path, byte[] FByte) 參數名稱是IP

    但方法內容中用的卻是s_ip  , 這樣不會有編譯器警告嗎 ?

    (3)先把兩台Device的IP對調測看看, 如果依然是那台有問題, 那應該就不是程式的問題.

     

     


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。
    2010年11月29日 上午 10:05
    版主
  • 抱歉,第二點的部分是我筆誤,實際上做法確實是利用傳入的IP來當作port!!

    第三點的部分,我試過輸入192.168.1.100&192.168.1.101,則只有101這台接收資料,

    但反過來打192.168.1.101&192.168.1.100,則是只有100這台接收資料...

    換句話說,只有最後的那台設備有收到資料...

     

    等等打算往硬體的部分處理看看,

    先把電腦跟兩台Device 都接在Switch 上,

    目前是PC接路由器,路由器1port接Switch... 設備是接在Switch上

    雖然覺得應該是沒影響... 不過看來也只能先一部分一部分排除了!

    測試過後在上來報告!

     

    感謝Bill 大的建議!

    • 已編輯 kireanita 2010年11月29日 下午 12:03
    2010年11月29日 上午 10:10
  • 小弟在猜測是否會是Device 上把主機連線佔用住了?

    但使用的port 不同,應該不太可能吧!?

     

    另外補上Device 端上的接收code, 呼叫方法為透過執行緒 Thread t = new Thread(() => TCP_Receive(g_path));

     

    private void TCP_Receive(string path)  //path 為接收檔案存放資料夾路徑
    {
       string filepath = "";

       Socket TCPsock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

       IPEndPoint ip = new IPEndPoint(IPAddress.Any, port);
       TCPsock.Bind(ip);
       TCPsock.Listen(1);

       while (alive && !downloading)  // downloading 用於接收完mp3後播放完畢後復歸 false (播放中不接收檔案)
       {
         Socket client = TCPsock.Accept();
         //MessageBox.Show(TCPsock.Connected.ToString());  //測試是否與主機連線上
         byte[] buffer = new byte[g_BufferSize];  // g_BufferSize = 8192
         client.Receive(buffer);

         long receive = 0L, length = BitConverter.ToInt64(buffer, 0);  //檔案長度

         try
         {
           string fileName = Encoding.Default.GetString(buffer, 0, client.Receive(buffer));
                       
           Thread.Sleep(50);

           using (FileStream writer = new FileStream(Path.Combine(path, fileName), FileMode.Create, FileAccess.Write, FileShare.None))
           {
             int received;
             while (receive < length)
             {

               received = client.Receive(buffer);

               // 將檔案寫入writer

               // 透過委派將接收進度顯示於畫面上

             }

           }

         }

         catch{ //處理接收失敗的處理, ex:檔名格式錯誤 }

      }

    }

    2010年11月29日 上午 10:24
  • 我想到有個地方測一下看看

    if (MyResult.IsCompleted)
          {

           /// 在While迴圈之前用個MessageBox.Show顯示是否每個傳進來的IP都有進到這邊;

    透過 while 迴圈發送資料

          }

    另外一點是, 在這程式中是否有共用到某些全域的變數讓兩個Thread產生了交互影響 ?

    有其它的建議是, 讓程式單純點, 用多緒+同步Socket的方法寫.

     


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。
    2010年11月29日 下午 03:45
    版主
  • Bill 大,

    我將TCP Send 的部分改成同步的方式,

    並在程式中拿掉在UI上顯示進度的程式碼,

    並觀看在Device 端上,顯示接收的位元進度,

    一開始有正確接收到檔案長度與檔案名稱,

    但在開始傳輸資料時,卻有股令人感覺好像TCP一次只能傳送一台設備的感覺,

    當192.168.1.100這台開始接收資料,數Kb之後,在192.168.1.101這台設備上看到接收進度在跑了,

    但192.168.1.100這台設備,畫面上的接受進度卻停止了。

    等待192.168.1.101這台接收數Kb之後,又停了下來,此時換192.168.1.100這台設備接收進度又繼續跑了...

    如此反覆直到兩台設備都接收完畢...

     

    這個問題我以前也遇過,一開始這個程式傳送檔案的方式本想採用傳送UDP廣播指令(一個http url),

    讓收到指令的設備利用 HttpWebRequest 到主機端下載檔案,

    但也是遇到這樣的問題,當第二台開始下載時,第一台變停了下來,過幾秒後又換第二台停下來,第一台繼續進度...

     

    不同的方式卻都會產生同樣的問題... 我有懷疑過是否會是PC或是網路交換設備的問題?

    但昨日接在純Switch 功能的交換器上,一樣有這個問題...

    2010年11月30日 上午 10:09
  • 這應該是多執行緒在切換吧, 因為多執行緒本來就是這個做一做, 然後輪下一個做一做, 一直輪來輪去直到做完為止.它的確不是真的在並行, 不過說實話, 我還是第一次聽到有人可以這麼明顯的觀察出這種切換, 是否你在傳資料的程序中有特別長的Sleep或是Wait Handle之類的 ?
    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。
    2010年11月30日 上午 10:17
    版主
  • Bill大,

    以下是我改過的code (同步Socket傳輸)

    private void TCPSend3(string IP, int port, string path, byte[] FByte)
            {
                int subPort = 10000 + int.Parse(IP.Split('.')[3]);
                Socket TCPsock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

                TCPsock.Connect(new IPEndPoint(IPAddress.Parse(IP), subPort));

                byte[] bLength = BitConverter.GetBytes((long)(FByte.Length));
                TCPsock.Send(bLength, 0, bLength.Length, SocketFlags.None);  // 傳送檔案長度
                Thread.Sleep(100);

                byte[] bName = Encoding.Default.GetBytes(Path.GetFileName(path));
                TCPsock.Send(bName, 0, bName.Length, SocketFlags.None);  // 傳送檔案名稱
                Thread.Sleep(200);

                int currentValue = 0, sending = 0;

                while (currentValue < FByte.Length)
                {
                    sending = (currentValue + BufferSize >= FByte.Length) ? (FByte.Length % BufferSize) : BufferSize; // BufferSize = 8192, 一次傳輸8K, 最後一次為傳輸不滿8k
                    TCPsock.Send(FByte, currentValue, sending, SocketFlags.None);

                    currentValue += sending;
                    Thread.Sleep(5); // 是否該在While 迴圈內加入此Sleep, 停下來讓其他執行緒傳送檔案至其他台設備
                }

                TCPsock.Close();
                TCPsock = null;
            }

     

    這樣應該沒有過長的Sleep吧!

    想請教的是,透過密集切換的執行緒傳輸多台設備,是否傳輸時間大略約為傳輸一台的時間在多一些,當然應該是要小於兩倍的時間。

    但小弟不解的是(或是太鑽牛角尖...) 若是類似微分的觀念,將時間切成無限小段,在每個interval 之間,CPU只能做一件事(傳輸一台)

    這樣把傳輸兩台的時間加總起來,應該還是該等於一次傳輸一台,完畢後再傳給下一台的時間...吧!?

    以上是小弟依數學的觀念來看,如鬧笑話請糾正小弟!!

    2010年11月30日 下午 05:02
  • 其實我也很迷惑, 照理說應該不會這麼明顯才是. 可是現在的網路設備應該都不錯了, 卡在Switch端的可能性應該也不大. 看來這問題我們要花點時間來慢慢檢視.

    先前提到你有Sniffer, 可否觀察一下重送封包的狀態是否嚴重 ? 當PC送資料給Device時, ACK回應的時間差又是如何 ?


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。
    2010年11月30日 下午 05:13
    版主
  • ... 快投降了!

    小弟覺得應該是WinCE 上面的問題了!

    原因是這樣的:

    我將在Device上的程式碼,放在PC上跑(直接雙擊執行,非使用模擬器)

    確實可以同時傳遞給四台PC ...

    這樣應該是網路設備與程式碼都是沒問題的。

     

    不知道這樣的資訊之下,Bill大或其他先進是否還有其他建議或看法呢?

    2010年12月1日 上午 07:23
  • 看起來狀況的範圍有縮小了, 我們先把焦點放在WinCE上好了.

    (1) 這個設備是你們自己客製的裝置還是標準的PDA ?

    (2) 他是透過無線還是有線的方式傳輸 ?

    (3) 我曾經遇過一個狀況是設備出問題, 導致設備一直回傳NACK, 然後那一段封包就來來回回重送個沒完. 這可能也需要用Sniffer去看一下是否有這樣的情況.

     


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。
    2010年12月1日 上午 07:31
    版主
  • 應該算是客製的裝置,網路上在賣的WinCE 開發粄, mini2440, OS版本是 WinCE6.0 R3

    傳輸方式是使用有線的方式傳輸。

    Bill大提到的第三點我會在監看一下封包往返的情況後再上來報告!

    如這篇該轉至Windows Embedded專區,在有勞一下版大! 謝謝!

    2010年12月1日 上午 07:54
  • 看起來如果要比對的工程不小, 如果可能的話, 搞兩台PDA或其它的WinCE Device來交叉測試看看.

    因為mini2440我也沒用過, 無法確認是否會有硬體本身的問題


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。
    2010年12月1日 上午 08:06
    版主