基于TCP的语音聊天通信系统.docx
- 文档编号:5877946
- 上传时间:2023-01-01
- 格式:DOCX
- 页数:15
- 大小:175.28KB
基于TCP的语音聊天通信系统.docx
《基于TCP的语音聊天通信系统.docx》由会员分享,可在线阅读,更多相关《基于TCP的语音聊天通信系统.docx(15页珍藏版)》请在冰豆网上搜索。
基于TCP的语音聊天通信系统
一、实验综述
1.1设计目标
网络通信程序使用基于MFC类库中的Socket(套接字)进行编程。
通信双方均有各自的套接字,且该套接字与特定的IP地址和端口号相关联。
本实验采用基于可靠连接的C/S编程模式,对文字聊天程序进行扩充,在基本框架不变的基础上,增加相应的声音采集和播放的部分,并进行相应修改,从而实现语音聊天通信系统。
1.2完成功能
(1)建立服务器;
(2)连接服务器;
(3)文字聊天;
(4)语音聊天;
(5)双方分别既可作客户端,又可作服务器。
程序界面如下:
图1语音聊天系统界面
二、系统设计和整体架构
2.1语音聊天系统功能模块图
图2语音聊天系统功能模块图
2.2利用套接字TCP网络通信流程
在TCP/IP协议组中,TCP是一种面向连接的协议,为用户提供可靠的、全双工的字节流服务,具有确认、流控制、多路复用和同步等功能,适用于数据传输。
在使用TCP协议进行通信时,一般服务端进程先使用socket调用得到一个描述符,然后使用bind调用将一个名字与socket描述符连接起来,对于Internet域就是将Internet地址联编到socket之后,服务端使用listen调用指出等待服务请求队列的长度。
然后就可以使用accept调用等待客户端发起连接,一般是阻塞等待连接,一旦有客户端发出连接,accept返回客户的地址信息,并返回一个新的socket描述符,该描述符与原先的socket有相同的特性,这时服务端就可以使用这个新的socket进行读写操作了。
一般服务端可能在accept返回后创建一个新的进程进行与客户的通信,父进程则再到accept调用处等待另一个连接。
客户端进程一般先使用socket调用得到一个socket描述符,然后使用connect向指定的服务器上的指定端口发起连接,一旦连接成功返回,就说明已经建立了与服务器的连接,这时就可以通过socket描述符进行读写操作了。
其服务器通信过程如图3所示,客户机通信过程如图4所示
图3服务器通信流程图4客户端通信流程
2.3语音聊天的套接字实现
2.3.1服务器Socket模块的基本设计
⑴创建套接字(socket);
⑵将套接字绑定到一个本地地址和端口上(bind);
⑶调用ServerSocket(),创建一个监听的socket;
⑷将套接字设为监听模式,准备接收客户请求(listen);
⑸等待客户请求到来。
当请求到来后,接受连接请求,返回一个新的对应于此次连接套接字(accept);
⑹用返回的套接字和客户端进行通信(send/recv);
⑺返回,等待另一客户请求;
⑻关闭ServerSocket(),关闭socket();
2.3.2客户端Socket模块的基本设计
⑴创建一个套接字(socket);
⑵调用ClientSocket()创建一个会话的socket;
⑶调用connect()建立与server端的连接;
⑷和服务器端进行通信(send/recv);
⑸关闭ClientSocket();
2.4声音的采集和播放模块的实现
录音的准备工作主要有4点:
打开录音设备;获得录音句柄;指定录音格式;分配若干用于录音的缓冲区。
开始录音时,先将所有内存块提供给录音设备用来录音,录音设备就会依次将语音数据写入内存,当一块内存写满,录音设备就会发一个Windows消息MM_WIM_DATA给相应的窗口,通知程序做相关处理,这时程序通常的处理方式是把内存中的数据进行复制,如写入文件等。
本程序中的处理方式是把数据通过网络发送并且在本地机上播放出来,然后把内存置空,返还给录音设备进行录音,这样就形成一个循环录音过程。
结束录音只需要释放所有内存块,并关闭录音设备。
播放的流程和录音流程相似,所以不再介绍。
在录音和放音的程序处理中,要特别注意两点。
一个是分配的内存的大小和数量。
内存的大小与IP电话中语音的连续性和延时性有直接关系,内存越大,则语音的连续性越好,但延时性越差,反之,内存越小,则语音的延时性越小,但连续性越差。
内存的数量则与内存的大小和对每个内存的录音数据的处理时间长短有关,一定要保证在录音过程中,录音设备至少有一块内存可供录音,也就是说录满的内存要及时返回,使得循环能够顺利进行。
三、关键模块分析
3.1程序模块划分
根据关键技术的分析,程序有如下几个模块:
(1)负责接收连接的服务器Socket模块:
负责该程序作为服务器时的网络端口监听。
(2)负责接收/发送数据的客户端Socket模块:
负责对Socket的发送数据和接收数据进行消息处理。
(3)声卡数据的采集和播放模块:
采用相关波形音频API,负责采集声音数据和播放声音数据。
(4)主框架的参数设定、消息处理和界面控制模块:
负责控制整个程序的操作,负责处理用户的输入参数,负责处理声卡消息和Socekt消息。
用AppWizard生成一个基于对话框的工程,该工程包含以下一些文件:
(1)主框架程序VoiceChat_jiajiaDlg.h/VoiceChat_jiajiaDlg.cpp:
负责处理各种信息,尤其是音频响应信息。
(2)服务器Socket程序ServerSocket.h/ServerSocket.cpp:
在服务器中,负责监听网络连接。
(3)客户端Socket程序ClientSocket.h/ClientSocket.cpp:
负责收发聊天数据,包括文字聊天数据和语音聊天数据。
(4)音频数据处理程序Sound.h/Sound.cpp:
采用相关声卡API,负责采集声音数据和播放声音数据。
3.2部分核心代码分析
3.2.1主窗口对话框类CVoiceChat_jiajiaDlg
(1)在VoiceChat_jiajiaDlg.h中的核心代码如下:
#include"ServerSocket.h"
#include"ClientSocket.h"
#include"Sound.h"
#include
classCExample2_ChatRoomDlg:
publicCDialog
{
public:
voidProcessPendingAccept();//做服务器端的时候用来处理socket的连接
voidCloseSessionSocket();//关闭会话的socket
voidClearContent();//清除edit窗口的内容
voidOnClearconnection();//清除socket的连接
public:
boolm_bInit;//是否已经网络连接
boolm_bClient;//客户端还是服务器端的表示
CClientSocketm_clientsocket;//如果应用程序做客户端,则使用这个socket通信
CServerSocketm_pListenSocket;//如果应用程序做服务器端,则使用这个socket监听窗口
CPtrListm_connectionList;//如果应用程序做服务器端,则用这个链表记录连接客户端socket
CStringm_sMsgList;//edit窗口显示的字符
CSoundm_sound;//音频处理对象
CMutexm_mutex;//需要进行锁处理的一个对象
BOOLm_bIsFileClosed;
BOOLm_willchating;//是否需要进行语音传输
protected:
afx_msgvoidOnInputText();//在edit窗口输入文字的消息处理
afx_msgvoidOnConnectserver();//连接到服务器的消息处理
afx_msgvoidOnSetserver();//设置为服务器的消息处理
afx_msgvoidOnSound();//开始进行语音聊天,它采用了一个dll对语音进行处理
afx_msgvoidOnNewsend();//开始进行语音聊天,采用CSound类的语音处理
//当CSound中录音的buffer装满数据时的消息处理函数
afx_msgvoidWriteBufferFull(LPARAMlp,WPARAMwp);
(2)VoiceChat_jiajiaDlg.cpp中的核心代码如下:
//做服务器端时,对连接上的客户端socket的处理函数
voidCVoiceChat_jiajiaDlg:
:
ProcessPendingAccept()
{
CClientSocket*pSocket=newCClientSocket();
if(m_pListenSocket.Accept(*pSocket))
{
CMessgmsg;
msg.m_strText="一个游客进入聊天室了";
m_sShowString+="一个游客进入聊天室了\n";
POSITIONpos;
for(pos=m_connectionList.GetHeadPosition();pos!
=NULL;)
{
CClientSocket*t=(CClientSocket*)m_connectionList.GetNext(pos);
t->SendMessage(&msg);
}
pSocket->Init(this);
m_connectionList.AddTail(pSocket);
}
else
deletepSocket;
}
//做客户端,连接到服务器端的消息处理
voidCVoiceChat_jiajiaDlg:
:
OnConnectserver()
{
if(!
m_bInit)
{
BYTEf0,f1,f2,f3;
CStringname;
((CIPAddressCtrl*)(GetDlgItem(IDC_IPADDRESS)))->GetAddress(f0,f1,f2,f3);
CStringip;
ip.Format("%d.%d.%d.%d",f0,f1,f2,f3);
m_bClient=true;
m_clientsocket.Create();
if(m_clientsocket.Connect(ip,GetDlgItemInt(IDC_PORT)))
{
m_clientsocket.Init(this);
SetDlgItemText(IDC_SHOWTEXT,"ClientConnectionSucceed");
m_bInit=true;
}
else
{
m_clientsocket.Close();
AfxMessageBox("clientconnectionfailed");
m_bInit=false;
}
}
}
//设置服务器端的消息处理
voidCVoiceChat_jiajiaDlg:
:
OnSetserver()
{
if(!
m_bInit)
{
m_bClient=false;
m_bInit=true;
if(m_pListenSocket.Init(GetDlgItemInt(IDC_PORT),this)==FALSE)
{
m_bInit=false;
return;
}
}
}
//语音聊天按钮的消息处理函数
voidCVoiceChat_jiajiaDlg:
:
OnNewsend()
{
//TODO:
Addyourcontrolnotificationhandlercodehere
if(m_willchating==TRUE)
{
m_sound.Init(this);
m_sound.Record();
SetDlgItemText(IDC_NEWSEND,"停止语音聊天");
m_willchating=FALSE;
}
else
{
CSingleLocklock(&m_mutex,TRUE);
m_sound.StopRecord();
SetDlgItemText(IDC_NEWSEND,"语音聊天");
m_willchating=TRUE;
lock.Unlock();
}
}
//录音buffer装满数据时的消息处理函数
voidCVoiceChat_jiajiaDlg:
:
WriteBufferFull(LPARAMlp,WPARAMwp)
{
m_sound.Play();//在本地发出声音
CSingleLocklock(&m_mutex,TRUE);
CMessgmsg;
msg.m_strText="";
msg.m_tag=1;
memcpy(msg.m_buffer,m_sound.m_cBufferIn,MAX_BUFFER_SIZE);
if(!
m_bClient)//服务器端,发送声音数据到所有的客户端
{
POSITIONpos;
for(pos=m_connectionList.GetHeadPosition();pos!
=NULL;)
{
CClientSocket*t=(CClientSocket*)m_connectionList.GetNext(pos);
t->SendMessage(&msg);
}
}
else//客户端,发送声音数据到服务器端
{
m_clientsocket.SendMessage(&msg);
}
m_sound.FreeRecordBuffer();//buffer重置
m_sound.FreePlayBuffer();//buffer重置
lock.Unlock();
}
其他的一些如服务器端Socket类CserverSocket,客户端Socket类CclientSocket和串行化数据类Cmessg以及音频设备控制的Csound类的核心代码在程序文件夹中均可找到,在这里就不作一一解释。
四、测试报告
程序编译完成后,使用VisualC++6.0对语音聊天系统进行测试,具体测试步骤为:
(1)选定一台主机即作为服务器,又作为其中一个客户端1。
(2)在主机上运行程序,点击“建立服务器”按钮,服务器IP地址为此主机IP地址,端口号默认为4000,此时显示框会出现“ServerHasBeenSetOK!
”的提示。
(3)在这台主机上再次运行程序,点击“连接服务器”按钮,此时会弹出窗口提示“请输入登陆名字”,输入名字以后显示框会出现“XXConnectionSucceed”的提示,等待另一个客户端的连接。
(4)选定另一台主机,作为另一个客户端2,运行程序,serverIP为服务器主机IP地址。
点击“连接服务器”按钮,同样会有弹出窗口提示“请输入登录名字”,输入登陆名字以后,此时客户端1的显示框会显示“XX进入聊天室了”,客户端2的显示框则显示“XXConnectionSucceed”。
此时即可在输入框输入文字信息,开始文字聊天。
(5)在两个客户端上分别点击“语音聊天”按钮,即可进行语音聊天通信。
(6)若要中断语音聊天,点击“停止语音聊天“即可。
下面是部分测试结果截图,由于程序是语音聊天系统,语音无法用截图表示,经过与同学进行双方连接测试,语音聊天有一定的延时,有点卡,但不妨碍语音聊天功能。
测试结果如下:
(1)服务器端
(2)客户端1
(3)客户端2
(4)建立连接开始文字聊天和语音聊天
五、实验总结和展望
在对通信网络课程理论学习的基础上,我学习了VC++网络通信编程的相关知识,深入体会原理,第一次将通信网络相关知识用于实践,我收获了很多。
通过这次编程,我学到了很多知识。
如:
(1)Csocket提供的是阻塞式的访问方式,而且Csocket类不是线程安全的。
(2)要使用Csocket对象,首先调用构造函数,然后调用Creat函数创建一个Socket句柄。
(3)一些成员函数提供了阻塞调用的功能,如Receive(),Send(),ReceiveFrom(),SendTo()和Accept()等。
在不能立即发送或接收数据时,不会立即返回一个WSAEWOULDBLOCK错误,它们会等待,直到操作结束。
等等。
作为语音聊天,这个程序已经设计了语音聊天的最重要的部分,但它还有一些不足,下面对程序存在的不足作一下总结和展望:
(1)程序没有对声音数据进行压缩处理。
如果熟悉数据压缩的相关知识,可以对程序中的声音数据进行压缩处理再传输。
(2)TCP采用阻塞方式,当接入客户端很多或发送数据量很大时,可能会造成程序卡死或信息溢出,可以选择非阻塞方式或多线程Socket通信进行改善。
六、参考资料
[1]龚向阳等.宽带通信网原理.北京邮电大学出版社.2006
[2]李代平等.C++程序设计教程.地震出版社.2004
[3]郎锐等.VisualC++网络通信程序开发指南.机械工业出版社.2004
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 基于 TCP 语音 聊天 通信 系统