亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

WPF+WCF一步一步打造音頻聊天室(三):語(yǔ)音聊天

系統(tǒng) 2443 0
前一篇文章中實(shí)現(xiàn)了文字聊天和共享白板的功能,這篇文章中,我將在前一篇文章的基礎(chǔ)上實(shí)現(xiàn)語(yǔ)音聊天的功能。語(yǔ)音聊天要比文字聊天和共享白板難度要大一點(diǎn)。

實(shí)現(xiàn)的大概的流程為:

1、一個(gè)聊天室成員向另外一個(gè)成員發(fā)起語(yǔ)音聊天請(qǐng)求

2、這個(gè)請(qǐng)求將被送至WCF服務(wù)端,WCF的雙工通知被邀請(qǐng)人。

3、被邀請(qǐng)人接到通知,他可以選擇接受或者拒絕語(yǔ)音聊天的請(qǐng)求。

4、如果拒絕,將通知請(qǐng)求者拒絕語(yǔ)音聊天

5、如果同意,邀請(qǐng)者和被邀請(qǐng)者的客戶端將進(jìn)行語(yǔ)音聊天,此時(shí)客戶端會(huì)開(kāi)啟一個(gè)播放聲音和接受聲音的線程。這里用到了一個(gè)開(kāi)源的wave類庫(kù),在 http://www.lumisoft.ee/lswww/download/downloads/Examples/ 可以下載。聲音的通信使用到了UDPClient 類。這個(gè)類使用 UDP 與網(wǎng)絡(luò)服務(wù)通訊。UDP 的優(yōu)點(diǎn)是簡(jiǎn)單易用,并且能夠同時(shí)向多個(gè)地址廣播消息。UdpClient 類提供了一些簡(jiǎn)單的方法,用于在阻止同步模式下發(fā)送和接收無(wú)連接 UDP 數(shù)據(jù)報(bào)。因?yàn)?UDP 是無(wú)連接傳輸協(xié)議,所以不需要在發(fā)送和接收數(shù)據(jù)前建立遠(yuǎn)程主機(jī)連接。但您可以選擇使用下面兩種方法之一來(lái)建立默認(rèn)遠(yuǎn)程主機(jī):

  • 使用遠(yuǎn)程主機(jī)名和端口號(hào)作為參數(shù)創(chuàng)建 UdpClient 類的實(shí)例。

  • 創(chuàng)建 UdpClient 類的實(shí)例,然后調(diào)用 Connect 方法。

可以使用在UdpClient 中提供的任何一種發(fā)送方法將數(shù)據(jù)發(fā)送到遠(yuǎn)程設(shè)備。使用 Receive 方法可以從遠(yuǎn)程主機(jī)接收數(shù)據(jù)。

這篇文章使用了 Receive 方法從客戶端接受數(shù)據(jù)。然后通過(guò)WCF中存儲(chǔ)的IP地址,通過(guò)Send方法將其發(fā)送給客戶端。

