1. 程式人生 > >基於UDP協議實現P2P語音聊天系統(C#版本)

基於UDP協議實現P2P語音聊天系統(C#版本)

原創性申明

概述

基於事件模型的UDP通訊框架(適用於網路包編解碼)已經實現過了UDP的分包傳送資料的功能,而這篇文章主要是一個應用,使用UDP協議傳送語音和文字等資訊。在這個系統中沒有服務端和客戶端,相互通訊都是直接相互聯絡的,能夠很好的實現效果。

具體實現

1、語言資料來源獲取

要想傳送語音資訊,首先得獲取語音,這裡有幾種方法,一種是使用DirectX的DirectXsound來錄音,我為了簡便使用一個開源的外掛NAudio來實現語音錄取。 首先在專案中引用NAudio.dll元件,具體的實現程式碼如下所示:

//------------------錄音相關-----------------------------
        private IWaveIn waveIn;
        private WaveFileWriter writer;


        private void LoadWasapiDevicesCombo()
        {
            var deviceEnum = new MMDeviceEnumerator();
            var devices = deviceEnum.EnumerateAudioEndPoints(DataFlow.Capture, DeviceState.Active).ToList();
            comboBox1.DataSource = devices;
            comboBox1.DisplayMember = "FriendlyName";
        }
        private void CreateWaveInDevice()
        {

            waveIn = new WaveIn();
            waveIn.WaveFormat = new WaveFormat(8000, 1);
            waveIn.DataAvailable += OnDataAvailable;
            waveIn.RecordingStopped += OnRecordingStopped;
        }
        void OnDataAvailable(object sender, WaveInEventArgs e)
        {
            if (this.InvokeRequired)
            {
                this.BeginInvoke(new EventHandler<WaveInEventArgs>(OnDataAvailable), sender, e);
            }
            else
            {
                writer.Write(e.Buffer, 0, e.BytesRecorded);
                int secondsRecorded = (int)(writer.Length / writer.WaveFormat.AverageBytesPerSecond);
                if (secondsRecorded >= 10)//最大10s
                {
                    StopRecord();
                }
                else
                {
                    l_sound.Text = secondsRecorded + " s";
                }
            }
        }
        void OnRecordingStopped(object sender, StoppedEventArgs e)
        {
            if (InvokeRequired)
            {
                BeginInvoke(new EventHandler<StoppedEventArgs>(OnRecordingStopped), sender, e);
            }
            else
            {
                FinalizeWaveFile();
            }
        }
        void StopRecord()
        {
            AllChangeBtn(btn_luyin, true);
            AllChangeBtn(btn_stop, false);
            AllChangeBtn(btn_sendsound, true);
            AllChangeBtn(btn_play, true);


            //btn_luyin.Enabled = true;
            //btn_stop.Enabled = false;
            //btn_sendsound.Enabled = true;
            //btn_play.Enabled = true;
            if (waveIn != null)
                waveIn.StopRecording();
            //Cleanup();
        }
		private void Cleanup()
        {
            if (waveIn != null)
            {
                waveIn.Dispose();
                waveIn = null;
            }
            FinalizeWaveFile();
        }
        private void FinalizeWaveFile()
        {
            if (writer != null)
            {
                writer.Dispose();
                writer = null;
            }
        }
		 //開始錄音
        private void btn_luyin_Click(object sender, EventArgs e)
        {
            btn_stop.Enabled = true;
            btn_luyin.Enabled = false;
            if (waveIn == null)
            {
                CreateWaveInDevice();
            }
            if (File.Exists(soundfile))
            {
                File.Delete(soundfile);
            }

            writer = new WaveFileWriter(soundfile, waveIn.WaveFormat);
            waveIn.StartRecording();
        }
上面的程式碼實現了錄音,並且寫入本地檔案p2psound_A.wav中儲存

語音傳送

當錄好音後,需要將錄好的檔案傳送出去,具體的實現程式碼如下所示:

 MsgTranslator tran = null;
 public Form1()
        {
            InitializeComponent();
            LoadWasapiDevicesCombo();//顯示音訊裝置

            Config cfg = SeiClient.GetDefaultConfig();
            cfg.Port = 7777;
            UDPThread udp = new UDPThread(cfg);
            tran = new MsgTranslator(udp, cfg);
            tran.MessageReceived += tran_MessageReceived;
            tran.Debuged += new EventHandler<DebugEventArgs>(tran_Debuged);
        }
		private void btn_sendsound_Click(object sender, EventArgs e)
        {
            if (t_ip.Text == "")
            {
                MessageBox.Show("請輸入ip");
                return;
            }
            if (t_port.Text == "")
            {
                MessageBox.Show("請輸入埠號");
                return;
            }
            string ip = t_ip.Text;
            int port = int.Parse(t_port.Text);
            string nick = t_nick.Text;
            string msg = "語音訊息";

            IPEndPoint remote = new IPEndPoint(IPAddress.Parse(ip), port);
            Msg m = new Msg(remote, "zz", nick, Commands.SendMsg, msg, "Come From A");
            m.IsRequireReceive = true;
            m.ExtendMessageBytes = FileContent(soundfile);
            m.PackageNo = Msg.GetRandomNumber();
            m.Type = Consts.MESSAGE_BINARY;
            tran.Send(m);
        }
		private byte[] FileContent(string fileName)
        {
            FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
            try
            {
                byte[] buffur = new byte[fs.Length];
                fs.Read(buffur, 0, (int)fs.Length);

                return buffur;
            }
            catch (Exception ex)
            {
                return null;
            }
            finally
            {
                if (fs != null)
                {

                    //關閉資源
                    fs.Close();
                }
            }
        }
如上面程式碼所示,我們就把產生的語音檔案傳送出去了,傳送端的處理結束。

語音的接收與播放

語音的接收和文字訊息的接收沒有什麼不同,只不過語音傳送的時候是以二進位制傳送的,因此我們在收到語音後 就應該寫入到一個檔案裡面去,接收完成後,播放這段語音就行了。

下面這段程式碼主要是把收到的資料儲存到檔案中去,這個函式式我的NetFrame裡收到訊息時所觸發的事件,在文章前面提過的那篇文章裡。

void tran_MessageReceived(object sender, MessageEventArgs e)
        {
            Msg msg = e.msg;

            if (msg.Type == Consts.MESSAGE_BINARY)
            {
                string m = msg.Type + "->" + msg.UserName + "發來二進位制訊息!";
                AddServerMessage(m);
                if (File.Exists(recive_soundfile))
                {
                    File.Delete(recive_soundfile);
                }
                FileStream fs = new FileStream(recive_soundfile, FileMode.Create, FileAccess.Write);
                fs.Write(msg.ExtendMessageBytes, 0, msg.ExtendMessageBytes.Length);
                fs.Close();
                //play_sound(recive_soundfile);
                ChangeBtn(true);

            }
            else
            {
                string m = msg.Type + "->" + msg.UserName + "說:" + msg.NormalMsg;
                AddServerMessage(m);
            }
        }

收到語音訊息後,我們要進行播放,播放時仍然用剛才那個外掛播放,具體的實現程式碼如下所示:
//--------播放部分----------
        private IWavePlayer wavePlayer;
        private WaveStream reader;


        public void play_sound(string filename)
        {
            if (wavePlayer != null)
            {
                wavePlayer.Dispose();
                wavePlayer = null;
            }
            if (reader != null)
            {
                reader.Dispose();
            }
            reader = new MediaFoundationReader(filename, new MediaFoundationReader.MediaFoundationReaderSettings() { SingleReaderObject = true });

            if (wavePlayer == null)
            {

                wavePlayer = new WaveOut();
                wavePlayer.PlaybackStopped += WavePlayerOnPlaybackStopped;
                wavePlayer.Init(reader);
            }
            wavePlayer.Play();
        }
        private void WavePlayerOnPlaybackStopped(object sender, StoppedEventArgs stoppedEventArgs)
        {
            if (stoppedEventArgs.Exception != null)
            {
                MessageBox.Show(stoppedEventArgs.Exception.Message);
            }
            if (wavePlayer != null)
            {
                wavePlayer.Stop();
            }
            btn_luyin.Enabled = true;
        }private void btn_play_Click(object sender, EventArgs e)
        {
            btn_luyin.Enabled = false;
            play_sound(soundfile);
        }

介面展示:



技術總結

整個系統實現的過程中,主要用到的技術就是UDP和NAudio的錄音和播放功能,其中用到的UDP傳輸類我放在了github上面 地址在我的部落格左邊的個人介紹裡有地址  專案地址 https://github.com/zhujunxxxxx/ZZNetFrame希望這篇文章能夠提供一個思路。

相關推薦

基於UDP協議實現P2P語音聊天系統(C#版本)

原創性申明概述基於事件模型的UDP通訊框架(適用於網路包編解碼)】已經實現過了UDP的分包傳送資料的功能,而這篇文章主要是一個應用,使用UDP協議傳送語音和文字等資訊。在這個系統中沒有服務端和客戶端,相互通訊都是直接相互聯絡的,能夠很好的實現效果。具體實現1、語言資料來源獲取

Linux下基於UDP協議實現聊天室專案(附原始碼)

好久沒來更新了,這段時間一直在著手完成這個專案,由於之前沒有接觸過這種稍大型的專案,而且對於C/S架構以及UDP通訊的瞭解也不是很深,所以前面很大的一段時間都被浪費掉了,做了很大無用功。 剛開始弄的時候,也是在網上搜了很多資料,找了很多版本,發現大都有

c#基於udp實現p2p語音聊天工具

initial lis combobox pre cat microsoft user eat let 原創性申明 此博文的出處 為?http://blog.csd

基於UDP協議簡單聊天

code serve from pri true soc udp協議 bsp nbsp server端 import socket sk = socket.socket(type=socket.SOCK_DGRAM) sk.bind((‘127.0.0.1‘,8080)

UDP協議實現聊天小程式

  今天我們用之前講解過的UDP協議來寫一個最基礎,最簡單的網路聊天程式。 //我們通過udp協議來實現一個簡單的網路聊天程式 //這是客戶端的實現 //過程: // 1.建立套接字 // 2.繫結地址資訊 // 3.向服務端傳送資料 // 4.接

JAVA——基於UDP協議的文字聊天程式

基於UDP協議的文字聊天程式 1.要求 使用JAVA完成一個基於TCP協議的文字聊天程式,建立TCP客戶端套接字,分別為服務端和客戶端,並使兩端可以互聯。 2.UDP協議 在UDP協議中,沒有TCP中所謂的伺服器端和客戶端,而是有資料的傳送端和接收端。也就是說UDP

基於udp協議的字串計數【網路程式設計 - 基於udp協議的字串計數,C實現

udp_server.c #include <stdio.h> #include <winsock2.h> #pragma comment(lib, "ws2_32.lib") typedef struct sockaddr_in sockaddr_in ; typedef s

如何使用 Python 編寫基於 UDP 協議聊天工具

import socket from sys import exit s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) HOST = '10.1.73.12' PORT = 10888 s.bind((HOST, PORT)) while Tru

語音識別——基於深度學習的中文語音識別系統實現(程式碼詳解)

文章目錄 利用thchs30為例建立一個語音識別系統 1. 特徵提取 2. 模型搭建 搭建cnn+dnn+ctc的聲學模型 3. 訓練準備 下載資料

基於pjsip實現p2p語音對講

目的 為實現跨網路的語音對講,使位於NAT後的兩個裝置進行p2p的語音通訊,此處選用pjsip開源專案來實現。 未解決的問題:對稱型的NAT無法實現p2p打洞,pjsip採用turn服務進行轉發,不能稱之為純粹的p2p。 pjisp簡介 PJSIP是

Socket網路程式設計(二)UDP協議實現聊天工具

package UDP_chat; import java.awt.BorderLayout; import java.awt.Frame; import java.awt.List; import java.awt.Panel; import java.awt.TextField; import jav

15.基於UDP協議聊天室程式

使用UDP協議完成一個聊天室程式的小專案,大部分程式碼都有註釋,一看就能看到的. 實現的功能:               (1)檢視/顯示已經登陸的使用者資訊               (2)向已登陸的使用者傳送訊息               (3)輸出錯誤訊息,給

基於UDP協議的Socket程式設計——使用Socket程式設計實現客戶諮詢

利用UDP通訊的兩個端點是平等的,也就是說通訊的兩個程式關係是對等的,沒有主次之分,甚至它們的程式碼都可以完全一致,這一點要與基於TCP協議的Socket程式設計區分開來。 步驟: 1、利用DatagramPacket物件封裝資料包。 2、利用DatagramSocke

一個簡單的基於udp協議聊天程式

下面,我就介紹一下我開發的一個使用udp協議的網路通訊程式、 程式的要求非常簡單,就是當我們知道對方的ip地址後,我們可以通過命令列輸入對方的ip地址,就可以向對方傳送文字資訊。 程式的使用方式:由命令列開啟程式,命令列有一個可選的引數,即你想要通訊的目標的ip地址(首先要

廣電系統衛星信號ASI數據轉成UDP協議流進入流媒體系統互聯網分發

ASI UDP RTMP 流媒體系統 ASI:傳輸的是數字信號,壓縮視頻信號(例如MPEG2-T,裏面是H.264碼流),用於廣播電視領域。 在目前的DVB-C系統設備的傳輸接口有兩種MPEG2視頻碼流傳輸接口標準:異步串行接口ASI和同步並行接口SPI。本文

基於UDP協議的socket編程

tcp server end 註意 發出 發生 數據報 無連接 允許 UDP協議特點:   1.無連接。服務端與客戶端傳輸數據之前不需要進行連接,且沒有超時重發等機制,只是把數據通過網絡發送出去。也正是因為此特點,所以基於UDP協議的socket的客戶端在啟動之前不需要先啟

Python--網絡編程-----基於UDP協議的套接字

data soc nbsp net 服務 int ddr bind 客戶 服務端: 1 from socket import * 2 3 server = socket(AF_INET, SOCK_DGRAM) 4 server.bind((‘127.0.0.1

Java學習總結(十三)——基於UDP協議網絡編程

網絡編程 UDP協議 聊天 一.UDP網絡編程1.面向無連接的數據傳輸,不可靠的,但效率高(音頻,視頻等).2.UDP一次發送的數據不能超過64kb.3.UDP編程所需要的類(1)DatagramSocket此類表示用來發送和接收數據報包的套接字(2)DatagramPacket此類表示數據報包方

基於UDP協議的網絡程序

UDP socket下圖是基於UDP協議的客戶端/服務器程序的一般流程:圖1.1 UDP協議通信流程UDP套接口是無連接的、不可靠的數據報協議;既然他不可靠為什麽還要用呢?其一:當應用程序使用廣播或多播時只能使用UDP協議;其二:由於他是無連接的,所以速度快。因為UDP套接口是無連接的,如果一方的數據報丟失,

網絡編程之基於UDP協議套接字

本質 系統內存 現象 服務 自身 png 收發消息 accept color 1.UDP協議介紹(數據報協議)   UDP協議不同於TCP,通信時,它不需要事先建立雙向鏈接,並且不區分客戶端先啟動還是服務端前起,工作原理:基於udp協議傳輸的信息,協議會將數據自動加上自定義