英文翻译.doc

基于C#的语音通信

收藏

压缩包内文档预览:
预览图 预览图 预览图 预览图 预览图 预览图 预览图 预览图 预览图 预览图
编号:36648488    类型:共享资源    大小:649.26KB    格式:RAR    上传时间:2020-01-03 上传人:遗**** IP属地:湖北
25
积分
关 键 词:
基于 C# 语音 通信
资源描述:
基于C#的语音通信,基于,C#,语音,通信
内容简介:
毕业设计(论文)任务书课题名称 基于C#的语音通信 学院(部) 信息工程学院 专 业 电子信息工程 班 级 24031001 学生姓名 郭超 学 号 2403100127 3 月 12 日至 6 月 21 日共 15 周指导教师(签字) 教学院长(签字) 2014 年 3 月 12 日1、 设计内容(论文阐述的问题)本项目基于视频语音的交通事件,当今世界正处于信息时代,一旦在高速路上出现交通事件,交警能够第一时间看到事故现场,以便做出最优的解决办法,减少或者是降低高速公路上的一些意外事件。若是单兵交警无法处理交通事故可以通过视频语音发送给其他交警或者交警指挥中心,派遣人来处理此事件。局域网通信工具,是在局域网内部使用的,用户之间用来交流的一个工具,一般都具有文本聊天和语音通信功能。局域网通讯软件因其使用简单,系统资源消耗少等优点,成为各企事业单位等的局域网内广泛应用的软件之一。当前的局域网聊天工具有很多,最有名的算IPMSG(飞鸽传书)。Ipmsg能够实现局域网内消息、文件传递。但是,ipmsg 在某些时候,在同一vlan下的用户,能够彼此看见对方,却不能通信,因为其采用的是udp协议作为消息传递协议,而这种传输是不稳定的,所以有时候能看到对方的用户之间不能实现互相通信,怎么办呢?一种解决方法就是,采用tcp协议,面向连接的传输层协议作为消息传递协议,开发出相应的网络应用程序就能够实现彼此连接的双方进行信息的传递。随着互联网的不断发展,“互联网要担当起通讯大任”的声音不绝于耳。未来的电信业务将呈现多元化格局。同样是话音业务,可能是PSTN网络(传统电话网)提供的,可能是Internet提供的,还可能是有线电视网络,甚至电力网、煤气管道网提供的。而用户的选择也将包括电脑与电脑、电脑与电话、电话与电话、电话与(智能)手机等通话方式。这一切,都是以IP为基础的通讯网络,而非传统通讯模式的电信服务。所以,VOIP将是聊天工具的一个发展方向。现有的局域网聊天工具,一般都不具备语音聊天的功能,本软件集成了语音聊天模块,用户可以通过此软件实现语音互通,提高局域网用户之间信息传递效率以及交流质量。通过开发本软件,可以了解当前流行的voip技术,练习socket编程,扩展知识面,锻炼编程的能力等,所以极具研究价值。2、 设计原始资料(实验、研究方案)计算机和通信网络是这一时代所谓“信息基础设施”。随着网络的普及化,网络作为人们相互间沟通与交流联系的现代化工具日益重要,可以说现在人们的生活已经离不开网络。在现实生活中人们可以通过多种手段与家人亲戚朋友交流通讯,像电话,电子邮件等。但这些手段都有些缺点,要么费用太高,要么实时性不强,要么1次只能和1位好友进行交流。于是这就需要1种便宜,快速,能同时与多个好友进行通讯的网络工具的出现,而网络聊天工具就满足了这些需求,填补了这个空白。 在互联网相当普及的今天,在互联网上聊天对很多“网虫”来说已经是家常便饭了。聊天室程序可以说是网上最简单的多点通信程序。一个简单的聊天室, 从程序员的观点来看就是在多个I/O端点之间实现多对多的通信。交通、计算机和通信网络是这一时代所谓“信息基础设施”。随着网络的普及化,网络作为人们相互间沟通与交流联系的现代化工具日益重要,可以说现在人们的生活已经离不开网络。在现实生活中人们可以通过多种手段与家人亲戚朋友交流通讯,像电话,电子邮件等。但这些手段都有些缺点,要么费用太高,要么实时性不强,要么1次只能和1位好友进行交流。于是这就需要1种便宜,快速,能同时与多个好友进行通讯的网络工具的出现,而网络聊天工具就满足了这些需求,填补了这个空白。 在互联网相当普及的今天,在互联网上聊天对很多“网虫”来说已经是家常便饭了。聊天室程序可以说是网上最简单的多点通信程序。一个简单的聊天功能, 从程序员的观点来看就是在多个I/O端点之间实现多对多的通信。广域网上的即时通信工具,如今一般采用UDP或者 TCP协议体系来实现,开发技术已经比较成熟,这些软件,在使用方面各有特色,在实现方面也各有所长,但基于这些产品正在商业运营阶段,其实现方式属于商业机密,具体细节不可能得知,但是它在大的方面无非就是利用各种平台上的网络通信接口,建构基于TCP/IP,或者UDP协议的软件产品。网络的飞速发展使网络应用程序的开发地位显的越来越重要,而网络应用程序的开发和传统应用程序的开发在思想和实现上有很大的区别,随着网络技术和数据库管理系统的发展,C/S体系结构在软件的开发中越来越流行。因为这种结构的应用软件可以充分发挥网络的作用和数据库系统的优势,以满足人们各种应用的需求。三、设计完成后提交的文件和图表(论文完成后提交的文件)1. 计算说明书部分:2、 图纸部分: (1)文字发送模块原理框图 (2)语音采集模块原理框图 (3)语音保存模块原理框图 (4)语音播放模块原理框图四、毕业设计(论文)进程安排 序号 设计(论文)各阶段名称 日期(教学周) 1 收集通信和语音方面的资料,确定设计方案 3月17日-3月23日(第3周) 2 完成英文翻译和开题报告 3月24日-3月30日(第4周) 3 查找有关C#语言的书籍,C#入门经典, 3月31日-4月20日(第5-7周) 了解其语法和使用过程 4 设计程序基本框架,开始编写程序 4月21日- 5月4日(第8-9周) 5 学习有关socket套接字通信以及Tcp、Udp 通信协议,查找有关线程方面的资料,搭起 5月5日-6月1日(第10-14周) 框架,衔接各个模块之间的通信,调试程序 6 撰写毕业设计论文 6月2日- 6月11日(第15周) 7 提交论文评审与答辩 6月12日-6月21日(第16周) 5、 主要参考资料1 马俊、何欣,C#网络编程及应用M,机械工业出版社,2008年2 唐政、房大伟,C#项目开发全程实录M,清华大学出版社,2008年3 张跃廷、王小科,C#程序开发范例宝典M,人民邮电出版社,2007年4 Anthony Jones,Windows 网络编程M.清华大学出版社,2004年5 谢希仁,计算机网络(第4版)M,电子工业出版社,2006年6 张海藩,软件工程(第二版)M,人民邮电出版社,2006年7 Ramadas Shanmugam、R.Padmini、S.Nivedita,Special Edition Using TCP/IP Second EditionM,NIIT,2002年8 刘洪林,蒋昌茂,张建永,IP语音通信原理、设计及组网应用,电子工业出版社,2009年9 王凯明,C#下用P2P技术实现点对点聊天EB/OL,/s/blog_6c9a9a870100m2d8.html,2009年10 (美)库柏,C#设计模式M,电子工业出版社,2003年11 冉林仓,尹建民,Visual C#.NET入门与进阶M,清华大学出版社,2007年6长安大学毕业设计(论文)开题报告表课题名称基于C#的语音通信课题来源校外协作课题类型工程设计指导教师高涛学生姓名郭超学 号2403100127专 业电子信息工程课题的意义:在当今信息时代,基于网络的即时通信在交通领域以及其他领域给人们带来诸多便利,信息通信已成为这个世纪必不可少的组成部分,也成为当今网络应用的主流。随着互联网技术的飞速发展,聊天软件作为即时性通信工具的主流,已被越来越多的人所喜爱。聊天工具作为当今使用最为广泛的即时通信工具之一,可以方便人们随时随地进行在线交流,比如腾讯公司的QQ聊天软件。在中国,上网的用户几乎都用腾讯QQ进行聊天。当前腾讯QQ的注册帐户已经超过4.3亿,是中国用户最多、最为流行的聊天工具。起初的QQ只支持文字聊天,随着相关技术的发展,腾讯QQ也不断的自我完善,增加众多实用的、人性化的功能,得到了许多用户的支持与喜爱。现如今腾讯公司已经初步完成了面向在线生活产业模式的业务布局:构建了QQ、QQ.com、QQ游戏以及QQ移动手机门户这四大网络平台,形成了规模巨大的网络社区。在满足用户信息传递与知识获取需求方面,拥有QQ.Com门户、QQ即时通讯工具以及年初收购的Foxmail电子邮件等;在满足用户群体交流和资源共享方面,腾讯推出的个人博客Q-Zone将与访问量极大的论坛、聊天室、QQ群相互协同;在满足用户个性展示和娱乐服务方面,腾讯拥有非常成功的虚拟形象产品QQshow、QQpet(宠物)和QQGame(游戏)QQMusic/Radio/LiveTV(音乐/电台/电视直播)等,另外对手机用户提供了彩铃、彩信等无线增值业务;在用户的交易需求方面,专门为腾讯用户设计开发的C2C电子商务拍卖网已经上线,并和整个社区平台无缝整合。像QQ这样聊天工具已经逐渐适应了时代的发展,市场的需求,给人们提供了全方位的信息服务平台,使用户在互联网上的生活更加丰富多彩,它不仅带来了巨大的商业价值,而且也给人们的生活带来了无比的欢乐与便利。国内外发展状况:中国的大部分网络用户都拥有自己的QQ号码。腾讯计算机系统有限公司成立于1998年11月。1999年2月,腾讯自主开发了基于Internet的即时通信网络工具腾讯即时通信Tencent Instant Messenger,简称TIM或腾讯QQ。腾讯QQ经过三年时间的发展,到2002年,其用户群成为中国最大的互联网注册用户群,注册用户达到1亿6000万,其中活跃用户总数超过5000万。自此腾讯QQ成为中国最大的即时通信服务提供商。腾讯公司是中国最早也是目前中国市场上最大的本土互联网即时通信软件开发商。公司成立7年多以来,一直以追求卓越的技术为导向,始终处于稳健、高速发展的状态。腾讯QQ超过4.3亿的注册用户群体现了网络用户对腾讯QQ这一聊天工具的广泛应用。在当今市场经济高速发展的前提下,腾讯QQ也在不断自我完善,渐渐地演变为一个人性化、市场化的多功能的聊天工具软件,它可以给网络聊天用户带来越来越多的人性化服务:从原来单一的文字聊天过渡到集语音、视频、文件传输等多功能于一身的聊天软件。ICQ和MSN是在国外比较盛行的即时聊天工具,腾讯QQ的开发对MSN和ICQ都进行了借鉴。ICQ是I Seek You(我找你)的缩写,最初是一家以色列公司开发出来的免费软件,中文名称目前可以称之为“网络呼叫器”,它在全球拥有超过4000万的用户。事实上这种软件最主要的功能就是让用户知道网络上的朋友现在有没有上线(前提是对方也有安装ICQ),然后可以互送Messages(消息)交谈或是交换档案等等,比电子邮件更具即时性。正如一位ICQ软件的最初程序设计员所说:“当你登录因特网之后,你周围都是些自己熟悉的人,而且,你还可以和他们分享这种体验,这的确是一件令人激动的事情。”MSN是微软推出的聊天软件,是一套类似ICQ的网上即时通讯软件,它以最简单的方式为用户提供强有力的即时消息支持,同时能使Microsoft Outlook Express(R) 和MSN Hotmail(R)变得更简单易用。MSN Messenger Service 还可使用户间的联机交谈变得更加轻松愉快,了解谁在联机以及查看用户何时联机并与其交换即时消息。用户间可以进行单独的聊天,或者在同一个对话窗口中与多达四个的联系人进行聊天。自动的输入指示器可让用户知道何时联系人正在输入答复,还可以进行全球电话呼叫以非常低的费用呼叫世界的任何地方。用户可以选择所要使用的电话服务提供商,从服务提供商的列表中选择最适合的提供商。用交谈取代键盘输入,使用计算机上的话筒和扬声器就可以和位于世界各地的朋友进行交谈,甚至可以呼叫联系人将文本消息发送到联系人的移动电话和传呼机上(注:此特性仅适用于美国和加拿大),而进入“聊天室”结识新朋友,或加入与名人聊天,此特性在所有地区都适用。用户可以使用 MSN Messenger 与朋友交换照片、喜爱的音乐或任何其他文件,也可以监视新的电子邮件并查看用户有多少新的电子邮件。MSN Messenger能在收到新的 Hotmail邮件时通知用户,还能邀请联系人进行联机DirectPlay 兼容游戏。本课题的研究内容:本课题的研究内容是客户端通过服务器可以向其他客户端发送文字和语音消息,本设计的开发采用C/S结构,在基于.NET开发环境下,使用C#编程语言进行本软件的开发。本文首先论述国内外聊天工具的发展情况和现今的发展方向,并对开发环境和开发语言进行了简单介绍;对本设计所要开发的多功能聊天软件作了较为详细的需求分析,并给出了多功能聊天软件的设计方案,主要功能包括文字聊天、语音聊天等;本毕业设计主要实现的是多功能聊天软件的部分功能,即语音聊天功能,分模块开发实现,将编写的文字聊天与文件传输模块集成在一起,构成一个实用的、功能完善的聊天软件,达到预期目标;通过运行、测试与分析说明,该多功能聊天软件运行稳定、可靠,具有一定的实用价值。本课题的研究方法和手段:阅读有关C#语言、通信技术方面的相关书籍和资料,对语音通信有一个基本的了解,在此基础上,分析课题设计的内容要求以及要实现的功能,采用整体到部分的设计思路,从整体上把握设计要求,再分各部分设计相关的模块;然后查阅相关资料进行各模块的软件设计;将各模块整合,从整体上调试设计的软件;将设计的软件进行反复测试。本课题的研究成果:课题研究实现基本的聊天窗体框架,实现了在局域网内基本的文字、语音聊天功能,可以实时的进行通信,方便了人们之间的交流。任务完成的阶段安排及时间安排: 3月17日-3月23日(第3周)收集通信和语音方面的资料,确定设计方案3月24日-3月30日(第4周)完成英文翻译和开题报告3月31日-4月20日(第5-7周)查找有关C#语言的书籍,C#入门经典,了解其语法和使用过程4月21日- 5月4日(第8-9周)设计程序基本框架,开始编写程序5月5日-6月1日(第10-14周)学习有关socket套接字通信以及Tcp、Udp通信协议,查找有关线程方面的资料,搭起框架,衔接各个模块之间的通信,调试程序6月2日- 6月11日(第15周)撰写毕业设计论文6月12日-6月21日(第16周)提交论文评审与答辩完成任务所具备的条件因素:1.Microsoft Visual Studio编译软件;2.掌握socket通信原理,会使用Tcp和Udp通信协议;3.理解多线程的使用机理;4.对C/S架构有一定的了解;指导教师意见及建议:指导教师签名: 年 月 日注:1、课题来源分为:国家重点、省部级重点、学校科研、校外协作、实验室建设和自选项目;课题类型分为:工程设计、专题研究、文献综述、综合实验。 2、此表由学生填写,交指导教师签署意见后方可开题。 基于 C#的语音通信摘摘 要要 从实际工程应用角度出发,以计算机网络原理为指导,结合当前网络中的一些常用技术,编程实现基于 windows socket 的网络通信工具,该通讯工具具有文本聊天、语音通信功能。该论文主要对当今通讯软件的发展情况、与该通讯软件相关的各种技术以及该通信工具的实现做了一个详细的阐述:通信工具的开发主要用到了网络通信技术、语音的采集、保存、传输以及回放等技术;该聊天软件基于 windows socket 通信,在此基础上,实现点对点聊天、语音通信的传输;由于系统采用 c/s 结构,客户端与客户端以及客户端与服务器端需要传送一些控制消息,因此系统需要自定义一些消息类型,这是该通信工具的又一大关键之处;语音通信作为一个独立的模块,镶嵌在点对点的聊天中,语音聊天的关键之处在于语音的采集,保存,发送,回放上。本文首先论述国内外通信工具的发展情况和现今的发展方向,并对开发环境和开发语言进行了简单介绍;对本设计所要开发的多功能通信软件作了较为详细的需求分析,并给出了多功能通信软件的设计方案,主要功能包括文字聊天、语音通信等;本毕业设计主要实现的是多功能通信功能,构成一个实用的、功能完善的聊天通信软件,达到预期目标;通过运行、测试与分析说明,该多功能聊天软件运行稳定、可靠,具有一定的实用价值。关键词:网络通信,文本聊天,语音聊天,C/S 结构,多线程 基于 C#的语音通信IAbstractIn the opinion of practical engineering and guided by computer network principal and taking some common network technicals, I take the software for communicating tool based on Winsock2.0 into practical.The softwares functions include point to point private text chat、sound communication. This article is main particular expatiate about the development of today communicating software、the technicals of communicating software and the designing, programming of the software for communicating tool: the software is need that transport control information between client and client, client and server because of the systems C/S structure. So it is another key point that the software defines many types of information itself; sound chatting is enchased in the pointing to pointing communicating as an independent module. Gathering, compressing, transporting, decompressing and playing sound information are the key technical of the sound chatting.This paper first elaborates the development situation of domestic and foreign chats tool and the development direction nowadays, and has carried on the simple introduction to the development environment and the development language. The demand of multi-function communication software is analyzed in detail, and its plan is provided. Finally, this design integrates the functions that another schoolmate designs the writing and file transfer, which forms chat software of practical and full functions. This software achieves the anticipated target. Through the running, the test and analysis, it shows, this multi-function chat software is stable, reliable, and has some practical values.Key words: network communication,text-chat,audio communication ,C/S Structure,multi-threaded,audio recording and playing 基于 C#的语音通信II目目 录录第一章 前言.1 1.1 课题背景.1 1.2 国内外主要通信工具的发展状况.1 1.2.1 国外通信软件开发情况 .1 1.2.2 国内通信软件开发现状 .2 1.3 课题研究意义.2 1.4 课题的主要工作.3第二章 开发平台与相关知识.5 2.1 系统功能要求.5 2.2 需求分析.5 2.2.1 硬件要求.5 2.2.2 程序运行环境.6 2.3 C# 简介.6 2.4 MICROSOFT .NET FRAMEWORK 简介.7 2.5 MICROSOFT DIRECTX SDK 简介.8 2.6 多线程技术.8 2.7 面向连接的 TCP 协议.9 2.8 C/S 结构与 WINDOWS SOCKETS 网络编程 .10 2.9 MICROSOFT SQL SERVER 2008 R2 数据库 .12第三章 系统结构.14 3.1 功能需求.14 3.2 系统模块流程.14 3.3 模块概要设计.16 3.4 数据库的连接.17第四章 系统模块详细设计与实现.19 基于 C#的语音通信III 4.1 注册模块的分析与设计.19 4.2 登录模块的分析与设计.21 4.3 文本聊天模块设计实现.23 4.3.1 文本聊天模块概述.23 4.3.2 文本聊天的模块设计.25 4.3.3 文本聊天客户端和服务器端工作原理.29 4.4 语音通信模块实现.31 4.4.1 语音通信模块概述.31 4.4.2 语音聊天的模块设计思想.32 4.4.3 语音通信发送端和接收端工作原理.34 4.5 文本聊天与语音通信模块功能的综合设计.35 第五章 主要问题及解决.37 5.1 多线程问题 .37 5.2 套接字异常 .37 5.3 网络流异常 .38结论.39结束语.40致谢.41参考文献.43附录.44 基于 C#的语音通信第 0 页 共 78 页第一章 前言1.1 课题背景当今世界正处于信息时代,一旦在高速路上出现交通事件,交警能够第一时间看到事故现场,以便做出最优的解决办法,减少或者是降低高速公路上的一些意外事件。若是单兵交警无法处理交通事故可以通过视频语音发送给其他交警或者交警指挥中心,派遣人来处理此事件。局域网通信工具,是在局域网内部使用的,用户之间用来交流和信息传递与共享的一个工具,一般都具有文本聊天和语音通信功能。局域网通讯软件因其使用简单,系统资源消耗少等优点,成为各企事业单位等的局域网内广泛应用的软件之一。如今互联网以其独特的传播方式吸引了大量用户,同时也给人们带来了许多便利,譬如可以借助于网络进行相互交流、信息传递等。聊天工具作为互联网中运用最为广泛的通信工具之一,它可以让用户之间进行即时的交流和信息的传递。聊天工具的出现可以说是基于互联网通信交流方式的历史性变革,它已经渐渐取代了原来效率低、费用高的如信件、电报的通信方式,以其快速、交互、简便的方式给用户的交流提供了简单、易用的信息平台,成为现今应用最为广泛的即时通信工具之一。随着相关技术的日益发展,用户对功能的需求不断提高。为了更好地服务于用户,聊天工具的功能也在不断地完善。在未来的网络时代中,聊天工具将以即时通信为其基本特点,并与各种网络应用整合,同时随着无线网络的广泛应用,它将成为未来不可缺少的一种通信软件。像 QQ 这样聊天工具已经逐渐适应了时代的发展,市场的需求,给人们提供了全方位的信息服务平台,使用户在互联网上的生活更加丰富多彩,它不仅带来了巨大的商业价值,而且也给人们的生活增添了便利与欢乐。1.2 国内外主要通讯工具的发展状况1.2.1 国外通信软件开发情况国外的通信业发展较早,与国内企业相比它们已经在企业的经营管理、市场运作和自身的发展上得出了很多经验,形成了自己的发展特色。首先国外的企业有比较明确的分工,大、小企业所开发的产品有着明显的区别。小的企业无论是在人力还是物力上都不能与大企业相比,但是他们经营灵活,管理不墨守成规,对市场的嗅觉比较灵敏,可以随时根据市场的需求进行产品转移,因此它们多开发一些比较小的但具有创新意义的项目,小的通信软件企业由于受实力的限制,向小而精、小而专 基于 C#的语音通信第 1 页 共 78 页的方向发展。具有独创性是一些小软件企业的重要特点。有的小型软件企业还向某一领域的创新方面发展,为企业带来丰厚的回报。小企业对某个方面的软件进行开发,一旦成功,经过测试满足要求后,将其所研发的软件产品拿到大企业(如 IBM、Intel、Sony)进行宣传推销,有些大公司将其产权买断为自己所用,或者大公司可以将小公司全面收购,将其变为自己的一个部门,对其进行直接的领导并按照本公司的管理运营方式对其进行改造,使其能够完全融入公司当中。1.2.2 国内通信软件开发现状由于历史的原因,国内的通信软件业发展较晚,同时也错过了一些发展机会,与国外的企业有着很大的差距。在通信制造业中,有将近 70%80%的企业并没有真正意义上的自己的核心技术,而是通过贴牌(OEM)的方式进行经营。通信运营业所使用的软件,绝大部分随设备一起购进,仅有很少量的增值服务应用软件是自己开发或合作开发。因此,通信业在技术上受制于人,发展的后劲明显不足。总体来看,我国的通信软件业还处于初期发展的低水平阶段,多数企业还没有进入系统软件的开发,较少使用先进的开发工具和软件开发平台。北京通信企业开发软件的规模大部分都在百万元级和十万元级的水平上,千万元级的较大型软件还很少。这种规模和国外的同类软件企业相比还有很大的差距,这也成为国内通信软件业发展的一个瓶颈,它极大地限制了国内企业赶超国际先进水平的步伐;在中低端产品方面,国内的技术发展较快,与国外的差距不大,但今后的竞争会更加激烈。据调查,在北京通信业开发的系统软件和应用软件中,使用最多的操作系统是WindowsXP,少部分使用的是 Unix 和其它操作系统软件,这表明我国通信领域和其他领域一样,被微软控制着。在开发嵌入式的软件中,使用最多的操作系统是美国风河公司的平台。开发数据库软件,使用最多的是 Oracle 系统,也有少数软件是用Sybase、Informix、SQLserver 和其它系统,很少有我国自己开发的数据库,这和我国整个软件产业的发展水平是一致的。但是通信软件使用的数据库,尤其是终端设备中使用的嵌入式数据库,不能采用上述大、中型数据库,而应像国外通信企业那样,根据设备和需求,开发适用、小型的数据库。1.3 课题研究意义随着信息化的普及和发展,现在网络不仅在各企事业单位中的应用越来越广泛而且也已经逐渐融入了每个人的生活当中,逐渐的成为人们生活中不可或缺的一部分。伴随着网络时代潮流的到来使得人与人之间又多了一种交流方法,那就是通过网络实现跨区域的间接交流,但是网络只是提供了硬件上的支持,想通过网络实现无地域局限性 基于 C#的语音通信第 2 页 共 78 页的交流还需要有软件方面的支持,迫于市场的需求关系聊天系统诞生了,最早的聊天系统只是在 UNIX 环境上,使用 talk 指令建立用户间纯文字的在线交谈,其所提供的功能仅能传输一般 ASC码文字而已。在当今信息时代,信息通信已成为这个世纪必不可少的组成部分,随着互联网技术的发展,聊天软件作为即时性通信工具的主流,已被越来越多的人所喜爱。在中国,上网的用户几乎都用腾讯 QQ 进行聊天。当前腾讯 QQ 的注册帐户已经超过 4.3 亿,是中国用户最多、最为流行的聊天工具。起初的 QQ 只支持文字聊天,随着相关技术的发展,腾讯 QQ 也不断的自我完善,增加众多实用的、人性化的功能,得到了许多用户的支持与喜爱。现如今腾讯公司已经初步完成了面向在线生活产业模式的业务布局:构建了 QQ、QQ.com、QQ 游戏以及 QQ 移动手机门户这四大网络平台,形成了规模巨大的网络社区。在满足用户信息传递与知识获取需求方面,拥有 QQ.Com 门户、QQ 即时通讯工具以及年初收购的 Foxmail 电子邮件等;在满足用户群体交流和资源共享方面,腾讯推出的个人博客 Q-Zone 将与访问量极大的论坛、聊天室、QQ 群相互协同;在满足用户个性展示和娱乐服务方面,腾讯拥有非常成功的虚拟形象产品 QQshow、QQpet(宠物)和 QQGame(游戏)QQMusic/Radio/LiveTV(音乐/电台/电视直播)等,另外对手机用户提供了彩铃、彩信等无线增值业务;在用户的交易需求方面,专门为腾讯用户设计开发的 C2C 电子商务拍卖网已经上线,并和整个社区平台无缝整合。像 QQ 这样聊天工具已经逐渐适应了时代的发展,市场的需求,给人们提供了全方位的信息服务平台,使用户在互联网上的生活更加丰富多彩,它不仅带来了巨大的商业价值,而且也给人们的生活带来了无比的欢乐与便利。1.4 课题的主要工作本课题基于视频语音的交通事件,在高速路上出现交通事件,交警能够第一时间看到事故现场,以便做出最优的解决办法,减少或者是降低高速公路上的一些意外事件。若是单兵交警无法处理交通事故可以通过视频语音发送给其他交警或者交警指挥中心,派遣人来处理此事件。局域网通信工具,是在局域网内部使用的,用户之间用来交流和信息传递与共享的一个工具,一般都具有文本聊天和语音通信功能。随着用户对软件功能的需求不断提高,即时通讯的产品也不断地更新换代。即时通讯的发展不论是在基础应用方面还是在扩展应用方面都有着飞跃。为了满足用户的需求,适应公司的项目,选择了江苏江阴大桥中一小部分模块作为设计课题,主要对通信软件的语音和文字通信等相关功能的实现原理进行了研究。 基于 C#的语音通信第 3 页 共 78 页课题首先对通信软件的发展以及其对人们的生活有何影响作了简述,并对开发环境和工具进行了相应说明并对此聊天软件系统进行了较为详细的分析,对客户端和服务器端之间的网络通信,语音保存、播放、以及对设备的初始化做了深入的学习和探索,进而提出了该多功能聊天软件的设计方案。在本次语音通信设计与开发中,从开始对 C#语言的认识,到整个设计框架的摸索、熟悉、理解、以至于掌握,分模块实现设计文字聊天,语音通信,设备初始化以及保存播放等功能,将各个模块衔接起来,一步一步进行调试,达到预期的目的,完成设计的既定目标。 基于 C#的语音通信第 4 页 共 78 页第二章 开发平台与相关知识2.1 系统功能要求在同一个局域网中,如何根据自身业务的要求,量身定制,对音频设备进行合理搭配,选择一套合理的语音通信系统。如何来满足局域网内部用户的通讯要求,语音的实时传输等,正是现代社会通讯所必需的。设计本系统时,分析网络承载、整个系统结构的组建等是实现局域网文字聊天和语音通信所必需的。文字、语音通信作为一种广泛的网络应用对其基础的承载网络环境有着较高的网络要求。其中应重点考虑的是网络的通畅情况、端到端的时延、时延抖动、丢包率等问题。文字、语音聊天的功能和应用效果体现在客户端,而服务器端则是必不可少的,对于系统的需求分析在系统设计的过程中应该明确、细致。这个程序要实现的功能如下:(1)开始先运行服务器端程序,启动服务器,捕捉启动是否正常,并给出提示,等待客户端的连接。(2)一旦有某个网内的机器上线了,要有即时通知,并能及时更新用户界面中的 ip 列表,返回该客户端的 ip 地址和端口号,添加到下拉菜单中。(3)运行客户端程序,输入服务器端绑定的 IP 地址和端口号,连接至服务器端,弹出是否连接成功的提示框后,可以进行语音、文字通信聊天。(4)聊天界面要人性化,下面是发送框,上面有已有聊天记录,并借助滚动条看到当前所有的聊天记录。(5)当有远程用户向本机发送文字的时候,服务器端收到后显示在文本框中,可以选择客户端所要发送的用户,并转发给该用户。(6)语音通信不需要通过服务器端点击转发,而是只要有语音,服务器端立即转发给指定用户,保证通信的实时性。2.2 需求分析2.2.1 硬件要求 操作系统:windows XP 或者 win7; 内存:1G 以上(最低为 1G) ; CPU:Intel(R) Celeron(R) 2.0GHz 或 AMD 1800+以上均可; 基于 C#的语音通信第 5 页 共 78 页 PC 机, 耳机设备,网络(局域网) 。2.2.2 程序运行环境系统开发平台:Microsoft Visual Studio 2010Microsoft DirectX SDK (2010)主要用到 Microsoft.DirectX和Microsoft.DirectX.DirectSound 这两个程序集SQL Server 2008 R2 数据库系统开发语言:C#运行平台:Window2000,Windows XP,Windows Server 2003,win7运行环境:Microsoft .Net Framework 2.0 或者 Microsoft .Net Framework 4.02.3 C#语言简介C#(C sharp)是一种最新的、面向对象的编程语言,它使得程序员可以快速地编写各种基于 Microsoft .NET 平台的应用程序。Microsoft .NET 提供了一系列的工具和服务来最大程度地开发、利用计算与通讯领域。C#面向对象的卓越设计使它成为构建各类组件的理想之选无论是高级的商业对象还是系统级的应用程序。使用简单的 C#语言结构,这些组件可以方便的转化为 XML 网络服务,从而使它们可以由任何语言在任何操作系统上通过 Internet 进行调用。最重要的是,C#使得 C+程序员可以高效率地开发程序,而绝不损失 C/C+原有的强大功能。因为这种继承关系,C#与 C/C+具有极大的相似性,熟悉类似语言的开发者可以很快地转向 C#。C#语言是允许类型定义、扩展的元数据,这些元数据可以应用于任何对象。项目构建者可以定义领域特有的属性并把他们应用于任何语言元素类、接口等等。然后,开发人员可以编程检查每个元素的属性。这样,很多工作都变得更加方便:譬如编写一个小工具来自动检查每个类或接口是否被正确定义为某个抽象商业对象的一部分,或者只是创建一份基于对象的领域特有属性的报表。定制的元数据和程序代码之间的紧密对应有助于加强程序的预期行为和实际实现的之间的对应关系。在 C#中,每个对象都自动生成为一个 COM 对象。开发者不再需要显式的实现 IUnknown和其他 COM 接口这些功能都是内置的、类似的,C#可以调用现有的 COM 对象,无论它是由什么语言编写的。C#包含了一个特殊的功能:使程序可以调用任何纯 API。在一段特别标记的代码中,开发者可以使用指针和传统 C/C+特性,如手工的内存管理和指针运算。这是其相对于其它环境的极大优势。这意味着 C#程序员可以在原有的 C/C+代码的基础上编写程序,而不是彻底放弃那些代码。C#的设计目的是简化网络应用。使用 C#语言能够迅 基于 C#的语音通信第 6 页 共 78 页速地架构基于 Windows 和 Internet 的应用程序和组件,如标准的 Windows 应用程序和控制台应用程序,编译后生成的文件扩展名为 EXE;程序库应用程序,编译后生成的文件扩展名为 DLL,主要用来共享程序代码。2.4 Microsoft .NET Framework 简介.NET Framework 是由微软开发,一个致力于敏捷软件开发(Agile software development) 、快速应用开发(Rapid application development) 、平台无关性和网络透明化的软件开发平台。.NET 是微软为下一个十年对服务器和桌上型软件工程迈出的第一步。NET 包含许多有助于 Internet 和 Intranet 应用迅捷开发的技术。.NET 也为编程界面(API)提供了新功能和开发工具。这些革新使得程序设计员可以同时进行 Windows 应用软件和网络应用软件以及元件和服务(web service)的开发。.NET 提供了一个新的反射性的且面向对象程序设计编程界面。.NET 设计得足够通用化从而使许多不同高级语言都得以被汇集。.NET Framework 的目的就是要让建立 Web Services 以及因特网应用程序的工作变的简单,.NET Framework 包括了三大部分:第一个部分是 Common Language Runtime(CLR,所有.NET 程序语言公用的执行时期组件) ,第二部分是共享对象类别库(提供所有.NET 程序语言所需要的基本对象) ,第三个部分是重新以组件的方式写成的(旧版本则是以 asp.dll 提供 ASP 网页所需要的对象) 。基本上安装了之后对机器不会有很大的影响,NET Framework 是支持生成和运行下一代应用程序和 XML Web services 的内部 Windows 组件 NET 的运行环境,类似用运行时间库的东西,要运行.NET 的计算机必须安装了这个东西。具体的说是支持生成和运行下一代应用程序和 XML Web services 的内部 Windows 组件。.NET Framework 旨在实现下列目标:提供一个一致的面向对象的编程环境,而无论对象代码是在本地存储和执行,还是在本地执行但在 Internet 上分布,或者是在远程执行的。提供一个将软件部署和版本控制冲突最小化的代码执行环境。提供一个可提高代码(包括由未知的或不完全受信任的第三方创建的代码)执行安全性的代码执行环境。提供一个可消除脚本环境或解释环境的性能问题的代码执行环境。使开发人员的经验在面对类型大不相同的应用程序(如基于 Windows 的应用程序和基于 Web 的应用程序)时保持一致。按照工业标准生成所有通信,以确保基于 .NET Framework 的代码可与任何其他代码集成。.NET Framework 具有两个主要组件:公共语言运行库和 .NET Framework 类库。公共语言运行库是 .NET Framework 的基础。您可以将运行库看作一个在执行时管理代码的代 基于 C#的语音通信第 7 页 共 78 页理,它提供内存管理、线程管理和远程处理等核心服务,并且还强制实施严格的类型安全以及可提高安全性和可靠性的其他形式的代码准确性。事实上,代码管理的概念是运行库的基本原则。以运行库为目标的代码称为托管代码,而不以运行库为目标的代码称为非托管代码。.NET Framework 的另一个主要组件是类库,它是一个综合性的面向对象的可重用类型集合,您可以使用它开发多种应用程序,这些应用程序包括传统的命令行或图形用户界面(GUI) 应用程序,也包括基于所提供的最新创新的应用程序(如 Web 窗体和 XML Web services) 。.NET Framework 可由非托管组件承载,这些组件将公共语言运行库加载到它们的进程中并启动托管代码的执行,从而创建一个可以同时利用托管和非托管功能的软件环境。.NET Framework 不但提供若干个运行库宿主,而且还支持第三方运行库宿主的开发。例如, 承载运行库已为托管代码提供可伸缩的服务器端环境。 直接使用运行库以启用应用程序和 XML Web services。Internet Explorer 是承载运行库(以 MIME 类型扩展的形式)的非托管应用程序的一个示例。使用 Internet Explorer 承载运行库使您能够在 HTML 文档中嵌入托管组件或 Windows 窗体控件。以这种方式承载运行库使得托管移动代码(类似于 Microsoft ActiveX 控件)成为可能,不过它需要只有托管代码才能提供的重大改进(如不完全受信任的执行和独立的文件存储) 。2.5 Microsoft DirectX SDK 简介SDK 是 Software Development Kit 的缩写,中文意思就是“软件开发工具包” 。这是一个覆盖面相当广泛的名词,可以这么说:辅助开发某一类软件的相关文档、范例和工具的集合都可以叫做 “SDK” 。具体到我们这个系列教程,我们后面只讨论广义 SDK 的一个子集即开发 Windows 平台下的应用程序所使用的 SDK。SDK 提供了一整套开发 Windows 应用程序所需的相关文件、范例和工具的“工具包” 。由于 SDK 包含了使用 API 的必需资料,所以人们也常把仅使用 API 来编写 Windows 应用程序的开发方式叫做“SDK 编程” 。而 API 和 SDK 是开发 Windows 应用程序所必需的东西,所以其它编程框架和类库都是建立在它们之上的,比如 VCL 和 MFC,虽然他们比起“SDK 编程”来有着更高的抽象度,但这丝毫不妨碍它们在需要的时候随时直接调用 API 函数。2.6 多线程技术线程是在共享内存空间中并发的多道执行路径,在 C#中,是使用 System.Threading 命 基于 C#的语音通信第 8 页 共 78 页名空间中的 Thread 类来创建线程的,线程优先级可以更改为 ThreadPriority 枚举中定义的一个值,C#中的 lock 关键字是实现线程同步的一种方法,同步的线程称为安全线程,除非绝对必要,否则不要创建线程安全的代码,因为添加不必要的锁定会降低性能。多线程是为了使得多个线程并行的工作以完成多项任务,以提高系统的效率。线程是在同一时间需要完成多项任务的时候被实现的。在本质上和结构来说,.NET 是一个多线程的环境。有两种主要的多线程方法是.NET 所提倡的:使用 ThreadStart 来开始你自己的进程,直接的 (使用ThreadPool.QueueUserWorkItem)或者间接的(比如 Stream.BeginRead,或者调用 BeginInvoke)使用 ThreadPool 类。线程的基类是 System.Threading。所有线程通过 CLI来进行管理。2.7 面向连接的 TCP 协议Windows 中的很多东西都是从 Unix 领域借鉴过来的,Socket 也是一样。在 Unix 中,socket 代表了一种文件描述符(在 Unix 中一切都是以文件为单位) ,而这里这个描述符则是用于描述网络访问的。就是程序员可以通过 socket 来发送和接收网络上的数据。你也可以理解成是一个 API。有了它,你就不用直接去操作网卡了,而是通过这个接口,这样就省了很多复杂的操作。在 C#中,MS 为我们提供了 System.Net.Sockets 命名空间,里面包含了 Socket 类。有了 socket,那就可以用它来访问网络了,还得有些基本的条件:a. 要确定本机的 IP 和端口,socket 只有与某一 IP 和端口绑定,才能发挥强大的威力。b. 得有协议,TCP 或者UDP 协议,由于程序中使用的 TCP 协议,则论文中着重介绍 TCP 协议 。如果具备了基本的条件,就可以开始用它们访问网络了。主要步骤如下: a. 建立一个套接字 b. 绑定本机的 IP 和端口 c. TCP 是面向连接的,所以要利用 Listen()方法来监听网络上是否有人给自己发东西。 d. TCP 情况下,如果监听到一个连接,就可以使用 accept 来接收这个连接,然后就可以利用 Send/Receive 来执行操作了。 e. 如果不想继续发送和接收了,就不要浪费资源。能关闭的就使用 close 函数关闭。详细步骤如下图 1-1 所示: 基于 C#的语音通信第 9 页 共 78 页图 1-1 无连接的套接字系统调用时序2.8 C/S 结构与 WINDOWS SOCKETS 网络编程通常的通信工具,都采用客户机/服务器(C/S)体系结构,C/S 结构是这样的一种结构:它包括一个客户机(或前端),一个服务器(或称后端),客户机的作用是访问和处理远程服务器上的数据,服务器的作用是接收和处理客户机的数据请求。有时,可能有多个客户向同一个服务器同时请求服务,这就需要服务器决定怎样处理这些请求。Client/Server 结构是当前数据库应用程序中极为流行的一种方式。尤其是网络技术的发展,使得当前很多系统都采用这种方式进行构造,其最大的优点是将计算机工作任务分别由客户端和服务器端来共同完成,这样有利于充分合理的利用系统资源。另外它的服务器端还可以将信息集中起来,任何客户机都可以通过访问服务器而获得所需的信息。Client/Server 模型最终可归结为一种“请求/应答”关系。一个请求总是首先被客户发出,然后服务器总是被动地接收请求,返回客户需要的结果。在客户发出一个请求之前,服务进程一直处于休眠状态。一个客户提出请 基于 C#的语音通信第 10 页 共 78 页求后,服务进程被“唤醒”并且为客户提供服务,对客户的请求做出所需要的应答,如图 1-2 所示:图 1-2 客户机/服务器通信结构示图为了方便 Client/Server 模型的网络编程,90 年代初,由 Microsoft 联合了其他几家公司共同制定了一套 WINDOWS 下的网络编程接口,即 Windows Sockets 规范,它不是一种网络协议,而是一套开放的、支持多种协议的 Windows 下的网络编程接口。现在的 Winsock已经基本上实现了与协议无关,你可以使用 Winsock 来调用多种协议的功能,但较常使用的是 TCP/IP 协议。在 IP 连接领域有两种通信类型:面向连接的(Connection-oriented ) 和无连接的(Connectionless)。在面向连接的套接字中,使用 TCP 协议来建立两个 IP 地址端点之间的会话。一旦建立了这种连接,就可以在设备之间可靠地传输数据。为了建立面向连接的套接字,服务器端和客户端必须分别进行编程。C/S 模式下 Windows socket 编程示意图,如图 1-3 所示: 基于 C#的语音通信第 11 页 共 78 页图 1-3 C/S 模式下 socket 编程2.9 Microsoft SQL Server 2008 R2 数据库介绍Microsoft SQL Server 2008 是一个分布式的关系型数据库管理系统,具有客户机/服务器体系结构,采用了 Transact-sql 的 sql 语言在客户机与服务器间传递客户机的请求与服务器的处理结果。众所周知,SQL Server 2008 是一个高可靠性、搞安全、功能丰富且智能的数据平台,它提供了革命性的易管理性,内置了企业级报表与分析工具。基于 SQL Server 2008 已有的功能与优势,SQL Server 2008R2 为关键业务应用、提高效率以及自服务的商业智能提供了更高级别的支持与保障。微软致力于为客户与合作伙伴提供更多更大的价值,SQL Server 2008R2 在支持 OLTP 与 BI 更多的功能的同时,提供比其他友商更低的总体拥有成本。当今,随着硬件的不断革新,微软是唯一一个不按核对多核处理器收费的主流数据库厂商。能够满足今天的商业环境要求不同类型的数据库解决方案。它一种应用广泛的数据库管理系统,具有许多显著的优点:易用性、适合分布式组织的可伸缩性、 基于 C#的语音通信第 12 页 共 78 页用于决策支持的数据仓库功能、与许多其他服务器软件紧密关联的集成性、良好的性价比等。性能、可伸缩性及可靠性是基本要求,而进入市场时间也非常关键。除这些核心企业品质外,SQL Server 2008 还为您的数据管理与分析带来了灵活性,允许单位在快速变化的环境中从容响应,从而获得竞争优势。从数据管理和分析角度看,将原始数据转化为商业智能和充分利用 Web 带来的机会非常重要。作为一个完备的数据库和数据分析包,SQL Server 2008 为快速开发新一代企业级商业应用程序、为企业赢得核心竞争优势打开了胜利之门。作为重要的基准测试可伸缩性和速度奖的记录保持者,SQL Server 2008 是一个具备完全 Web 支持的数据库产品,提供了对可扩展标记语言(XML)的核心支持以及在Internet 上和防火墙外进行查询的能力。 基于 C#的语音通信第 13 页 共 78 页第三章 系统结构3.1 功能需求基于公司大桥工作项目中的视频语音需求,通过需求调研并分析,确定系统具备的基本功能,语音通信。我自己想从基础学起、做起,在里面增添了文字聊天功能,实现简单的文字聊天。采用的是 TCP 模式,包括服务器端和客户端。首先启动服务器端,客户端通过用户名和密码,再输入服务器端的 IP 和端口号登录服务器,服务器响应客户端登录并提示有用户登录,此时两个用户就可以进行文字聊天,在文字聊天时通过服务器中转。语音聊天也是以同样的方式登录,两个用户连接到服务器,点击按钮就可以实现通信。软件系统功能主要分为客户端功能、服务器端功能、数据库表: (1)服务器端功能 1.打开服务器等待用户连接; 2.处理用户发送信息; 3.保存用户聊天数据; 4.转发用户聊天数据;(2)客户端功能 1.用户注册界面和实现; 2.用户登录界面和实现; 3.用户发送信息界面和实现; 4.ip 连接服务器;(3)数据库需求 1.基于服务器跨平台运行的构想,服务器的后台数据库使用了 SQL Server 2008 R2。 2.主要的数据表,登录注册表,该表主要包含了用户的账号信息,用户名和密码。 3.聊天系统采用用户名作为用户帐号,并且每个用户名是唯一的,做系统内部的用户标识。3.2 系统模块流程在整个系统中主要运行两个功能:(1)文字聊天流程图如下图 1-4 所示: 基于 C#的语音通信第 14 页 共 78 页图 1-4 文字聊天流程图(2)语音通信流程图如下图 1-5 所示:1-5 语音通信流程图 基于 C#的语音通信第 15 页 共 78 页3.3 模块概要设计主界面是系统的初始界面,主要功能是为系统内所有的模块提供接口,协调各个子模块在系统中的关系,为模块提供运行环境的支持。主界面采用类 Windows 操作系统的一个界面,在感官视觉上给人一种亲切的感觉,在操作上也比较方便,符合人们平时的使用习惯。客户端的主界面包括两个模块:注册模块、登录模块。它的总体结构如图 1-6 所示:图 1-6 客户端总体结构图在主界面开始之前,用户首先得进行登录、注册,以便开始文字语音通信。大致可分为一下两个界面:(1)登录模块登录功能是语音聊天软件的重要部分。结构如 UML 图 1-7 所示: 图 1-7 登录子模块 UML 图(2)注册模块注册模块的功能是把用户的信息存入到服务端的数据库中。结构如 UML 图 1-8 所示:登录模块语音聊天文本聊天客户端 主界面注册模块登录模块 基于 C#的语音通信第 16 页 共 78 页图 1-8 注册子模块 UML 图3.4 数据库的连接系统可以采用任何一种流行的数据库,Visual Studio 2010 系统采用了 Microsoft 公司的 SQL Server 2008 R2 作为后台数据库。SQL Server 2008 R2 是功能强大的数据库管理系统,拥有十分友好的用户界面,支持结构化查询语言(structured query language) 。本系统中服务器类型是数据库引擎,服务器名称为 GUO-PCSQLEXPRESS,SQL Server 身份验证,使用的登录名为 sa,密码为 123456 进行连接测试。(1)创建数据库和表 打开 SQL Server 2008 R2 Management Studio,连接到本地服务器,创建数据库VoiceCommunication,再在数据库中新建表 userInformations,可以手动插入数据,或者利用 SQL 语句进行数据插入。(2)建立连接在 VS2010 中,与数据库建立连接的标准调用方法有多种,本系统采用Data source = GUO-PCSQLEXPRESS;Initial Catalog = VoiceCommunication;Integrated Security = True;(3)执行 SQL 语句在登录类中采用 SQL 查询语句对用户的登录名与密码和数据库中的数据进行比对,用于登录用户的验证,反馈是否登录成功。在注册类中采用插入语句进行操作,将用户所注册的信息存入数据库表中。(4)检索结果SQL 语句发送后,数据库返回的结果是否为真,而且系统将用户输入的数据和数据库 基于 C#的语音通信第 17 页 共 78 页返回的结果集逐个地进行比较以确定用户的输入是否正确,最后反馈给用户。(5)关闭连接在对象使用完毕后应当调用 close()方法解除与数据库的连接,并关闭数据库,以释放应用程序占用的系统资源。图 1-9 演示如何连接 SQL Server 2008 数据库的过程建立数据源建立与数据库的连接发送 SQL 操作语句执行 SQL 语句,得到结果关闭数据库连接,释放资源图 1-9 连接数据库图 数据源 连接SQL 语句结果集关闭连接 基于 C#的语音通信第 18 页 共 78 页第四章 系统模块详细设计与实现4.1 注册模块的分析与设计 用户通过填写注册相关的信息并将其提交存入的数据库中去,表明注册成功,可以在数据库中查看所注册的用户名和密码,所使用的数据库 SQL 语句以及代码如下:string userName = textBoxUserName.Text.Trim();string userPassword = textBoxUserPassword.Text.Trim();string userPasswordRepetition = textBoxUserPasswordRepetition.Text.Trim();if(userName != & userPassword != & userPasswordRepetition != ) if (string.Compare(userPassword,userPasswordRepetition) = 0) string consqlserver = Data Source = GUO-PCSQLEXPRESS;Initial Catalog = VoiceCommunication;Integrated Security = True; SqlConnection con = new SqlConnection(consqlserver); con.Open(); string sql = select * from userInformations where userName = + userName + and userPassword = + userPassword + ; SqlCommand com = new SqlCommand(sql,con); SqlDataReader sdr = com.ExecuteReader(); sdr.Read(); if (sdr.HasRows) MessageBox.Show(该用户已被注册,请重新注册!, 提示); else 基于 C#的语音通信第 19 页 共 78 页 sdr.Close(); string insertUser = insert into userInformations(userName,userPassword) values ( + userName + , + userPassword + ); SqlCommand insertCom = new SqlCommand(insertUser,con); insertCom.ExecuteNonQuery();/执行插入语句 con.Close(); con.Dispose(); MessageBox.Show(注册成功!,提示); else MessageBox.Show(密码不一致!,提示); else if (userName = ) MessageBox.Show(用户名不能为空!,提示); else if(userPassword = ) MessageBox.Show(密码不能为空!, 提示); else if(userPasswordRepetition = ) MessageBox.Show(重复密码不能为空!, 提示); 基于 C#的语音通信第 20 页 共 78 页 在登录的界面中有个注册按钮,可以注册一个新的用户,注册时前后两次输入的密码必须一致,否则无法完成注册。注册界面如下图 2-1 所示:图 2-1 注册界面及提示4.2 登录模块的分析与设计用户在注册获得用户名和密码后,在登录界面填写用户名和密码并提交给服务器,服务器通过用户提交过来的信息和数据库中原有的用户信息进行比较和验证,如果结果一致表明验证成功,用户进入到客户端的系统界面下,如果不一致,给用户返回登录错误信息,用户可以再次输入登录。登录代码如下所示:string userName = textBoxUserName.Text.Trim();string userPassword = textBoxPassword.Text.Trim();if (userName != & userPassword != ) /定义数据库连接服务器语句 string consqlserver = Data Source = GUO-PCSQLEXPRESS;Initial Catalog = VoiceCommunication;Integrated Security = True; 基于 C#的语音通信第 21 页 共 78 页 /定义SQL查询语句:用户名 密码 string sql = select * from userInformations where userName = + userName + and userPassword = + userPassword + ; /定义SQL Server连接对象 SqlConnection connection = new SqlConnection(consqlserver); /打开数据库 connection.Open(); /定义查询命令:表示对数据库执行一个SQL语句或存储过程 SqlCommand com = new SqlCommand(sql, connection); /执行查询:提供一种读取数据库行的方式 SqlDataReader sread = com.ExecuteReader(); try if (sread.Read() /如果存在用户名和密码正确数据执行进入系统操作 MessageBox.Show(登录成功,提示); this.DialogResult = DialogResult.OK; this.Close(); else MessageBox.Show(用户名或者密码错误); catch (Exception ex) throw new Exception(ex.ToString();/处理异常信息 finally connection.Close();/关闭连接 connection.Dispose();/释放连接 sread.Dispose();/释放资源 基于 C#的语音通信第 22 页 共 78 页 else if (userName = ) MessageBox.Show(用户名不能为空); else if (userPassword = ) MessageBox.Show(密码不能为空); 输入用户名和密码后点击登录按钮,登录成功与否都会有相应的提示。登录系统界面如下图 2-2 所示:图 2-2 登录界面以及提示4.3 文本聊天模块设计实现4.3.1 文本聊天模块概述凡是聊天通讯工具,都少不了文本聊天功能,这是个基础也是最基本的功能。通过文本聊天,局域网内用户能够发送和接收文字信息,并在程序窗体上显示出来,相互之间进行在线交流,并能根据用户需要,对聊天记录进行简单的保存。文本聊天的信息传输协议主要有面向连接的 TCP 和无连接的 UDP。TCP 协议是互联网中最重的协议之一,主要特点是: 基于 C#的语音通信第 23 页 共 78 页保证数据包的准确到达;保证各数据包到达的顺序和数据包发出的顺序相同。UDP 协议提供了快速但不一定可靠的传输服务,UDP 协议采用无连接的套接字,不需要在网络设备之间发送连接信息,和 TCP 相比,UDP 缺乏双方的握手信号,因此不保证数据包一定到达目的地,可靠性不如 TCP,而且,由于 UDP 没有任何对双方会话的支持,不能保证各数据包到达的顺序与数据包发出的顺序相同。所以,在文本聊天模块中,服务器端与客户端的连接是采用的 TCP 套接节进行连接。客户端与服务端之间连接的操作流程具体详细说明如下图 2-3 所示:关闭 serversocket()关闭 socket()创建一个套接字(socket)关闭 socket()()和服务器端进行通信(send/recv) 调用 connect()建立与 server 端的连接)创建套接字(socket)将套接字绑定到一个本地地址和端口上(bind)将套接字设为监听模式,准备接收客户请求(listen)用返回的套接字和客户端进行通信(send/recv)等待客户请求到来;当请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept)返回,等待另一客户请求调用 socket()创建一个会话的 socket调用 serversocket()创建一个监听的 socket客户端服务端 基于 C#的语音通信第 24 页 共 78 页图 2-3 客户端与服务端以 TCP 连接方式调用过程 .2 文本聊天的模块设计文本聊天的模块设计Server 服务器运行时,利用一个 Socket 对象 SocketServer 启动服务器,用函数 gethostname 来获得服务器端主机名和 IP,同时在服务器对话框中显示服务器 IP,并将分配的固定端口号 8888 显示在对话框中。用一个 textbox 显示客户端的聊天内容以及系统提示消息。每一个 Client 客户端启动时,利用服务器内定的用户号和密码来登录。当用户登录成功之后,弹出文字语音通信界面,当服务器开启之后,用户输入服务器端 IP 地址,开始连接服务器,如下图 2-4 所示: 图2-4 开启服务器端界面及提示数据库中保存的用户信息即登录名和密码,用户信息可以用代码实现增删改查,也可以通过对数据库中的表直接操作实现相同的功能,用 VS2010控件 DataGridView 显示如下图2-5所示: 基于 C#的语音通信第 25 页 共 78 页图2-5 用户信息客户端连接到服务器后客户端主界面显示如下图 2-6 所示:图 2-6 客户端界面及提示 基于 C#的语音通信第 26 页 共 78 页当有客户端连接到服务器后,服务器端的界面和数据会有所变化,具体变化如下图 2-7 所示:图2-7 服务器端界面同时服务器端会有提示,表明有用户连接到服务器端了,出的提示框如下图 2-8 所示:图 2-8 服务器提示框创建 socket 对象来处理服务器端与客户端的连接,要使用 socket 对象,首先要调用 基于 C#的语音通信第 27 页 共 78 页构造函数,然后调用 Create 函数创建一个 Socket 句柄。socket 函数缺省是创建一个流 socket;创建一个数据报 socket。服务器端调用 Accept,客户端调用 Connect,以用来发送和接收数据。用户“小明”和用户“小五”文字聊天过程及其内容,如图 2-9 和图 2-10 所示:图2-9 文字聊天过程及用户“小明”界面 基于 C#的语音通信第 28 页 共 78 页图2-10 文字聊天过程及用户“小五”界面4.3.3 文本聊天客户端和服务器端工作原理(1)服务器端:最少有两个 socket,一个是服务端负责监听客户端发来连接请求,但不负责与请求的客户端通信,另一个是每当服务器端成功接收到客户端时,但在服务器端创建一个用与请求的客户端进行通信的 socket,该 socket 完成客户端之间的通信。步骤如下所示: 申请一个 socket (socketWatch)用来监听的 绑定到一个 IP 地址和一个端口上 开启侦听,等待接授客户端的连接 当有连接时创建一个用于和连接进来的客户端进行通信的 基于 C#的语音通信第 29 页 共 78 页socket(socketConnection) 即续监听,等侍下一个客户的连接具体代码实现如下:serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);/服务器端ip地址和端口号IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Parse(48), 8888);serverSocket.Bind(localEndPoint);/绑定ip和端口serverSocket.Listen(10);serverThread = new Thread(new ThreadStart(AcceptConnection);serverThread.IsBackground = true;serverThread.Start();(2)客户端:指定要连接的服务器端地址和端口,通过创建一个 socket 对象来初始化一个到服务器端的 TCP 连接。步骤如下所示: 申请一个 socket(socketClient) 连接服务器(指明 IP 地址和端口号)具体代码实现如下:IPEndPoint remoteEndPoint = new IPEndPoint(ip, Convert.ToInt32(strPort);clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);ObjectState state = new ObjectState();state.stateSocket = clientSocket;/连接服务器 基于 C#的语音通信第 30 页 共 78 页clientSocket.BeginConnect(remoteEndPoint,new AsyncCallback(ConnectionCallback), state);connectionDone.WaitOne();/创建接收数据线程clientThread = new Thread(new ThreadStart(OnReceive);clientThread.IsBackground = true;clientThread.Start();4.4 语音通信模块实现4.4.1 语音通信模块概述语音通信方面主要分为一下几个模块:(1)语音采集:采集的作用就是从你的麦克风中获取数据,采用 DirectSound 类来实现这个技术。(2)语音保存:语音在录制发送的过程中对语音进行本地保存,格式为 WAV,WAVE 是录音时用的标准的 WINDOWS 文件格式,扩展名为“WAV” ,使用 DirectSound 采集的 WAV 声音,其音频数据是按照 PCM(脉冲编码调制,对连续变化的模拟信号进行抽样、量化和编码产生的数据,0 和 1 的组合)调制后放入缓冲区的。WAVE 文件格式采用 RIFF 文件格式结构,对 PCM 数据和其它一些音频信息进行相应的编排,从而最终形成的 WAVE 文件才能被音频播放器识别,才能进行播放。录音结束,写入 WAV 文件尾,这样一个可以播放的 WAVE 文件就好了。(3)语音传输:将采集到的声音缓存传输到网络上的其它主机,采用 Socket TCP 方式来实现。除了语音传输之外,还需要对网络进行监听,从而能捕获对方发送给自己的语音信息。(4)语音播放:当对方通过网络传输到本机时,将接收的字节内存存放在缓冲区中,当达到一定量的数据,就进行实时语音播放。 基于 C#的语音通信第 31 页 共 78 页4.4.2 语音聊天的模块设计思想(1)语音采集模块,其具体步骤如下:1. 设置 PCM 格式,设置相关的参数,如:采样频率、量化位数等。2. 建立采集用的设备对象,建立采集用的缓冲区对象。3. 设置缓冲区通知,设置通知被触发后的事件。通知是用于当缓冲区的读指针达到某预设位置时触发通知事件,提醒可以对某部分的数据进行传送。4. 开始采集声音。5. 当通知被触发后,建立一个新的线程来处理数据传送的事件。 (建立一个新的线程,就是为了防止采集过程被中断) 。(2)语音传输模块,大致流程如下: 1. 建立 socket 对象,在实例化这个对象的时候有一个参数是设置使用的协议,在本软件中,我采用的是 TCP。采用 TCP 在建立连接和维护连接中对时间和系统资源的有一定开销,会有一些时延发生。 2. 绑定本机的 IP 和端口,主机的 IP 地址为 01,端口我们约定默认为 8888。 3. 启动监听线程,来监听网络。采用异步的方式,以便获得更好的系统响应度。4. 发送语音数据。(3)语音播放模块,主要过程是:利用 MemoryStream 来代表这个接收缓冲区。设置两个表示指针位置的字段: 1.缓冲区指针private int iPositionWrite = 0;/内存流中写指针位移private int iPositionPlay = 0;/内存流中播放指针位移缓冲区是存放音频数据的地方,并且它还提供了我们两个指针:读指针和捕捉指针。它们的位置按照相对于缓冲区起始位置的偏移量计算。读指针位于当前已经被完全捕捉到缓冲区的数据末尾。捕捉指针位于当前将要从硬件中复制的数据块的末尾。如果想从缓冲区中读取数据,则只能从已经完全写入缓冲区的数据中读取,也就是说只能从偏移量小于 基于 C#的语音通信第 32 页 共 78 页读指针的地方读取。2. 缓冲区通知时间相同的音频文件,WAVE 文件会比其它格式的音频文件大得多,这是因为 WAVE 文件没有对数据进行压缩。如果录音的时候,不限制缓冲区大小,那么录制很短的时间可能就会占用很多内存,说不定不过多久,1G 内存就不够用了。因此必须对缓冲区的大小进行限制,而且当缓冲区满了之后,还可以重新从缓冲区起始处开始,用新的数据覆盖旧的数据。那旧的数据怎么办呢?如果不想丢失旧的数据,那就得在旧的数据被覆盖之前,将它转移到其它地方。 微软提供了我们一个解决办法:“通知” 。可以在缓冲区中的某些位置处设置通知,当读指针到达通知位置的时候,就会触发相应的事件执行转移操作。 当接收到数据后,则移动写指针,移动的长度为接收到的数据长度。利用一个字段表示通知大小:private int intNotifySize = 5000;当写指针的位置达到通知大小,则执行播放操作,然后移动播放指针到刚才的通知的位置。如果当前写指针的位移与将要写入到缓冲区的数据大小相加后超过缓冲容量的,则进行摩尔运算,实现循环的效果。模块框架实现如下(具体代码见附录):1.需要引用的命名空间和外部 dll。 两个外部 DLL 为:Microsoft.DirectX.dll 和 Microsoft.DirectX.DirectSound.dll2.用户变量 private string strRecSaveFile = string.Empty;/录音文件保存路径 private FileStream fsWav = null;/保存的文件流 private int iSampleSize = 0; private BinaryWriter mWriter = null; private WaveFormat mWaveFormat; private Notify myNotify;/缓冲区通知 private int iNotifyNum = 10;/通知个数 private int iNotifySize = 4410; private int iBufferOffSet = 0; private int iBufferSize = 0; private Capture capture; private CaptureBuffer captureBuffer; private AutoResetEvent notifyEvent; 基于 C#的语音通信第 33 页 共 78 页 private BufferDescription bufferDiscript; private SecondaryBuffer secondaryBuffer;/辅助缓冲区 private Device playDevice; private FormClient client; private IntPtr intptr;/窗口句柄 private MemoryStream memstream;/内存流 private Thread notifyThread;/通知线程3.设置 PCM 格式 WaveFormat format = new WaveFormat(); format.FormatTag = WaveFormatTag.Pcm;/设置音频类型 format.SamplesPerSecond = 11025;/采样率 format.BitsPerSample = 16;/采样位数 format.Channels = 1; format.BlockAlign = (short)(format.Channels * (format.BitsPerSample / 8);/单位采样点的字节数 format.AverageBytesPerSecond = format.BlockAlign * format.SamplesPerSecond; return format;4.创建 WAVE 文件,创建的 WAVE 文件可以在本地保存通信时的语音数据 5.建立两个对象,一个是捕捉设备对象CaptureDevicesCollection,另一个是播放设备对象DevicesCollection6.设置通知以及相应的事件,以便进行语音录制和等待缓冲区的通知消息7.开始捕捉,调用缓冲区的 START 方法就可以开始捕捉。8.结束捕捉并写入 WAV 文件尾4.4.3 语音通信发送端和接收端工作原理(1)建立 socket 对象,在实例化这个对象的时候有一个参数是设置使用的协议,在本软件中,采用的是 TCP。 (2) 绑定本机的 IP 和端口,IP 地址:01,选择绑定到本机可用的 IP地址,端口我们约定默认为 8888。 基于 C#的语音通信第 34 页 共 78 页 (3)启动监听线程,来监听网络。我采用异步的方式,以便获得更好的系统响应度。4.5 文本聊天与语音通信模块功能的综合设计在客户端界面中,文字聊天和语音通信的在同一个界面中,分别用不同的按钮控制和触发用户所需要的服务,当成功连接到服务器端之后,点击文字按钮即可开始发送文字信息,两个用户即可展开文字聊天;点击语音按钮即可开始语音通信,客户端发送数据的具体代码实现过程如下所示:if (clientSocket != null) ObjectState state = new ObjectState(); state.stateSocket = clientSocket; string content = Encoding.UTF8.GetString(buffer); if (content.Substring(0, 8) = wordData) content = content.Remove(0, 8); if (isFirst) isFirst = false; form.richTextBoxContent.Text = clientSocket.LocalEndPoint.ToString() + + DateTime.Now + :n + content; else form.richTextBoxContent.Text += n + clientSocket.LocalEndPoint.ToString() + + DateTime.Now + :n + content; /MessageBox.Show(Convert.ToString(buffer.Length); clientSocket.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, new 基于 C#的语音通信第 35 页 共 78 页AsyncCallback(SendCallback), state); sendDone.WaitOne(); 客户端接收数据的具体代码实现过程如下所示:while (true) if (clientSocket.Poll(5000, SelectMode.SelectRead) ObjectState state = new ObjectState(); state.stateSocket = clientSocket; bufferData = new byte50000; /bufferData = state.buffer; clientSocket.BeginReceive(state.buffer, 0, ObjectState.bufferSize, 0, new AsyncCallback(ReceiveCallback), state); 基于 C#的语音通信第 36 页 共 78 页第五章 主要问题及解决5.1 多线程问题因为本系统的客户端程序采用 P2P 模式,每个用户即可以是服务的发起端,又可以是服务的接收端,所以必须在程序加载时对网络进行监听,监听聊天连接请求和文字和语音传输连接请求,并能够对己方发起的事件进行处理,因而必须在此应用程序中使用多线程技术,耗时的任务可以在后台执行,而使应用程序窗口和控件保持响应。对于应用程序监听功能,多线程处理提供了用不同线程处理每个传入请求的能力。否则,在完全满足前一个请求之前,将无法处理其他的新的请求。在两个用户的通信过程中,任何一方断开连接之后,要像对方发送断开信号,而对方接收到传入的断开信号后,需断开Socket、NetStream 等,并重新开启监听线程,销毁接收消息线程。在开发的时候,经常因为线程的创建和销毁不当,而造成程序假死或者异常退出,却没有释放其占用的端口等资源。另外一个问题,程序退出的时候,未将线程处理好,同样造成资源占用,并且程序还驻留在内存中。在客户端发送语音数据需要用到通知事件,在 MSDN 中查找有关通知方面的资料,为能够正确的发送数据做好准备。测试客户端的每个功能的实现,期间修改了.NET 的版本,屏蔽了 Linq 库文件,增加了线程阻塞处理,以及线程的等待处理。通过查阅资料,掌握了 C#中在 winform 关闭时需要进行的操作:1、接收线程需要关闭;2、监听需要关闭;3、Socket 需要关闭;4、网络流需要关闭;5、如果是传文件的话还需要关闭流文件。在后来经过不断的修改和大量的测试,终于将这个 bug 解决了。通过对这个问题的解决,掌握了 C#中的使用多线程时,线程的创建、相互协调和销毁等技术。5.2 套接字异常在文本聊天模块测试时,通过在两个用户间连续地连接、断开,出现 Socket 异常,因为 Socket 在使用时,一个 IPEndpoint 只能使用一次,如果本地在使用某个 IPEndpoint,远程主机断开连接后,本地并没有释放此 Socket,当远程主机再次尝试连接此 IPEndpoint时,将出现 Socket 异常。解决的办法就是在断开连接的时候,向对方发送“断开”信号,对方接收到断开信号后,将其自身的 Socket 释放掉。在调试语音通信时,当时对套接字不是很清楚,在服务器端只创建了一个套接字socket,进行语音通信时,总是无法传输语音,查阅有关 socket 的资料才知道,在通信时,必须有两个套接字 socket,一个是服务端用来负责监听客户端发来连接请求,但不负责与请求的客户端通信,将连接请求的套接字赋给另一个负责通信的套接字,即每当服务器端 基于 C#的语音通信第 37 页 共 78 页成功接收到客户端时,但在服务器端创建一个用与请求的客户端进行通信的 socket,该socket 完成客户端之间的语音通信。5.3 网络流异常消息的传递离不开网络流操作,在编写文本聊天模块时,碰到网络流异常。当互相连接的两个用户之间,任意一方在退出的时候,需要向对方发送“断开”信号,并随后关闭其自身的网络流,而对方在接收到“断开”信号的时候,有个处理过程,但是流却已经关闭了,所以产生流异常, “本地主机尝试读取一个不存在的流” ,通过修改,对网络流使用try catch 操作后,问题解决。在语音通信模块测试时,接收端接收到的语音缓存达到一定量时就进行播放,由于设置过大或者过小对播放出的语音都会有影响,或者直接就会出现播放异常,经过几天的一步一步调试和改变通知大小,将噪声降到最低,才得到令人满意的结果。 基于 C#的语音通信第 38 页 共 78 页结 论经过这次对通信系统软件的开发与设计,学到了很多知识,也对软件开发的具体步骤有了更进一步的了解。该系统采用 C/S 结构,基于.NET 开发环境,用 C#语言进行编译,在开发与设计这个聊天系统软件时,对.NET 开发环境有基本的认识,并基本学会运用 C#的语言编译简单的程序。在编写程序的过程中,我也遇到了很多的问题。实习期间通过同事的讲解和指导,开始对所做东西的框架有了基本的了解和认识,一步步开始完成毕业设计任务,回到学校,有老师的指点,请教同学,自己查阅资料等方式,使自己的问题和功能得到了解决和完善,并圆满完成了整个程序的开发工作,同时积累了许多解决经验。这次的毕业设计达到了预期的目的,实现了文字聊天和语音视频聊天。由于囧于技术水平,尝试 N 次之后,仍旧不是很清楚如何才能正确地实现语音效果(如回声消除、降噪等)来保障音质,语音聊天基本能够传送语音,但是语音的质量不是太好,噪音不小。因此在单机测试会有回声干扰,嚣叫声比较严重,希望高手解囊。通过这次毕业设计,使我从理论到实践迈出了坚实的一步。在学习理论、分析和组织程序结构以及具体的实现等整个过程中,我体会到了编写程序的酸、甜、苦、辣。要编写出一个好的程序,必须要有缜密的思维,谨慎的作风和坚忍不拔的毅力。 基于 C#的语音通信第 39 页 共 78 页结 束 语初次编写软件,因为对语言和编译环境缺乏认识和接触,自己在编程方面的经验也不足,无法对布置的课题做出正确的分析和理解,对 socket 和客户端服务器的陌生,让我走了很多弯路,浪费了不少时间。但是在实习期间可以请教其他人,让我对语音通信的整个框架有了一定的认识,在重复几次的编写代码后,开始慢慢的走上正轨,分模块来实现所需要的功能,并通过这次的实际操作编码学到的很多东西,也在这期间深入的了解到不少实际编程的理念,使我受益匪浅。本文在阅读了C#入门经典书籍,查找了大量相关文献和网上资料的基础上,自己尝试做了一个类似聊天的系统。本文首先就课题背景,研究此课题的目的和意义,国内外概况等做了比较详细的介绍;其次,对此系统做了简单的设计分析,分服务器端和客户端分别介绍;最后,再对系统做详细的分析说明。由于学习 C#语言的时间并不长,接触到 Microsoft Visual Studio 开始写相关的代码也很少, 所以做出来的程序不是十分完善,虽然我采用的编译器有大量的控件,但是其中的逻辑思维和一些知识还是得自己从零开始学习,慢慢的我已经对所做的通信系统框架已经很熟悉,对运用的知识也掌握到位。现在来看,可视化编程虽然降低了代码编写的能力,但对逻辑思维还是有很高的要求的,需要对基本知识熟练掌握。做出来的系统虽然在网上有类似的作品,但我的课题从一开始都是我自己一行一行敲的代码,期间程序出现过很大的错误,自己也调试了两周,终于在实习开药结束的时候完成了课题,由于的我的水平有限,知识掌握不足,其中还有很多不足值得改进的地方但是还基本上符合要求,我想通过以后的逐渐学习和积累,再做系统时一定会界面更美观,功能更强大,操作更方便。 基于 C#的语音通信第 40 页 共 78 页致 谢四年的大学学习生涯就要结束了,首先我要向我的大学长安大学特别是信息工程学院的领导和老师致以深深的谢意。在这里我度过了人生中一段令人难忘的日子。回头展望,酸甜苦辣都曾经有过,大学四年自己成熟了一些,也发现了自己的诸多不足,这四年充实了我的人生。真诚感谢理学院的领导和老师,他们在我平常的学习与日常生活中,给了我许多关心与帮助。我非常感谢理学院的各级领导对于我们学习的支持,为我们创造了良好的学习环境和学术氛围。在设计的完成中,还有很多同学给与我很多帮助、意见和建议,在这里我表示对他们的感谢。衷心感谢陕西科技开发咨询公司江苏办事处给我提供的学习环境。在办事处的实习使得我在专业知识方面大大的提高,让我深入学习了 C#语言和 Windows 程序设计等各方面的知识,使我从一个对软件知识只是一知半解的状态到能够很熟练的运用。在论文即将完成之际,我的心情无法平静,从开始进入课题到论文的顺利完成,有多少可敬的师长、同学、朋友给了我无言的帮助,在这里请接受我诚挚的谢意!天下没有不散的宴席,虽然大四的生活多半时间还是呆在学校里,但是论文致谢语写就的那一刻也真正标志着我与这所学校就此别离了,没有伤感,更多的是遗憾,但是总归不如意事十有八九,过去的不能挽回,人应该大胆向前看,所以这段文字应该像它的标题一样充满感恩和致谢,感谢四年来在我的成长道路上扶持过我,指点过我的人。这篇论文所涉及的议题是是在实习期间修订的,在前期的实习积累经验,到中期的修改和讨论,及最后的反复斟酌,我希望能尽自己最大的努力,写出一篇具有现实意义的论文。但是在具体实施的过程中,我还是遇到了相当多当初没有预料的困难,也曾经令我迷茫和彷徨,论文最终的定稿,也没有我当初设想的那么完美,但是总归是自己尽力完成的著作,和我的每一篇球评一样都是我心血的累积。论文得以顺利完成,要感谢的人实在太多了。首先要衷心地感谢我的指导老师高涛老师,您严谨的治学态度,开阔的思维,循循善诱的指导一直给我很大的帮助。当我对论文的思路感到迷茫时,您为我理清思路,指导我往一条比较清晰的思路上进行修改。在论文的不断修改中,我也努力做到及时积极地跟胡老师交流,因为我觉得这样可以使得我的论文更加完善。在这里还要深深的对您说上一句抱歉,因为我的懒散和懈怠,令您费尽苦心并且几近失望。论文的最终完成,也是一波三折。在不断完善和修改的过程中,也让我更加懂得“一分耕耘才有一分收获”的道理。再次对您表示感谢,师恩伟大,无以回报。然后还要感谢所有在大学期间传授我知识的老师,每一位老师的悉心教导都是我完成这篇论文的基础。因我知识和学识有限,设计和论文中不当之处,还望各位老师和同学给予指正和建议, 基于 C#的语音通信第 41 页 共 78 页不胜感激。感谢所有和我在一起做毕业设计的同学,感谢他们的指点与帮助。 参考文献1 马俊、何欣,C#网络编程及应用M,机械工业出版社,2008 年. 基于 C#的语音通信第 42 页 共 78 页2 唐政、房大伟,C#项目开发全程实录M,清华大学出版社,2008 年.3 张跃廷、王小科,C#程序开发范例宝典M,人民邮电出版社,2007 年.4 Anthony Jones,Windows 网络编程M.清华大学出版社,2004 年.5 谢希仁,计算机网络(第 4 版)M,电子工业出版社,2006 年.6 张海藩,软件工程(第二版)M,人民邮电出版社,2006 年.7 Ramadas Shanmugam、R.Padmini、S.Nivedita,Special Edition Using TCP/IP Second EditionM,NIIT,2002 年.8 刘洪林,蒋昌茂,张建永,IP 语音通信原理、设计及组网应用,电子工业出 版社,2009 年.9 王凯明,C#下用 P2P 技术实现点对点聊天EB/OL,/s/blog_6c9a9a870100m2d8.html,2009 年.10 (美)库柏,C#设计模式M,电子工业出版社,2003 年.11 冉林仓,尹建民,Visual C#.NET 入门与进阶M,清华大学出版社,2007 年.12 (美)Karli Watson ,Christian Nagel 等著,齐立波 翻译 ,C#入门经典(第五版) ,清华大学出版社,2010 年.13 (美)Christian Nagel,Bill Evjen,Jay Glynn 等著,李铭 翻译,C#高级编程(第七版) ,清华大学出版社,2010 年.附 录客户端注册用户代码:using System;using System.Collections.Generic;using System.ComponentModel; 基于 C#的语音通信第 43 页 共 78 页using System.Data;using System.Drawing;using System.Text;using System.Windows.Forms;using System.Data.SqlClient;using System.Data.OleDb;using System.Reflection;namespace VoiceCommunication public partial class EnrollForm : Form public EnrollForm() InitializeComponent(); private void buttonEnroll_Click(object sender, EventArgs e) string userName = textBoxUserName.Text.Trim(); string userPassword = textBoxUserPassword.Text.Trim(); string userPasswordRepetition = textBoxUserPasswordRepetition.Text.Trim(); if(userName != & userPassword != & userPasswordRepetition != ) if (string.Compare(userPassword,userPasswordRepetition) = 0) string consqlserver = Data Source = GUO-PCSQLEXPRESS;Initial Catalog = VoiceCommunication;Integrated Security = True; SqlConnection con = new SqlConnection(consqlserver); con.Open(); string sql = select * from userInformations where userName = + userName + and userPassword = + userPassword + ; SqlCommand com = new SqlCommand(sql,con); SqlDataReader sdr = com.ExecuteReader(); sdr.Read(); if (sdr.HasRows) MessageBox.Show(该用户已被注册,请重新注册!, 提示); else 基于 C#的语音通信第 44 页 共 78 页 sdr.Close(); string insertUser = insert into userInformations(userName,userPassword) values ( + userName + , + userPassword + ); SqlCommand insertCom = new SqlCommand(insertUser,con); insertCom.ExecuteNonQuery();/执行插入语句 con.Close(); con.Dispose(); MessageBox.Show(注册成功!,提示); else MessageBox.Show(密码不一致!,提示); else if (userName = ) MessageBox.Show(用户名不能为空!,提示); else if(userPassword = ) MessageBox.Show(密码不能为空!, 提示); else if(userPasswordRepetition = ) MessageBox.Show(重复密码不能为空!, 提示); private void buttonCancel_Click(object sender, EventArgs e) this.Close(); 登录代码:using System;using System.Collections.Generic; 基于 C#的语音通信第 45 页 共 78 页using System.ComponentModel;using System.Data;using System.Drawing;using System.Text;using System.Windows.Forms;using System.Data.SqlClient;using System.Reflection;namespace VoiceCommunication public delegate void nameDelegate(string str); public partial class Login : Form public Login() InitializeComponent(); public nameDelegate OnName get; set; private void buttonLogin_Click(object sender, EventArgs e) string userName = textBoxUserName.Text.Trim(); string userPassword = textBoxPassword.Text.Trim(); if (userName != & userPassword != ) /定义数据库连接语句:服务器=.(本地) 数据库名=VoiceCommunication string consqlserver = Data Source = GUO-PCSQLEXPRESS;Initial Catalog = VoiceCommunication;Integrated Security = True; /定义 SQL 查询语句:用户名 密码 string sql = select * from userInformations where userName = + userName + and userPassword = + userPassword + ; /定义 SQL Server 连接对象 SqlConnection connection = new SqlConnection(consqlserver); /打开数据库 connection.Open(); /定义查询命令:表示对数据库执行一个 SQL 语句或存储过程 基于 C#的语音通信第 46 页 共 78 页 SqlCommand com = new SqlCommand(sql, connection); /执行查询:提供一种读取数据库行的方式 SqlDataReader sread = com.ExecuteReader(); try if (sread.Read()/如果存在用户名和密码正确数据执行进入系统操作 DataInformationForm dataForm = new DataInformationForm(); dataForm.Show(); MessageBox.Show(登录成功,提示); /if(OnName != null) / / OnName(userName); / FormClient client = new FormClient(); client.Text = userName; client.Show(); this.Hide(); /this.DialogResult = DialogResult.OK; /this.Close(); else MessageBox.Show(用户名或者密码错误); catch (Exception ex) throw new Exception(ex.ToString();/处理异常信息 finally connection.Close();/关闭连接 connection.Dispose();/释放连接 sread.Dispose();/释放资源 else if (userName = ) 基于 C#的语音通信第 47 页 共 78 页 MessageBox.Show(用户名不能为空); else if (userPassword = ) MessageBox.Show(密码不能为空); private void buttonExit_Click(object sender, EventArgs e) Application.Exit(); private void buttonEnroll_Click(object sender, EventArgs e) EnrollForm enroll = new EnrollForm(); enroll.Show(); 主界面代码:using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;/using System.Linq;using System.Text;using System.Windows.Forms;using System.IO;using System.Threading;using System.Net;namespace VoiceCommunication public partial class FormClient : Form private SocketClient client; private VoiceInit record; private bool btnConnectionState = true; 基于 C#的语音通信第 48 页 共 78 页 private bool btnVoiceState = true; public FormClient() InitializeComponent(); client = new SocketClient(this); TextBox.CheckForIllegalCrossThreadCalls = false; /private void MyName(string name) / / MessageBox.Show(name); / this.Text = name; / /private void FormClient_Load(object sender, EventArgs e) / / Login user = new Login(); / user.OnName = MyName; / /连接服务器端 private void buttonConnectServer_Click(object sender, EventArgs e) if (btnConnectionState) try IPAddress ip = IPAddress.Parse(textBoxIPInput.Text.Trim();/获取文本框中写入的 ip 地址 if (textBoxIPInput.Text.Trim() != null & textBoxPort.Text.Trim() != null) /传参数,IP 地址和端口 client.ConnectServer(ip, textBoxPort.Text.Trim(); else return; btnConnectionState = false; 基于 C#的语音通信第 49 页 共 78 页 buttonSendWord.Enabled = true; buttonSendVoice.Enabled = true; buttonConnectServer.Text = 断开服务器; catch (Exception ex) if (MessageBox.Show(ex.Message + n 是否重启程序?, 提示, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question) = DialogResult.Yes) Application.Exit(); System.Diagnostics.Process.Start(System.Reflection.Assembly.GetExecutingAssembly().Location); else client.DisconnectServer(); btnConnectionState = true; buttonSendWord.Enabled = false; buttonSendVoice.Enabled = false; buttonConnectServer.Text = 连接服务器; MessageBox.Show(已断开服务器连接!, 客户端提示); /发送文字数据 private void buttonSendWord_Click(object sender, EventArgs e) if (buttonSendWord.Text = 发送文字) try string strWord = richTextBoxText.Text; byte wordData = Encoding.UTF8.GetBytes(wordData + strWord); client.OnSendData(wordData);/调用套接字发送函数 catch (Exception ex) MessageBox.Show(发送文字数据异常! + ex.Message, 客户端 基于 C#的语音通信第 50 页 共 78 页提示); /发送语音数据 private void buttonSendVoice_Click(object sender, EventArgs e) if (btnVoiceState) btnVoiceState = false; buttonSendVoice.Text = 结束语音; try string wavFile = C:record.wav;/创建保存文件目录 if (File.Exists(wavFile) File.Delete(wavFile); MessageBox.Show(语音聊天开始, 客户端提示); record = new VoiceInit(); record.Intptr = this.Handle; record.CreatWaveFile(wavFile);/创建 wave 文件 record.CreatCaptureDevice();/初始化捕捉设备 record.CreatCaptureBuffer();/初始化缓冲区 record.CreatNotification();/发送数据 record.CreatPlayDevice();/初始化播放设备 record.CreatSecondaryCaptureBffer();/初始化辅助缓冲区 record.StartVoiceCapture();/开始采集数据 client.PlayVoice = record; record.OnInfo = MyInfo;/声音采集传递一段语音内存 buffer catch (Exception ex) MessageBox.Show(发送语音数据异常! + ex.Message, 客户端提示); else btnVoiceState = true; 基于 C#的语音通信第 51 页 共 78 页 buttonSendVoice.Text = 发送语音; MessageBox.Show(语音聊天结束!, 客户端提示); /client.StopThread(); record.StopVoiceCapture(); /发送语音数据的方法 private void MyInfo(byte buffer) client.OnSendData(buffer); private void FormClient_FormClosing(object sender, FormClosingEventArgs e) client.DisconnectServer(); 通信 socket 代码:using System;using System.Collections.Generic;/using System.Linq;using System.Text;using System.Net;using System.Net.Sockets;using System.Threading;using System.Windows.Forms;namespace VoiceCommunication class SocketClient private Socket clientSocket; private Thread clientThread; private FormClient form; private VoiceInit playVoice; 基于 C#的语音通信第 52 页 共 78 页 private ManualResetEvent connectionDone = new ManualResetEvent(false); private ManualResetEvent receiveDone = new ManualResetEvent(false); private ManualResetEvent sendDone = new ManualResetEvent(false); public VoiceInit PlayVoice set playVoice = value; public SocketClient(FormClient mainForm) form = mainForm; /连接服务器 public void ConnectServer(IPAddress ip, string strPort) IPEndPoint remoteEndPoint = new IPEndPoint(ip, Convert.ToInt32(strPort); clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); ObjectState state = new ObjectState(); state.stateSocket = clientSocket; /连接服务器 clientSocket.BeginConnect(remoteEndPoint, new AsyncCallback(ConnectionCallback), state); connectionDone.WaitOne(); /创建接收数据线程 clientThread = new Thread(new ThreadStart(OnReceive); clientThread.IsBackground = true; clientThread.Start(); /断开服务器 public void DisconnectServer() if (clientThread != null) clientThread.Abort(); 基于 C#的语音通信第 53 页 共 78 页 if (clientSocket != null) clientSocket.Shutdown(SocketShutdown.Both); clientSocket.Close(); /连接回调函数 private void ConnectionCallback(IAsyncResult iar) try ObjectState state = (ObjectState)iar.AsyncState; Socket s = state.stateSocket; s.EndConnect(iar); MessageBox.Show(连接服务器成功!, 客户端提示); connectionDone.Set(); catch (Exception ex) if (MessageBox.Show(连接服务器失败!n + ex.Message + 是否重新启动程序?, 客户端提示, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question) = DialogResult.Yes) Application.Exit(); System.Diagnostics.Process.Start(System.Reflection.Assembly.GetExecutingAssembly().Location); return; /接收函数 private void OnReceive() while (true) if (clientSocket.Poll(5000, SelectMode.SelectRead) ObjectState state = new ObjectState(); 基于 C#的语音通信第 54 页 共 78 页 state.stateSocket = clientSocket; bufferData = new byte50000; /bufferData = state.buffer; clientSocket.BeginReceive(state.buffer, 0, ObjectState.bufferSize, 0, new AsyncCallback(ReceiveCallback), state); /clientSocket.BeginReceive(bufferData, 0, bufferData.Length, 0, new AsyncCallback(ReceiveCallback), state); /ObjectState state = new ObjectState(); /state.stateSocket = clientSocket; /bufferData = new byte50000; /clientSocket.BeginReceive(state.buffer, 0, ObjectState.bufferSize, 0, new AsyncCallback(ReceiveCallback), state); /clientSocket.BeginReceive(bufferData, 0, bufferData.Length, 0, new AsyncCallback(ReceiveCallback), state); /receiveDone.WaitOne(); /接收回调函数 private byte bufferData; private void ReceiveCallback(IAsyncResult iar) try ObjectState state = (ObjectState)iar.AsyncState; Socket s = state.stateSocket; int iRecv = s.EndReceive(iar); /MessageBox.Show(Convert.ToString(bufferData.Length), 接收到的数据); /bufferData = state.buffer; string content = Encoding.UTF8.GetString(state.buffer); /判断接收到的数据是文字数据还是语音数据,对文字数据进行了编解码 if (content.Substring(0,8) = wordData)/判断是否有文字接收的标志 ReceiveWordData(content); else/语音接收 基于 C#的语音通信第 55 页 共 78 页 if (iRecv 0) byte byteBufferData = new byteiRecv; Buffer.BlockCopy(state.buffer, 0, byteBufferData, 0, iRecv); playVoice.GetVoiceData(iRecv, byteBufferData);/调用声音模块中的 GetVoiceData 来从字节数组中获取声音并播放 /ReceiveVoicedata(iRecv); /receiveDone.Set(); catch (Exception ex) MessageBox.Show(接收数据异常!1111111111111111n + ex.Message, 客户端提示); /文字接收 private static bool isFirst = true; private void ReceiveWordData(string content) content = content.Remove(0, 8); if (isFirst) isFirst = false; form.richTextBoxContent.Text = clientSocket.LocalEndPoint.ToString() + + DateTime.Now + :n + content; else form.richTextBoxContent.Text += n + clientSocket.LocalEndPoint.ToString() + + DateTime.Now + :n + content; /语音接收 /private void ReceiveVoicedata(int iRecv) / / /结束数据完成开始播放 buffer / if (iRecv 0) 基于 C#的语音通信第 56 页 共 78 页 / / byte byteBufferData = new byteiRecv; / Buffer.BlockCopy(bufferData, 0, byteBufferData, 0, iRecv); / /调用声音模块中的 GetVoiceData 来从字节数组中获取声音并播放 / playVoice.GetVoiceData(iRecv, byteBufferData); / / /发送数据 public void OnSendData(byte buffer) if (clientSocket != null) ObjectState state = new ObjectState(); state.stateSocket = clientSocket; string content = Encoding.UTF8.GetString(buffer); if (content.Substring(0, 8) = wordData) content = content.Remove(0, 8); if (isFirst) isFirst = false; form.richTextBoxContent.Text = clientSocket.LocalEndPoint.ToString() + + DateTime.Now + :n + content; else form.richTextBoxContent.Text += n + clientSocket.LocalEndPoint.ToString() + + DateTime.Now + :n + content; /MessageBox.Show(Convert.ToString(buffer.Length); clientSocket.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(SendCallback), state); sendDone.WaitOne(); /发送数据回调函数 private void SendCallback(IAsyncResult iar) try 基于 C#的语音通信第 57 页 共 78 页 ObjectState state = (ObjectState)iar.AsyncState; Socket s = state.stateSocket; int iSend = s.EndSend(iar); /MessageBox.Show(Convert.ToString(iSend), 发送的数据); sendDone.Set(); catch (Exception ex) MessageBox.Show(发送数据异常!n + ex.Message, 客户端提示); public void StopThread() if(clientThread != null & clientThread.IsAlive = true) clientThread.Abort(); /if(clientSocket != null) / / clientSocket.Close(); / 初始化设备代码:using System;using System.Collections.Generic;/using System.Linq;using System.Text;using Microsoft.DirectX;using Microsoft.DirectX.DirectSound;using System.IO;using System.Windows.Forms;using System.Threading;using System.Net;using System.Net.Sockets;using System.Media; 基于 C#的语音通信第 58 页 共 78 页namespace VoiceCommunication public delegate void InfoDelegate(byte buffer); class VoiceInit private string strRecSaveFile = string.Empty;/录音文件保存路径 private FileStream fsWav = null;/保存的文件流 private int iSampleSize = 0; private BinaryWriter mWriter = null; private WaveFormat mWaveFormat; private Notify myNotify;/缓冲区通知 private int iNotifyNum = 10;/通知个数 private int iNotifySize = 4410; private int iBufferOffSet = 0; private int iBufferSize = 0; private Capture capture; private CaptureBuffer captureBuffer; private AutoResetEvent notifyEvent; private BufferDescription bufferDiscript; private SecondaryBuffer secondaryBuffer;/辅助缓冲区 private Device playDevice; private FormClient client; private IntPtr intptr;/窗口句柄 private MemoryStream memstream;/内存流 private Thread notifyThread;/通知线程 public IntPtr Intptr set intptr = value; /委托传值 public InfoDelegate OnInfo get; set; 基于 C#的语音通信第 59 页 共 78 页 /创建捕捉设备对象 public void CreatCaptureDevice() CaptureDevicesCollection captureDevice = new CaptureDevicesCollection(); Guid deviceGuid; if (captureDevice.Count 0) deviceGuid = captureDevice0.DriverGuid; else MessageBox.Show(当前没有可用于音频捕捉的设备, 系统提示); return; capture = new Capture(deviceGuid); /创建捕捉缓冲区 public void CreatCaptureBuffer() WaveFormat mWaveFormat = SetWaveFormat(); CaptureBufferDescription bufferDescription = new CaptureBufferDescription(); bufferDescription.Format = mWaveFormat; iNotifySize = mWaveFormat.AverageBytesPerSecond / iNotifyNum;/1 秒的数据量/设置的通知数得到的每个通知大小小于 0.2s 的数据量,话音延迟小于 200ms 为优质话音 iBufferSize = iNotifyNum * iNotifySize; bufferDescription.BufferBytes = iBufferSize; bufferDescription.ControlEffects = true; bufferDescription.WaveMapped = true; captureBuffer = new CaptureBuffer(bufferDescription, capture); byte byteMemory = new byte100000; memstream = new MemoryStream(byteMemory, 0, 100000, true, true); /设置 PCM 格式 private WaveFormat SetWaveFormat() WaveFormat format = new WaveFormat(); 基于 C#的语音通信第 60 页 共 78 页 format.FormatTag = WaveFormatTag.Pcm;/设置音频类型 format.SamplesPerSecond = 11025;/采样率 format.BitsPerSample = 16;/采样位数 format.Channels = 1; format.BlockAlign = (short)(format.Channels * (format.BitsPerSample / 8);/单位采样点的字节数 format.AverageBytesPerSecond = format.BlockAlign * format.SamplesPerSecond; return format; /创建播放设备对象 public void CreatPlayDevice() DevicesCollection deviceCollection = new DevicesCollection(); Guid guid; if (deviceCollection.Count 0) guid = deviceCollection0.DriverGuid; else throw new Exception(); return; playDevice = new Device(guid); playDevice.SetCooperativeLevel(intptr, CooperativeLevel.Normal); /创建辅助缓冲区 public void CreatSecondaryCaptureBffer() bufferDiscript = new BufferDescription(); WaveFormat mWaveFormat = SetWaveFormat(); bufferDiscript.Format = mWaveFormat; iNotifySize = mWaveFormat.AverageBytesPerSecond / iNotifyNum;/设置通知大小 iBufferSize = iNotifyNum * iNotifySize; bufferDiscript.BufferBytes = iBufferSize; bufferDiscript.ControlPan = true; bufferDiscript.ControlFrequency = true; bufferDiscript.ControlVolume = true; 基于 C#的语音通信第 61 页 共 78 页 bufferDiscript.GlobalFocus = true; secondaryBuffer = new SecondaryBuffer(bufferDiscript, playDevice); /设置通知 public void CreatNotification() BufferPositionNotify bpn = new BufferPositionNotifyiNotifyNum;/设置缓冲区通知个数 notifyEvent = new AutoResetEvent(false);/设置通知事件 notifyThread = new Thread(RecordData);/通知触发事件 notifyThread.IsBackground = true; notifyThread.Start(); for (int i = 0; i iNotifyNum; i+) bpni.Offset = iNotifySize + i * iNotifySize - 1;/设置通知位置 bpni.EventNotifyHandle = notifyEvent.Handle; myNotify = new Notify(captureBuffer); myNotify.SetNotificationPositions(bpn); /线程中的事件 private void RecordData() while (true) notifyEvent.WaitOne(Timeout.Infinite, true);/ 等待缓冲区的通知消息 RecordCapturedData();/ 录制数据 /数据传输 private void RecordCapturedData() byte captureData = null; int readpos = 0; int capturepos = 0; int locksize = 0; captureBuffer.GetCurrentPosition(out capturepos, out readpos); 基于 C#的语音通信第 62 页 共 78 页 locksize = readpos - iBufferOffSet;/读取数据大小 if (locksize = 0) return; else if (locksize 0) /因为使用的是循环缓冲区,所以有一种情况下为负:当文以载读指针回到第一个通知点,而 Ibuffeoffset 还在最后一个通知处 locksize += iBufferSize; captureData = (byte)captureBuffer.Read(iBufferOffSet, typeof(byte), LockFlag.FromWriteCursor, locksize); /MessageBox.Show(Convert.ToString(captureData.Length), 发送的数据); try /发送语音数据,传送 buffer,调用 socket 套接字通信 if (OnInfo != null) OnInfo(captureData); catch throw new Exception(); mWriter.Write(captureData, 0, captureData.Length);/写入文件 iSampleSize += captureData.Length; iBufferOffSet += captureData.Length; iBufferOffSet %= iBufferSize;/取模是因为缓冲区是循环的 private int iPositionWrite = 0;/内存流中写指针位移 private int iPositionPlay = 0;/内存流中播放指针位移 private int intNotifySize = 5000;/设置通知大小 /从字节数组中获取音频数据,并进行播放 public void GetVoiceData(int iRecv, byte byteRecv) /string fileBuffer = receiveVoice.wav; /CreatWaveFile(fileBuffer); /string path = D:record.wav; 基于 C#的语音通信第 63 页 共 78 页 /SoundPlayer player = new SoundPlayer(path); /player.Play(); /iPositionWrite 指示最新的数据写好后的末尾。iPositionPlay 指示本次播放开始的位置 if (iPositionWrite + iRecv = 0 & iPositionWrite - iPositionPlay intNotifySize) | (iPositionWrite - iPositionPlay 0 & iPositionWrite - iPositionPlay + memstream.Capacity intNotifySize) if (memstream.Position + iRecv = 0)/存储一定量的数据,当达到一定数据量时就播放声音 bufferDiscript.BufferBytes = iPositionWrite - iPositionPlay;/缓冲区大小为播放指针到写指针之间的距离 SecondaryBuffer sec = new SecondaryBuffer(bufferDiscript, playDevice);/建立一个合适的缓冲区用于播放这段数据 memstream.Position = iPositionPlay;/先将 memstream 的指针定位到这一次播放开始的位置 /MessageBox.Show(iPositionWrite - iPositionPlay).ToString(); /sec.Write(0, memstream, iPositionWrite - iPositionPlay, LockFlag.FromWriteCursor); sec.Write(0, memstream, intNotifySize, LockFlag.FromWriteCursor); sec.Play(0, BufferPlayFlags.Default); memstream.Position = iPositionWrite;/写完后重新将memstream 的指针定位到将要写下去的位置 iPositionPlay = iPositionWrite; else if (iPositionWrite - iPositionPlay 0) bufferDiscript.BufferBytes = iPositionWrite - iPositionPlay 基于 C#的语音通信第 64 页 共 78 页+ memstream.Capacity;/缓冲区大小为播放指针到写指针之间的距离 SecondaryBuffer sec = new SecondaryBuffer(bufferDiscript, playDevice);/建立一个合适的缓冲区用于播放这段数据 memstream.Position = iPositionPlay; /sec.Write(0, memstream, memstream.Capacity - iPositionPlay, LockFlag.FromWriteCursor); sec.Write(0, memstream, intNotifySize, LockFlag.FromWriteCursor); memstream.Position = 0; sec.Write(memstream.Capacity - iPositionPlay, memstream, iPositionWrite, LockFlag.FromWriteCursor); /sec.Write(memstream.Capacity - iPositionPlay, memstream, 5000, LockFlag.FromWriteCursor); sec.Play(0, BufferPlayFlags.Default); memstream.Position = iPositionWrite; iPositionPlay = iPositionWrite; else /当数据将要大于 memstream 可容纳的大小时 int iReadStream = memstream.Capacity - iPositionWrite;/memstream 中剩下的可容纳的字节数 memstream.Write(byteRecv, 0, iReadStream);/先写完这个内存流 memstream.Position = 0;/然后让新的数据从 memstream 的 0 位置开始记录 memstream.Write(byteRecv, iReadStream, iRecv - iReadStream);/覆盖旧的数据 iPositionWrite = iRecv - iReadStream;/更新写指针位置 /创建 WAVE 文件 public void CreatWaveFile(string strFileName) fsWav = new FileStream(strFileName, FileMode.CreateNew); mWriter = new BinaryWriter(fsWav); char ChunkRiff = R, I, F, F ; char ChunkTpye = W, A, V, E ; char ChunkFmt = f, m, t, ; char ChunkData = d, a, t, a ; 基于 C#的语音通信第 65 页 共 78 页 short shPad = 1; int iLength = 0; int iFormatChunkLength = 0x10; short shBytesPerSample = 0; / 一个样本点的字节数目 if (mWaveFormat.BitsPerSample = 8 & mWaveFormat.Channels = 1) shBytesPerSample = 1; else if (mWaveFormat.BitsPerSample = 8 & mWaveFormat.Channels = 2) | (mWaveFormat.BitsPerSample = 16 & mWaveFormat.Channels = 1) shBytesPerSample = 2; else if (mWaveFormat.BitsPerSample = 16 & mWaveFormat.Channels = 2) shBytesPerSample = 4; / RIFF 块 mWriter.Write(ChunkRiff); mWriter.Write(iLength); mWriter.Write(ChunkTpye); / WAVE 块 mWriter.Write(ChunkFmt); mWriter.Write(iFormatChunkLength); mWriter.Write(shPad); mWriter.Write(mWaveFormat.Channels); mWriter.Write(mWaveFormat.SamplesPerSecond); mWriter.Write(mWaveFormat.AverageBytesPerSecond); mWriter.Write(shBytesPerSample); mWriter.Write(mWaveFormat.BitsPerSample); / 数据块 mWriter.Write(ChunkData); mWriter.Write(int)0); /开始语音采集 public void StartVoiceCapture() captureBuffer.Start(true);/true 表示设置缓冲区为循环方式,开始捕捉 基于 C#的语音通信第 66 页 共 78 页 /停止语音采集,将录音存入 WAV 文件 public void StopVoiceCapture() if(captureBuffer != null) captureBuffer.Stop();/调用缓冲区的停止方法,停止采集声音 if (notifyEvent != null) notifyEvent.Set();/关闭通知 if (notifyThread != null & notifyThread.I
温馨提示:
1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
2: 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
3.本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
提示  人人文库网所有资源均是用户自行上传分享,仅供网友学习交流,未经上传用户书面授权,请勿作他用。
关于本文
本文标题:基于C#的语音通信
链接地址:https://www.renrendoc.com/p-36648488.html

官方联系方式

2:不支持迅雷下载,请使用浏览器下载   
3:不支持QQ浏览器下载,请用其他浏览器   
4:下载后的文档和图纸-无水印   
5:文档经过压缩,下载后原文更清晰   
关于我们 - 网站声明 - 网站地图 - 资源地图 - 友情链接 - 网站客服 - 联系我们

网站客服QQ:2881952447     

copyright@ 2020-2025  renrendoc.com 人人文库版权所有   联系电话:400-852-1180

备案号:蜀ICP备2022000484号-2       经营许可证: 川B2-20220663       公网安备川公网安备: 51019002004831号

本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知人人文库网,我们立即给予删除!