不要意思,篇幅有点长,希望大家耐心地看。大家好,我的情况是这样的:局域网内,经过路由器,3个客户端Socket连接服务器(其中一台作为服务器),发一个请求(通过ComboBox更换请求的类型),服务器根据请求,死循环的发送相应的结果到客户端,频率是500ms / 次。工作模式:说明:
1.服务器启动。
2.服务器启动8个线程(由主线程启动,分别不断地从数据库读取数据)。
3.开始监听,并开辟一个线程等待客户端连接
4.客户端连接上之后,服务器开一个线程接受他的命令
5.服务器根据请求,取8个线程中的一个所产生的结果,再开一个线程,用作死循环返回给客户。如果客户第一次请求“水果”,然后服务器死循环发送“苹果,蕉”,然后客户第二次请求“海鲜”,服务器先销毁之前的线程,再取得“海鲜”的数据死循环发送“虾,蟹”。发送的频率是500ms / 次
疑问:
1.服务器的设计,合理吗?
2.象这样开启多级的线程,会出什么问题吗?除了那8个线程是由主线程开出来之外,其他线程都是有副线程开出来的,会不会有什么问题?
3.客户多的时候,这样设计还能行吗?
出现的问题:
1.当客户端请求“海鲜”,接收到的是“虾、蟹”,当他切换到“水果”,接收到的还是“虾、蟹”,要经过几秒甚至十几秒才能收到“苹果、蕉”,这个“延时”时快时慢。以下是代码//8个线程:
/// <summary>
/// 启动服务器
public MyServer StartupServer(string serverIP, string serverPort)
{
     myServer = MyServer.getInstance(serverIP, Convert.ToInt32(serverPort));     InitDataTable(ref initedDT, listCompanys);     myServer.Listen();     // 第一个线程
     Thread thGetSeaFood = new Thread(new ThreadStart(GetSeaFood));
     thGetSeaFood .IsBackground = true;
     thGetSeaFood .Start();     // 依次类推     return myServer;
}private void GetSeaFood()
{                 
     List<DataTable> ldt = null;     DataTable dt2 = null;     DataTable dt = null;     while (true)
     {
         ldt = new List<DataTable>();         dt2 = initedDT.Clone();         dt = // 读数据库         dtResult = dt2;         Thread.Sleep(500);     }
}//服务器监听:
/// 开始监听,并开一个线程,等待客户端连接
public void Listen()
{
    myServer.Listener = new TcpListener(IPAddress.Parse(myServer.ServerIP), myServer.ServerPort);
    myServer.Listener.Start();    BeginWaitingConnection();
}
//等待客户端连接:
/// 开一个线程,等待客户端连接
private void BeginWaitingConnection()
{
    this.thWaitingConnect = new Thread(new ThreadStart(WaitingConnection));
    this.thWaitingConnect.IsBackground = true;
    this.thWaitingConnect.Start();
}/// 等待客户端连接
private void WaitingConnection()
{
     while (true)
     {
          Socket socket = myServer.Listener.AcceptSocket();          if (null != socket)
          {
               MyClient myClient = new MyClient();
               myClient.SClient = socket;
               myClient.ClientIp = Regex.Split(myClient.SClient.RemoteEndPoint.ToString(), ":")[0];               myClient.ThReceiveCmd = new Thread(new ParameterizedThreadStart(ReceiveCmd));
               myClient.ThReceiveCmd.IsBackground = true;
               myClient.ThReceiveCmd.Start(myClient);           }
      }
}
//处理请求:
/// 分析命令
private void AnalyseCmd(MyClient myClient, string cmd)
{
     string[] param = Tool.GetParms(cmd);     // 接收到客户端请求
     switch (param[0])
     {
          case CmdCollection.SM1_CMD_REQUEST_FOR_DATA:
          {
               string userName = param[1];
               string type = param[2];  //类型               if (null != eventPrepareResponseWaterData)
               {
                   // 如果是第二次请求(客户端切换请求类型),就要把之前的死循环返回结果的线程销毁掉
                   DestoryClientThread(userName);                   dicClients[userName].ThSendCmd = new Thread(new ParameterizedThreadStar(PrepareToSendData));
                   dicClients[userName].ThSendCmd.IsBackground = true;
                   dicClients[userName].ThSendCmd.Start(new object[] { myClient, type});
                }                break;
           }
      }
}
/// 销毁客户端线程
private void DestoryClientThread(string userName)
{
     if (dicClients.ContainsKey(userName))
     {
         if (null != dicClients[userName].ThSendCmd)
         {
             dicClients[userName].ThSendCmd.Abort();
             //dicClients[userName].ThSendCmd.Join();
         }
     }
}
//返回结果:
private void PrepareToSendData(object sendParam)
{
     object[] temp = (sendParam as object[]);
     MyClient myClient = temp[0] as MyClient;
     string type = temp[1].ToString();
     DataTable dtResponseData = null;     do
     {           dtResponseData = eventPrepareResponseWaterData(type);   //服务器根据请求,取得结果           SendToClient(myClient, CmdCollection.SM1_CMD_RESPONSE_WATER_TO_CLIENT, dtResponseData, type);           if (Thread.CurrentThread.ThreadState == ThreadState.Background)
           {
               Thread.Sleep(500);
           }           dtResponseData = null;       }
       while (true);
}
private void SendToClient(MyClient myClient, string cmd, DataTable dtWater, string type)
{
     if (dicClients.ContainsKey(myClient.UserName))
     {
          Socket sClient = dicClients[myClient.UserName].SClient;          if (null != dtWater && sClient.Connected && Thread.CurrentThread.ThreadState == ThreadState.Background)
          {
              myClient.Sw = new StreamWriter(new NetworkStream(sClient));
          }          if (null != dtWater && sClient.Connected && Thread.CurrentThread.ThreadState == ThreadState.Background)
          {
              myClient.Sw.WriteLine(String.Format("{0}|{1}|{2}\r\n\t\t\t", cmd, Tool.SerializeAndCompress(dtWater), type));
          }          if (null != dtWater && sClient.Connected && Thread.CurrentThread.ThreadState == ThreadState.Background)
          {
               myClient.Sw.Flush();
          }     }}
疑问:
1.服务器的设计,合理吗?
2.象这样开启多级的线程,会出什么问题吗?除了那8个线程是由主线程开出来之外,其他线程都是有副线程开出来的,会不会有什么问题?
3.客户多的时候,这样设计还能行吗?
出现的问题:
1.当客户端请求“海鲜”,接收到的是“虾、蟹”,当他切换到“水果”,接收到的还是“虾、蟹”,要经过几秒甚至十几秒才能收到“苹果、蕉”,这个“延时”时快时慢。
请大家指教,先谢谢了!

解决方案 »

  1.   

    我感觉一个客户端你开一个线程,用一个TCP链接就可以了啊,当客户请求“海鲜”,你返回结果,当客户继续请求水果,你再返回,当客户发出结束命令,你服务器端就可以关链接,线程了
    当时,这中间的命令 协议你自己定下就可以了!
      

  2.   


    数据量是20K, 压缩有变成5K, 数据量是不大的, 但我是很频繁的, 500ms / 次 . 
    如果客户多, 每个客户都直接取的话, 服务器就做了许多重复的操作, 就很占CPU.
      

  3.   


    我感觉一个客户端你开一个线程,用一个TCP链接就可以了啊可以解释一下这句话吗?
      

  4.   


    我没用TcpClient, 只用Socket.
    有客户连接的时候, 服务器都开2个线程用于接受和发送, 这是不是也达到了"交互"的效果?
      

  5.   


    的确,你的是可以算异步了,不过不是用的BeginXXX方法,你自己新开线程没有使用BeginXXX好,因为你得维护线程的关闭。另外你说的全局变量会相互覆盖的问题,那你就用对象吧,给每个建立的链接分配一个对象,对象内部存放那个判断关键字。我之所以建议你用BeginXXX方法,也是有道理的,你看下MSDN上的示例就会发现,他会传递一个StateObject对象,这个StateObject是自定义的,里面不但可以保存Socket,还可以保持各种信息。我们来看一下BeginSend的函数定义:
    public IAsyncResult BeginSend(
    byte[] buffer,
    int offset,
    int size,
    SocketFlags socketFlags,
    AsyncCallback callback,
    Object state
    )
    这里面最后一个参数用来传递自定义状态对象,这样你就可以把同一个对象传递给BeginSend和BeginReceive函数同时使用了,操作对象中的关键字,一个线程写,一个线程读,这样不是非常完美吗?
      

  6.   

    完全使用异步方式来效率上非常高.同时也不需要new很多对象.但异步方式不好控制.
      

  7.   

    你可以这样 做一个list列表,记录客户信息。比如记录Ip,你好像也获取了客户的Ip。
    当客户A连接后,你可以记录A的Ip在list中和networktream这里面可能记录了很多网络信息。
    发送的时候,只要ip对应上,networkstream就可以用来发送信息,这样就不会乱了
      

  8.   


    我用了dicClients(Dictionary)来存的, 给每个客户端发的信息也没乱.
    现在的问题是切换类型, 好慢!!
      

  9.   

    你都没有给全代码,而且是最关键的代码,所以只能从你设计的合理性入手了。如果你要问延迟,请给出eventPrepareResponseWaterData的实现。
      

  10.   

    eventPrepareResponseWaterData 所订阅的方法是:public DataTable ResponseWaterData(string clientType)
    {
    switch (clientType)
    {
         case "海鲜":
               return dtSeadFood.Copy();
         case "水果":
               return dtFruit.Copy();
         // 依次类推
         default:
               return null;
                    }
    }
    而上面的dtSeadFood, 是下面的方法里得出来的private void GetSeaFood()
    {                 
         List<DataTable> ldt = null;     DataTable dt2 = null; // 这个dt2是表结构     DataTable dt = null;     while (true)
         {
             ldt = new List<DataTable>();         dt2 = initedDT.Clone();         dt = // 读数据库          dtSeadFood= dt2;         Thread.Sleep(500);
         }
    }
      

  11.   

    qldsrx兄,BeginSend是怎样用的?
      

  12.   

    关于BeginSend的用法,MSDN上有很详细的示例代码,运行那个示例就足够学会它的用法了。eventPrepareResponseWaterData 的过程那么简单,可以确定不是它的问题了。你如果要确定是哪个地方导致切换慢了,有个很好的方法。你可以在每个可能会慢的操作前记录下操作名及时间,操作后再记录下操作名及时间,如果是控制台程序,直接输出到控制台即可,如果是窗体程序就用文件记录下,或者在界面放个ListView记录下。
      

  13.   

    哎呀 这个真不知道是我没用BeginSend()的问题, 还是其他问题. 感觉好难测试啊.还有就是, 我说的切换慢.
    如果第一次请求"水果", 服务器返回"香蕉". 然后我切换到"海鲜", 客户端收到的仍然是"香蕉", 要过几秒甚至十几秒才收到"虾".现在还有一种情况是:
    如果客户端挂在那里半小时不动, 他切换就慢. 但这个时候不停的切换, 他就快起来了. 这大概是什么问题???
      

  14.   

    难道是缓存问题?说实在的,你的那个操作里面产生了太多的DataTable了,而你的DataTable都是置之不理的,没有调用Dispose方法释放资源,看看你的内存,不知道用掉了多少。
      

  15.   

    呃...
    问题解决了.是我某个地方用了Thread.Sleep(); 造成阻塞, 并且累计了.总之...谢谢大家!