Tcp应用实例---2.从服务端获取文件列表

本文介绍了一种TCP客户端和服务端之间的交互设计方法,包括命令格式定义、枚举类型创建及命令字符串与字节数组间的转换。此外,还详细讲解了客户端请求文件列表的过程和服务端响应的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

(源码下载地址,http://download.youkuaiyun.com/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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值