这篇文章主要是一个应用,使用udp传送语音和文本等信息。在这个系统中没有服务端和客户端,相互通讯都是直接相互联系的。能够很好的实现效果。

语音获取

要想发送语音信息,首先得获取语音,这里有几种方法,一种是使用DirectX的DirectXsound来录音,我为了简便使用一个开源的插件NAudio来实现语音录取。 在项目中引用NAudio.dll

  1. //------------------录音相关-----------------------------
  2. private IWaveIn waveIn;
  3. private WaveFileWriter writer;
  4. private void LoadWasapiDevicesCombo()
  5. {
  6. var deviceEnum = new MMDeviceEnumerator();
  7. var devices = deviceEnum.EnumerateAudioEndPoints(DataFlow.Capture, DeviceState.Active).ToList();
  8. comboBox1.DataSource = devices;
  9. comboBox1.DisplayMember = "FriendlyName";
  10. }
  11. private void CreateWaveInDevice()
  12. {
  13. waveIn = new WaveIn();
  14. waveIn.WaveFormat = new WaveFormat(8000, 1);
  15. waveIn.DataAvailable += OnDataAvailable;
  16. waveIn.RecordingStopped += OnRecordingStopped;
  17. }
  18. void OnDataAvailable(object sender, WaveInEventArgs e)
  19. {
  20. if (this.InvokeRequired)
  21. {
  22. this.BeginInvoke(new EventHandler<WaveInEventArgs>(OnDataAvailable), sender, e);
  23. }
  24. else
  25. {
  26. writer.Write(e.Buffer, 0, e.BytesRecorded);
  27. int secondsRecorded = (int)(writer.Length / writer.WaveFormat.AverageBytesPerSecond);
  28. if (secondsRecorded >= 10)//最大10s
  29. {
  30. StopRecord();
  31. }
  32. else
  33. {
  34. l_sound.Text = secondsRecorded + " s";
  35. }
  36. }
  37. }
  38. void OnRecordingStopped(object sender, StoppedEventArgs e)
  39. {
  40. if (InvokeRequired)
  41. {
  42. BeginInvoke(new EventHandler<StoppedEventArgs>(OnRecordingStopped), sender, e);
  43. }
  44. else
  45. {
  46. FinalizeWaveFile();
  47. }
  48. }
  49. void StopRecord()
  50. {
  51. AllChangeBtn(btn_luyin, true);
  52. AllChangeBtn(btn_stop, false);
  53. AllChangeBtn(btn_sendsound, true);
  54. AllChangeBtn(btn_play, true);
  55. //btn_luyin.Enabled = true;
  56. //btn_stop.Enabled = false;
  57. //btn_sendsound.Enabled = true;
  58. //btn_play.Enabled = true;
  59. if (waveIn != null)
  60. waveIn.StopRecording();
  61. //Cleanup();
  62. }
  63. private void Cleanup()
  64. {
  65. if (waveIn != null)
  66. {
  67. waveIn.Dispose();
  68. waveIn = null;
  69. }
  70. FinalizeWaveFile();
  71. }
  72. private void FinalizeWaveFile()
  73. {
  74. if (writer != null)
  75. {
  76. writer.Dispose();
  77. writer = null;
  78. }
  79. }
  80. //开始录音
  81. private void btn_luyin_Click(object sender, EventArgs e)
  82. {
  83. btn_stop.Enabled = true;
  84. btn_luyin.Enabled = false;
  85. if (waveIn == null)
  86. {
  87. CreateWaveInDevice();
  88. }
  89. if (File.Exists(soundfile))
  90. {
  91. File.Delete(soundfile);
  92. }
  93. writer = new WaveFileWriter(soundfile, waveIn.WaveFormat);
  94. waveIn.StartRecording();
  95. }

上面的代码实现了录音,并且写入文件p2psound_A.wav

语音发送

获取到语音后我们要把语音发送出去

