Tcp應用實例---2.從服務端獲取文件列表

(源碼下載地址,http://download.csdn.net/source/406996

  在這個實例中,客房端和服務端的數據交換,有許多類型,比如請求文件列表的、請求文件大小的。命令的傳輸,實際上是將字符串以流的形式寫入NetWorkStream;而命令字符串的組成,類似於這樣的格式:
  001|參數1|參數2|參數3
  命令字符串以代表命令類型的代碼開始,後面加上所需要的參數,中間以“|”分隔。

  爲了方便程序的可讀性,需要定義一個枚舉,來記錄命令的類型。打開vs2005,新建一個類庫項目TConst,再將解決方案重命名爲TcpTest。在TConst類庫項目中添加一個枚舉CommandStyleEnum:

    public enum CommandStyleEnum
    
{
        cNone 
= 0,
        cList 
= 1,//請示文件列表
        cListReturn = 2,//文件列表返回
        cGetFileLength = 3,//請示文件長度
        cGetFileLengthReturn = 4,//返回文件長度
        cGetFileLengthReturnNone = 5,//返回文件長度失敗
        cGetFile = 6,//請求文件
        cGetFileReturn = 7,//請求文件返回
        cGetFileReturnNone = 8,//請示文件返回失敗
    }

 

  再添加一個類TCommand,這個類用於命令字符串、字節數組的相互轉換(字節數組在NetWorkStream.Write中用到):

    public class TCommand
    
{
        
private CommandStyleEnum _commandStyle;
        
//命令類型
        public CommandStyleEnum commandStyle
        
{
            
get{return _commandStyle;}
        }


        
private ArrayList _argList;
        
public ArrayList argList
        
{
            
get{return _argList;}
        }


        
public void AppendArg(string arg)
        
{
            _argList.Add(arg);
        }


        
public TCommand(CommandStyleEnum style)
        
{
            _commandStyle 
= style;
            _argList 
= new ArrayList();
        }


        
public TCommand(byte[] bytesCommand, int len)
        
{
            
//
        }


        
public byte[] ToBytes()
        
{
            
//
        }

    }


 

  類TCommand有兩種使用方法:
  1。通過構造TCommand(CommandStyleEnum style)來生成對象,再通過AppendArg(string arg)來附加參數,最後通過 ToBytes()轉換成字節數組。
  2。通過構造TCommand(byte[] bytesCommand, int len)來生成對象,再通過屬性commandStyle、argList來得到命令類型、參數。
  具體的實現請看項目源代碼。

 

  下面開始客戶端的設計。
  首先要新增一個Windows窗體應用程序,命名爲Client。
  Client引用了一個dll文件:SysConfig.dll。這個dll文件提供了系統配置服務,有興趣的可以在我的另一組文章,MyLog3開發日誌裏看到實現方法。
  
  首先,在窗口的Load事件裏,我們從系統配置文件SysConfig.ini中獲取服務器地址和端口:

        private void Form1_Load(object sender, EventArgs e)
        
{
            _sysConfig 
= new TSysConfig();
            _port 
= _sysConfig.GetIniInt("ServerPort"9999);
            _server 
= _sysConfig.GetIniString("ServerAddress""ie");
        }

 

  然後,在“獲取文件列表”按鈕的Click事件中,增加如下的代碼:

        private void button1_Click(object sender, EventArgs e)
        
{
            
try
            
{
                TcpClient tcp 
= new TcpClient();
                tcp.Connect(_server, _port);

                TclientConnection con 
= new TclientConnection(tcp, listBox1);
                Thread t 
= new Thread(new ThreadStart(con.GetFileList));
                t.IsBackground 
= true;
                t.Start();
            }

            
catch (Exception ex)
            
{
                MessageBox.Show(ex.Message);
            }

        }


 

  首先生成一個TcpClient對象並連接到服務端,再啓動一個線程,線程執行的是類TclientConnection中的函數GetFileList(),請看TclientConnection的構造:

        public TclientConnection(TcpClient tcp,ListBox listBox)
        
{
            _tcp 
= tcp;
            _listBox 
= listBox;
        }

 

  再看GetFileList()的實現:  

        private delegate void dd();
        
public void GetFileList()
        
{
            
if (_tcp == null)
                
return;
            
if (_listBox == null)
                
return;

            NetworkStream stream 
= _tcp.GetStream();
            
try
            
{
                TCommand command 
= new TCommand(CommandStyleEnum.cList);
                
byte[] data = command.ToBytes();
                stream.Write(data, 
0, data.Length);

                
byte[] recData = new byte[9999];
                
int recLen = stream.Read(recData, 0, recData.Length);

                
if (recLen == 0)
                    
return;

                command 
= new TCommand(recData, recLen);
                
if (command.commandStyle != CommandStyleEnum.cListReturn)
                    
return;

                dd a 
= delegate()
                
{
                    _listBox.Items.Clear();
                    
for (int i = 0; i < command.argList.Count; i++)
                    
{
                        _listBox.Items.Add((
string)command.argList[i]);
                    }

                }
;
                _listBox.Invoke(a);
            }

            
finally
            
{
                stream.Close();
                _tcp.Close();
            }

        }


  函數首先將請求文件列表的命令發送到服務端,然後接收返回數據,再根據返回的數據生成TCommand對象,返回的文件名列表就存儲在TCommand.argList中。(這裏假定服務端收到文件列表命令,就發回一個字符串,這個字符串是這樣的組成形式:命令代碼|文件名1|文件名2|文件名3。。。,而且轉換成Unicode後長度不超過9999,即原始長度不超過9999/2。)

  下面看服務端的設計。
  首先新建一個Windows窗體應用程序,命名爲Server。
  在窗口的Load事件中,讀取端口號,並啓動監聽線程:

        private void Form1_Load(object sender, EventArgs e)
        
{
            _sysConfig 
= new TSysConfig();
            _port 
= _sysConfig.GetIniInt("Port"9999);

            Thread t 
= new Thread(new ThreadStart(WaitForConnect));
            t.IsBackground 
= true;
            t.Start();
        }


  
  監聽線程實際上執行的是類函數WaitForConnect中的代碼:

        private void WaitForConnect()
        
{
            TListener lis 
= new TListener(_port);
            lis.StartListening();
        }

 

  WaitForConnect生成了類TListener的對象,並調用TListener.StartListening()。(在這裏,函數WaitForConnect是不必要的,線程的構造參數中直接傳遞TListener.StartListening即可。)
  看TListener.StartListening()的代碼:

        public void StartListening()
        
{
            
try
            
{
                TcpListener tcpl 
= new TcpListener(IPAddress.Any, _port);//新建一個TcpListener對象
                tcpl.Start();

                
while (true)//開始監聽
                {
                    TcpClient tcp 
= tcpl.AcceptTcpClient();
                    TserverConnection con 
= new TserverConnection(tcp);
                    Thread t 
= new Thread(new ThreadStart(con.WaitForSendData));
                    t.IsBackground 
= true;
                    t.Start();
                }

            }

            
catch (Exception ex)
            
{
                System.Windows.Forms.MessageBox.Show(ex.Message);
            }

        }


 

  它建立了一個TcpListener對象並開始監聽,每當建立連接時,便將連接寫入到類TserverConnection的對象,並啓動新的線程,以執行類TserverConnection中的函數WaitForSendData()。
  下面看TserverConnection.WaitForSendData()的代碼:

        public void WaitForSendData()
        
{
            NetworkStream stream 
= _tcp.GetStream();

            
try
            
{
                
while (true)
                
{
                    
try
                    
{
                        
byte[] data = new byte[1024];
                        
int recLen = stream.Read(data, 01024);
                        
if (recLen == 0)
                            
break;

                        TCommand command 
= new TCommand(data, recLen);
                        ExtractRecStr(command, stream);
                    }

                    
catch (Exception)
                    
{
                        
//MessageBox.Show(ex.Message);
                        break;
                    }

                }

            }

            
finally
            
{
                stream.Close();
                _tcp.Close();
            }

        }


 

  它不斷地從NetWorStream中讀取客戶端發送過來的消息並放入字節數組data中(這裏假定客戶端發送過來的消息長度小於1024),然後以data爲參數生成類TCommand的對象,最後調用ExtractRecStr(command, stream)函數:

        private void ExtractRecStr(TCommand command, NetworkStream stream)
        
{
            
switch (command.commandStyle)
            
{
                
case CommandStyleEnum.cList:
                    OnGetFileList(stream);
                    
break;
                
case CommandStyleEnum.cGetFileLength:
                    
//OnGetFileLength(list, stream);
                    break;
                
case CommandStyleEnum.cGetFile:
                    
//OnGetFile(list, stream);
                    break;
                
default:
                    
break;
            }

        }

 

  ExtractRecStr函數根據命令的類型,將控制權轉交到相應的控制函數中,這是是用於發送文件列表的函數OnGetFileList:

        private void OnGetFileList(NetworkStream stream)
        
{
            TCommand command 
= GetData.GetFileListCommand();
            
byte[] data = command.ToBytes();
            stream.Write(data, 
0, data.Length);
        }

 

  
  OnGetFileList通過類GetData中的GetFileListCommand返回了一個TCommand對象,並從它得到字節數組,寫入NetWorkStream。
  GetData類在項目TConst中,它包含一些用於返回數據的函數,看GetFileListCommand函數的代碼:

        private static string[] GetFileList(string FilePath)
        
{
            
string[] files = System.IO.Directory.GetFiles(FilePath);

            
for (int i = 0; i < files.Length; i++)
            
{
                files[i] 
= System.IO.Path.GetFileName(files[i]);
            }


            
return files;
        }

        
public static TCommand GetFileListCommand()
        
{
            TSysConfig sysConfig 
= new TSysConfig();
            
string path = sysConfig.GetIniString("path""-1");
            
if (path == "-1")
            
{
                MessageBox.Show(
"沒有從系統配置文件中找到目錄");
                
return null;
            }


            
string[] files = GetFileList(path);
            TCommand command 
= new TCommand(CommandStyleEnum.cListReturn);

            
foreach (string s in files)
                command.AppendArg(s);

            
return command;
        }


  

  GetFileListCommand函數以CommandStyleEnum.cListReturn爲參數生成一個TCommand對象,然後將某個目錄裏的文件名一個個附加到TCommand對象的參數中。(目錄的路徑存放在SysConfig.ini中。)

  
  最終的流程圖像下面這樣:
  


    ie.2008-04-09

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章