下面我將在前一篇文章的基礎(chǔ)上實(shí)現(xiàn)這個(gè)語(yǔ)音聊天的功能。首先在客戶端添加聲音管理的類CallManager,這個(gè)類使用到了開(kāi)源的wave類庫(kù),代碼如下:

    
      public
    
    
      class
    
     CallManager
    {

        
    
      private
    
     WaveIn _waveIn;

        
    
      private
    
     WaveOut _waveOut;

        
    
      private
    
     IPEndPoint _serverEndPoint;

        
    
      private
    
     Thread _playSound;

        
    
      private
    
     UdpClient _socket;

        
    
      public
    
     CallManager(IPEndPoint serverEndpoint)
        {
            _serverEndPoint = serverEndpoint;
        }

        
    
      public
    
    
      void
    
     Start()
        {
            
    
      if
    
     (_waveIn != 
    
      null
    
     || _waveOut != 
    
      null
    
    )
            {
                
    
      throw
    
    
      new
    
     Exception("
    
      Call is allready started
    
    ");
            }

            
    
      int
    
     waveInDevice = (Int32)Application.UserAppDataRegistry.GetValue("
    
      WaveIn
    
    ", 0);
            
    
      int
    
     waveOutDevice = (Int32)Application.UserAppDataRegistry.GetValue("
    
      WaveOut
    
    ", 0);

            _socket = 
    
      new
    
     UdpClient(0); 
    
      // opens a random available port on all interfaces
    
    

            _waveIn = 
    
      new
    
     WaveIn(WaveIn.Devices[waveInDevice], 8000, 16, 1, 400);
            _waveIn.BufferFull += 
    
      new
    
     BufferFullHandler(_waveIn_BufferFull);
            _waveIn.Start();

            _waveOut = 
    
      new
    
     WaveOut(WaveOut.Devices[waveOutDevice], 8000, 16, 1);

            _playSound = 
    
      new
    
     Thread(
    
      new
    
     ThreadStart(playSound));
            _playSound.IsBackground = 
    
      true
    
    ;
            _playSound.Start();

        }

        
    
      private
    
    
      void
    
     playSound()
        {
            
    
      try
    
    
            {
                
    
      while
    
     (
    
      true
    
    )
                {
                    
    
      lock
    
     (_socket)
                    {
                        
    
      if
    
     (_socket.Available != 0)
                        {
                            IPEndPoint endpoint = 
    
      new
    
     IPEndPoint(IPAddress.Any, 0);
                            
    
      byte
    
    [] received = _socket.Receive(
    
      ref
    
     endpoint);
                            
    
      // todo: add codec
    
    

                            _waveOut.Play(received, 0, received.Length);
                        }
                    }
                    Thread.Sleep(1);
                }
            }
            
    
      catch
    
     (ThreadAbortException)
            {
            }
            
    
      catch
    
    
            {
                
    
      this
    
    .Stop();
            }
        }

        
    
      void
    
     _waveIn_BufferFull(
    
      byte
    
    [] buffer)
        {
            
    
      lock
    
     (_socket)
            {
                
    
      //todo: add codec
    
    
                _socket.Send(buffer, buffer.Length, _serverEndPoint);
            }
        }
        
    
      public
    
    
      void
    
     Stop()
        {
            
    
      if
    
     (_waveIn != 
    
      null
    
    )
            {
                _waveIn.Dispose();
            }

            
    
      if
    
     (_waveOut != 
    
      null
    
    )
            {
                _waveOut.Dispose();
            }

            
    
      if
    
     (_playSound.IsAlive)
            {
                _playSound.Abort();
            }

            
    
      if
    
     (_socket != 
    
      null
    
    )
            {
                _socket.Close();
                _socket = 
    
      null
    
    ;
            }
        }
    }
  

在服務(wù)端添加將接受到的聲音,發(fā)送給接受者的類,使用到了UDPClient類:

    
      public
    
    
      class
    
     UdpServer
    {
        
    
      private
    
     Thread _listenerThread;

        
    
      private
    
     List<IPEndPoint> _users = 
    
      new
    
     List<IPEndPoint>();

  
        
    
      private
    
     UdpClient _udpSender = 
    
      new
    
     UdpClient();

        
    
      public
    
     IPAddress ServerAddress
        {
            
    
      get
    
    ;
            
    
      set
    
    ;
        }

        
    
      public
    
     UdpClient UdpListener
        {
            
    
      get
    
    ;
            
    
      set
    
    ;
        }

        
    
      public
    
     UdpServer()
        {
            
    
      try
    
    
            {
                ServerAddress = IPAddress.Parse("
    
      127.0.0.1
    
    ");
            }
            
    
      catch
    
    
            {
                
    
      throw
    
    
      new
    
     Exception("
    
      Configuration not set propperly. View original source code
    
    ");
            }
        }

        
    
      public
    
    
      void
    
     Start()
        {
            UdpListener = 
    
      new
    
     System.Net.Sockets.UdpClient(0);
            _listenerThread = 
    
      new
    
     Thread(
    
      new
    
     ThreadStart(listen));
            _listenerThread.IsBackground = 
    
      true
    
    ;
            _listenerThread.Start();
        }

        
    
      private
    
    
      void
    
     listen()
        {
            
    
      while
    
     (
    
      true
    
    )
            {
                IPEndPoint sender = 
    
      new
    
     IPEndPoint(IPAddress.Any, 0);
                
    
      byte
    
    [] received = UdpListener.Receive(
    
      ref
    
     sender);

                
    
      if
    
     (!_users.Contains(sender))
                {
                    _users.Add(sender);
                }

                
    
      foreach
    
     (IPEndPoint endpoint 
    
      in
    
     _users)
                {
                    
    
      if
    
     (!endpoint.Equals(sender))
                    {
                        _udpSender.Send(received, received.Length, endpoint);
                    }
                }
            }
        }

        
    
      public
    
    
      void
    
     EndCall()
        {
            _listenerThread.Abort();
        }
    }
  

在WCF服務(wù)中添加兩個(gè)方法:初始化語(yǔ)音通信和結(jié)束語(yǔ)音通信。

            [OperationContract(IsOneWay = 
    
      false
    
    )]
        
    
      bool
    
     InitiateCall(
    
      string
    
     username);

        [OperationContract(IsOneWay = 
    
      true
    
    )]
        
    
      void
    
     EndCall();
具體是實(shí)現(xiàn)代碼:
  
    
      public
    
    
      bool
    
     InitiateCall(
    
      string
    
     username)
        {
            ClientCallBack clientCaller = s_dictCallbackToUser[OperationContext.Current.GetCallbackChannel<IZqlChartServiceCallback>()];
            ClientCallBack clientCalee = s_dictCallbackToUser.Values.Where(p => p.JoinChatUser.NickName == username).First();

            
    
      if
    
     (clientCaller.Callee != 
    
      null
    
     || clientCalee.Callee != 
    
      null
    
    ) 
    
      // callee or caller is in another call
    
    
            {
                
    
      return
    
    
      false
    
    ;
            }

            
    
      if
    
     (clientCaller == clientCalee)
            {
                
    
      return
    
    
      false
    
    ;
            }

            
    
      if
    
     (clientCalee.Client.AcceptCall(clientCaller.JoinChatUser.NickName))
            {
                clientCaller.Callee = clientCalee.Client;

                clientCalee.Callee = clientCaller.Client;

                clientCaller.UdpCallServer = 
    
      new
    
     UdpServer();
                clientCaller.UdpCallServer.Start();

                EmtpyDelegate separateThread = 
    
      delegate
    
    () 
                {
                    IPEndPoint endpoint = 
    
      new
    
     IPEndPoint(clientCaller.UdpCallServer.ServerAddress,
                        ((IPEndPoint)clientCaller.UdpCallServer.UdpListener.Client.LocalEndPoint).Port);

                    clientCalee.Client.CallDetailes(endpoint, clientCaller.JoinChatUser.NickName, username);
                    clientCaller.Client.CallDetailes(endpoint, clientCaller.JoinChatUser.NickName, username);

                    
    
      foreach
    
     (var callback 
    
      in
    
     s_dictCallbackToUser.Keys)
                    {
                        callback.NotifyMessage(String.Format("
    
      System:User \"{0}\" and user \"{1}\" have started a call
    
    ",clientCaller.JoinChatUser.NickName, username));
                    }
                };
                separateThread.BeginInvoke(
    
      null
    
    , 
    
      null
    
    );
                
    
      return
    
    
      true
    
    ;
            }
            
    
      else
    
    
            {
                
    
      return
    
    
      false
    
    ;
            }
        }

        
    
      public
    
    
      void
    
     EndCall()
        {
            ClientCallBack clientCaller = s_dictCallbackToUser[OperationContext.Current.GetCallbackChannel<IZqlChartServiceCallback>()];
            ClientCallBack ClientCalee = s_dictCallbackToUser[clientCaller.Callee];

            
    
      if
    
     (clientCaller.UdpCallServer != 
    
      null
    
    )
            {
                clientCaller.UdpCallServer.EndCall();
            }

            
    
      if
    
     (ClientCalee.UdpCallServer != 
    
      null
    
    ) 
            {
                ClientCalee.UdpCallServer.EndCall();
            }

            
    
      if
    
     (clientCaller.Callee != 
    
      null
    
    )
            {
                
    
      foreach
    
     (var callback 
    
      in
    
     s_dictCallbackToUser.Keys)
                {
                    callback.NotifyMessage(String.Format("
    
      System:User \"{0}\" and user \"{1}\" have ended the call
    
    ", clientCaller.JoinChatUser.NickName, ClientCalee.JoinChatUser.NickName));
                }

                clientCaller.Callee.EndCallClient();
                clientCaller.Callee = 
    
      null
    
    ;
            }

            
    
      if
    
     (ClientCalee.Callee != 
    
      null
    
    )
            {
                ClientCalee.Callee.EndCallClient();
                ClientCalee.Callee = 
    
      null
    
    ;
            }
        }
  
        還有部分做了修改的代碼見(jiàn)附件代碼。
  
    
      
        下面看下演示的截圖:
      
    
  
        1、兩個(gè)用戶登錄:
  
    
      1
    
  
    
      2
    
  
        2、選擇小花,點(diǎn)擊按鈕。麒麟向小花同學(xué)發(fā)起語(yǔ)音聊天:
  
    
      3
    
  
        3、小花同學(xué)接受通知,選擇是:
  
    
      4
    
  
        4、彈出通話中的窗體,雙發(fā)都可以選擇結(jié)束通話:
  
    
      5
    
  
        5、結(jié)束通話之后,消息框中會(huì)廣告消息
  
    
      6
    
  
    
      
        總結(jié):
      
    
  
    
    
    這個(gè)聊天程序主要都是用到了WCF的雙工通信。沒(méi)有用兩臺(tái)機(jī)子測(cè)試,我在我的筆記本上開(kāi)了一個(gè)服務(wù)端和一個(gè)客戶端,用了一個(gè)帶耳麥的耳機(jī),聲音效果良好。
  
    其實(shí)這個(gè)東西做完整還有很多細(xì)活,這個(gè)只是Demo,非常的粗糙。最近會(huì)很忙,期待將來(lái)的某一天能空去完善。如果實(shí)現(xiàn)了視頻的功能我會(huì)寫第四篇的。
  
    
      
        代碼:
      
    
    
      http://files.cnblogs.com/zhuqil/Zql_src.rar
    
  

WPF+WCF一步一步打造音頻聊天室(三):語(yǔ)音聊天


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對(duì)您有幫助就好】

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長(zhǎng)會(huì)非常 感謝您的哦?。。?/p>

發(fā)表我的評(píng)論
最新評(píng)論 總共0條評(píng)論
主站蜘蛛池模板: 日本不卡一区二区三区 | 奇米影视亚洲狠狠色777不卡 | 国产欧美日韩精品一区二区三区 | 久久精品视频播放 | 日韩高清一区二区 | 国产欧美亚洲另类第一页 | 同性女女黄h片在线播放 | 天天爽天天 | 91粉色视频在线观看 | 久久婷婷国产综合精品 | 欧美在线一区二区三区 | 亚洲视频中文 | 国产一级内谢a级高清毛片 国产一级片毛片 | 国内久久精品视频 | 久久综合九色综合欧洲色 | 只有精品| 精品久久国产视频 | 奇米狠狠干 | 另类日本人xxxxbbbb | 一级毛片秋霞特色大片 | 国产精品视频观看 | 国产国产精品四虎视频精品 | 亚洲黄色片视频 | 久久久亚洲国产精品主播 | 一区二区三区不卡免费视频97 | 欧美视频第二页 | 操操日| 久久九九有精品国产56 | 欧美性猛交aa一级 | 欧美在线观看第一页 | 五月激情综合网 | 黄色网毛片 | 亚洲日本va中文字幕婷婷 | 99热在线获取最新地址 | 4hu最新| 狠狠插狠狠干 | 国产精品柳州莫菁身材四 | 久久99这里只有精品国产 | 亚洲视屏一区 | 一级黑人 | 国产视频一区二 |