当我们录好音后点击发送,这部分相关代码是

  1. MsgTranslator tran = null;
  2. ublic Form1()
  3. {
  4. InitializeComponent();
  5. LoadWasapiDevicesCombo();//显示音频设备
  6. Config cfg = SeiClient.GetDefaultConfig();
  7. cfg.Port = 7777;
  8. UDPThread udp = new UDPThread(cfg);
  9. tran = new MsgTranslator(udp, cfg);
  10. tran.MessageReceived += tran_MessageReceived;
  11. tran.Debuged += new EventHandler<DebugEventArgs>(tran_Debuged);
  12. }
  13. private void btn_sendsound_Click(object sender, EventArgs e)
  14. {
  15. if (t_ip.Text == "")
  16. {
  17. MessageBox.Show("请输入ip");
  18. return;
  19. }
  20. if (t_port.Text == "")
  21. {
  22. MessageBox.Show("请输入端口号");
  23. return;
  24. }
  25. string ip = t_ip.Text;
  26. int port = int.Parse(t_port.Text);
  27. string nick = t_nick.Text;
  28. string msg = "语音消息";
  29. IPEndPoint remote = new IPEndPoint(IPAddress.Parse(ip), port);
  30. Msg m = new Msg(remote, "zz", nick, Commands.SendMsg, msg, "Come From A");
  31. m.IsRequireReceive = true;
  32. m.ExtendMessageBytes = FileContent(soundfile);
  33. m.PackageNo = Msg.GetRandomNumber();
  34. m.Type = Consts.MESSAGE_BINARY;
  35. tran.Send(m);
  36. }
  37. private byte[] FileContent(string fileName)
  38. {
  39. FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
  40. try
  41. {
  42. byte[] buffur = new byte[fs.Length];
  43. fs.Read(buffur, 0, (int)fs.Length);
  44. return buffur;
  45. }
  46. catch (Exception ex)
  47. {
  48. return null;
  49. }
  50. finally
  51. {
  52. if (fs != null)
  53. {
  54. //关闭资源
  55. fs.Close();
  56. }
  57. }
  58. }

如此一来我们就把产生的语音文件发送出去了

语音的接收与播放

其实语音的接收和文本消息的接收没有什么不同,只不过语音发送的时候是以二进制发送的,因此我们在收到语音后 就应该写入到一个文件里面去,接收完成后,播放这段语音就行了。

下面这段代码主要是把收到的数据保存到文件中去,这个函数式我的NetFrame里收到消息时所触发的事件,在文章前面提过的那篇文章里

  1. void tran_MessageReceived(object sender, MessageEventArgs e)
  2. {
  3. Msg msg = e.msg;
  4. if (msg.Type == Consts.MESSAGE_BINARY)
  5. {
  6. string m = msg.Type + "->" + msg.UserName + "发来二进制消息!";
  7. AddServerMessage(m);
  8. if (File.Exists(recive_soundfile))
  9. {
  10. File.Delete(recive_soundfile);
  11. }
  12. FileStream fs = new FileStream(recive_soundfile, FileMode.Create, FileAccess.Write);
  13. fs.Write(msg.ExtendMessageBytes, 0, msg.ExtendMessageBytes.Length);
  14. fs.Close();
  15. //play_sound(recive_soundfile);
  16. ChangeBtn(true);
  17. }
  18. else
  19. {
  20. string m = msg.Type + "->" + msg.UserName + "说:" + msg.NormalMsg;
  21. AddServerMessage(m);
  22. }
  23. }

收到语音消息后,我们要进行播放,播放时仍然用刚才那个插件播放

  1. //--------播放部分----------
  2. private IWavePlayer wavePlayer;
  3. private WaveStream reader;
  4. public void play_sound(string filename)
  5. {
  6. if (wavePlayer != null)
  7. {
  8. wavePlayer.Dispose();
  9. wavePlayer = null;
  10. }
  11. if (reader != null)
  12. {
  13. reader.Dispose();
  14. }
  15. reader = new MediaFoundationReader(filename, new MediaFoundationReader.MediaFoundationReaderSettings() { SingleReaderObject = true });
  16. if (wavePlayer == null)
  17. {
  18. wavePlayer = new WaveOut();
  19. wavePlayer.PlaybackStopped += WavePlayerOnPlaybackStopped;
  20. wavePlayer.Init(reader);
  21. }
  22. wavePlayer.Play();
  23. }
  24. private void WavePlayerOnPlaybackStopped(object sender, StoppedEventArgs stoppedEventArgs)
  25. {
  26. if (stoppedEventArgs.Exception != null)
  27. {
  28. MessageBox.Show(stoppedEventArgs.Exception.Message);
  29. }
  30. if (wavePlayer != null)
  31. {
  32. wavePlayer.Stop();
  33. }
  34. btn_luyin.Enabled = true;
  35. }private void btn_play_Click(object sender, EventArgs e)
  36. {
  37. btn_luyin.Enabled = false;
  38. play_sound(soundfile);
  39. }

在上面演示了接收和发送一段语音消息的界面

技术总结

主要用到的技术就是UDP和NAudio的录音和播放功能

其中用到的UDP传输类我放在了github上面 地址在我的博客左边的个人介绍里有地址  项目地址 https://github.com/zhujunxxxxx/ZZNetFrame

希望这篇文章能够提供一个思路。

C#基于UDP实现的P2P语音聊天工具(1)的更多相关文章

  1. c#基于udp实现的p2p语音聊天工具

    原创性申明 此博文的出处 为 http://blog.csdn.net/zhujunxxxxx/article/details/40124773假设进行转载请注明出处.本文作者原创,邮箱zhujunx ...

  2. Android 即时语音聊天工具 开发

    使用融云SDK 1. 功能需求分析 1.1 核心功能需求: * 即时通讯 * 文字聊天 * 语音聊天 1.2 辅助功能需求: * 注册.登录 * 好友添加功能 * 好友关系管理 2. 融云即时通讯平台 ...

  3. 基于Nodejs开发的web即时聊天工具

    由于公司需要开发web即时聊天的功能,开始时我们主要的实施方法是用jquery的ajax定时(10秒)轮询向服务器请求,由于是轮询请求,对 服务器的压力比较大.我们网站上线的时间不长,访问量不是很大, ...

  4. C 基于UDP实现一个简易的聊天室

    引言 本文是围绕Linux udp api 构建一个简易的多人聊天室.重点看思路,帮助我们加深 对udp开发中一些api了解.相对而言udp socket开发相比tcp socket开发注意的细节要少 ...

  5. C++开发的基于TCP协议的内网聊天工具

    项目相关地址 源码:https://github.com/easonjim/TCPChat bug提交:https://github.com/easonjim/TCPChat/issues

  6. Pilin —— 一个基于Xmpp openfire smack的即时聊天工具

    https://github.com/whfcomm/Pilin

  7. 基于Qt的P2P局域网聊天及文件传送软件设计

    基于Qt的P2P局域网聊天及文件传送软件设计 zouxy09@qq.com http://blog.csdn.net/zouxy09         这是我的<通信网络>的课程设计作业,之 ...

  8. 基于UDP协议的控制台聊天

    这几天学了java的网络编程弄出一个基于UDP协议的聊天工具 功能 添加并且备注好友(输入对方的ip) 删除好友 查看好友列表 用java写的控制台程序导出可执行程序后不能双击打开 还需要些一个脚本文 ...

  9. 与众不同 windows phone (31) - Communication(通信)之基于 Socket UDP 开发一个多人聊天室

    原文:与众不同 windows phone (31) - Communication(通信)之基于 Socket UDP 开发一个多人聊天室 [索引页][源码下载] 与众不同 windows phon ...

随机推荐

  1. Web Service 初步了解

    Web Service见名之意就是网络上的一些服务,解决的问题就是如何使用这些服务,因为软件的开发有各种各样的语言,利用Java,C#,VB.NET,PHP等等,如何使这些语言编写的程序能够进行互通, ...

  2. (转)A drop-in universal solution for moving text fields out of the way of the keyboard

    There are a hundred and one proposed solutions out there for how to move UITextField andUITextView o ...

  3. (转)Android中截取当前屏幕图片

    该篇文章是说明在Android手机或平板电脑中如何实现截取当前屏幕的功能,并把截取的屏幕保存到SDCard中的某个目录文件夹下面.实现的代码如下: /** * 获取和保存当前屏幕的截图 */ priv ...

  4. core_cm3文件函数一览

    core_cm3是ARM公司推出来的统一规定,这是对下游芯片厂商的统一规定,因此可以再Cortex-M3(CM3)之间进行移植.此文件中定义了一些对特殊功能寄存器的C语言形式的操作,本质上是内敛汇编和 ...

  5. 执行eclipse,迅速failed to create the java virtual machine。

    它们必须在一排,否则会出现The Eclipse executable launcher was unable to locate its companion shared library的错误 打开 ...

  6. git config配置文件 (共有三个配置文件)

    设置 git status的颜色. git config --global color.status auto 一.Git已经在你的系统中了,你会做一些事情来客户化你的Git环境.你只需要做这些设置一 ...

  7. transition过渡的趣玩

    本例中将三张图(来自网络)进行堆叠,鼠标悬停触发.附有源代码

  8. eclipse 404以及tomcat failed to start错误

    eclipse中的servlet项目有时会不编译,不编译可能就会出现404错误,因为在build path的输出目录并没有class文件,然而如果在输出目录引入之前编译的class文件,就可能出现cl ...

  9. C#中Property和Attribute的区别

    C#中Property和Attribute的区别 Attribute 字段Property 属性(get;set;) 属性的正常写: private string name; public strin ...

  10. python字符串的encode和decode

    原文 decode的作用是将其他编码的字符串转换成unicode编码. str1.decode('gb2312') #表示将gb2312编码的字符串转换成unicode编码 encode的作用是将un ...