基于C局域网视频聊天设计说明.docx
《基于C局域网视频聊天设计说明.docx》由会员分享,可在线阅读,更多相关《基于C局域网视频聊天设计说明.docx(41页珍藏版)》请在冰豆网上搜索。
基于C局域网视频聊天设计说明
1绪论
视频监控是各行业重点部门或重要场所进行实时监控的物理基础,管理部门可通过它获得有效数据、图像或声音信息,对突发性异常事件的过程进行及时的监视和记忆,用以提供高效、及时地指挥和高度、布置警力、处理案件等。
本系统采用DirectShow网络组播技术实现了视频捕获、视频压缩、网络传输、视频解码和实时回放,减小了网络带宽占用,高效的传输视频数据,独立于硬件。
可扩展性好。
完全利用现有1P数据网络传输数据.不需要单独布线,显著降低了系统成本,缩短了系统开发周期,并且可以容易的实现远程监测
2Windows服务
一个Windows服务程序是在Windows操作系统下能完成特定功能的可执行的应用程序。
Windows服务程序虽然是可执行的,但是它不像一般的可执行文件通过双击就能开始运行了,它必须有特定的启动方式。
这些启动方式包括了自动启动和手动启动两种。
对于自动启动的Windows服务程序,它们在Windows启动或是重启之后用户登录之前就开始执行了。
而对于手动启动的Windows服务程序,你可以通过命令行工具的NETSTART命令来启动它,或是通过控制面板中管理工具下的服务一项来启动相应的Windows服务程序。
同样,一个Windows服务程序也不能像一般的应用程序那样被终止。
因为Windows服务程序一般是没有用户界面的,所以你也要通过命令行工具或是下面图中的工具来停止它,或是在系统关闭时使得Windows服务程序自动停止。
因为Windows服务程序没有用户界面,为了能使一个Windows服务程序能够正常并有效的在系统环境下工作,程序员必须实现一系列的方法来完成其服务功能。
Windows服务程序的应用围很广,典型的Windows服务程序包含了硬件控制、应用程序监视、系统级应用、诊断、报告、Web和文件系统服务等功能。
2.1添加文件监视服务
将生成的服务名为Webcamservice的服务添加视频监视功能:
Ø首先,在C盘创建文件夹w
Ø将程序生成的debug中的文件复制到w文件夹
Ø在C/windows/搜索installutil.exe执行文件,将其复制到w文件夹
Ø启动cmd,打开命令提示符窗体键入如图2-1所示
图2-1添加服务功能
Ø用netstart命令启动服务Webcamservice如图2-2所示
图2-2启动Webcamservice
3项目的设计原理
3.1DirectShow技术
采用网络摄像机的远程视频监控具有录像时间长、图像质量好、查询速度快等优点,目前应用非常广泛。
对于网络摄像机传输的视频数据,需要专门的Filter来处理并在DirectShow的框架下或回放,或保存。
监控服务器通过Internet/Intranet轮询网络摄像机获取视频。
本文以视频数据接收Filter的设计过程介绍基于DirectShow的视频数据流的传输以及通过自定义的通讯协议的数据解析过程和Filter程序设计与实现过程。
DirectShow[1]是微软公司在ActiveMovie和VideoforWindows的基础上推出的基于COM的流媒体处理的开发包,与DirectX开发包一起发布。
DirectShow为多媒体流的捕捉和回放以及二次开发提供了强有力的支持。
运用DirectShow,可以很方便地从支持WDM驱动模型的采集卡上采集数据,并且调用其API函数进行后期处理。
它广泛地支持各种媒体格式,包括Asf,Mpeg,Avi,Dv,Mp3,Wave等等,使得多媒体数据的回放变得轻而易举。
DirectShow是一个开放的框架,因此只要有合适的Filter来分析和解码,可以支持任何格式。
3.2TCP/IP协议
在TCP/IP协议组分两种协议:
网络层的协议,应用层的协议
Ø网络层协议
网络层协议管理离散的计算机间的数据传输。
这些协议是在系统表层以下工作的。
比如,IP协议为用户和远程计算机提供了信息包的传输方法。
它是在许多信息的基础上工作的,好比说是机器的IP地址。
在机器IP地址和其它信息的基础上,IP确保信息包能正确地到达目的机器。
通过这一过程,IP和其它网络层的协议共同用于数据传输。
如果没有网络工具,用户就看不到在系统里工作的IP。
Ø应用层协议
相反地,应用层协议用户是可以看得到的。
比如,文件传输协议(FTP)用户是看得到的。
用户为了传输一个文件请求一个和其它计算机的连接,连接建立后,就开始传输文件。
在传输时,用户和远程计算机的交换的一部分是能看到的。
ØIP
IP层接收由更低层(网络接口层例如以太网设备驱动程序)发来的数据包,并把该数据包发送到更高层---TCP或UDP层;相反,IP层也把从TCP或UDP层接收来的数据包传送到更低层。
IP数据包是不可靠的,因为IP并没有做任何事情来确认数据包是按顺序发送的或者没有被破坏。
IP数据包中含有发送它的主机的地址(源地址)和接收它的主机的地址(目的地址)。
高层的TCP和UDP服务在接收数据包时,通常假设包中的源地址是有效的。
也可以这样说,IP地址形成了许多服务的认证基础,这些服务相信数据包是从一个有效的主机发送来的。
ØTCP
如果IP数据包中有已经封好的TCP数据包,那么IP将把它们向上传送到TCP层。
TCP将包排序并进行错误检查,同时实现虚电路间的连接。
TCP数据包中包括序号和确认,所以未按照顺序收到的包可以被排序,而损坏的包可以被重传。
TCP将它的信息送到更高层的应用程序,例如Telnet的服务程序和客户程序。
应用程序轮流将信息送回TCP层,TCP层便将它们向下传送到IP层,设备驱动程序和物理介质,最后到接收方。
3.3C/S架构
在网络连接模式中除对等网外,还有另一种形式的网络,即客户机/服务器网,Client/Server。
在客户机/服务器网络中,服务器是网络的核心,而客户机是网络的基础,客户机依靠服务器获得所需要的网络资源,而服务器为客户机提供网络必须的资源。
图3-1c/s结构
4程序流程图及设计
4.1程序时序图与系统架构
本系统采用面向连接的客户/服务模型,服务器必须首先启动,否则客户进程的Connect()系统调用将返回错误代码表示连接失败。
无连接的服务进程也必须首先启动以指定本地的套接字地址否则客户进程的数据服务请求传送不到服务器进程。
面向连接的c/s时序图如图4-1所示
图4-1程序时序图
系统由服务器终端采集传输系统和客户端接收系统两部分组,系统构架如图4-2所示
图4-2系统架构
4.2程序设计分析
4.2.1任务目标
服务器端程序目标:
服务器服务器端服务程序进行数据采集(捕捉摄像头捕获数据),提供IP端口实现数据流的传输。
客户端程序目标:
客户端程序通过IP协议与服务器端通信,接收并回放服务器端采集的视频数据流。
4.2.2程序描述
Socket类:
socket之间的连接可以分为三种类型:
客户端连接,监听连接以及服务器端连接。
客户端连接是指由客户端的socket提出连接请求,要连接的目标是服务器端的socket。
为此,客户端的socket必须首先描述它要连接的服务器端socket(主要是指服务器端socket的地址和端口号),然后再定位所要连接的服务器端socket,找到以后,就向服务器端socket请求连接。
当然,服务器端的socket此时未必正好处于准备好状态,不过,服务器端的socket会自动维护客户请求连接的队列,然后在它认为合适的时候向客户端socket发出"允许连接"(accept)的信号,这时客户端socket与服务器端socket的连接就建立了。
监听连接,服务器端socket并不定位具体的客户端socket,而是处于等待连接的状态。
当服务器端socket监听到或者说接收到客户端socket的连接请求,它就响应客户端socket的请求建立一个新的socket句柄并与客户端连接,而服务器端socket继续处于监听状态,还可以接收其它客户端socket的连接请求。
服务器端连接,是指当服务器端socket接收到客户端socket的连接请求后,就把服务器端socket的描述发给客户端,一旦客户端确认了此描述,连接就建立了。
在本文中的聊天程序用的就是监听连接,即服务器设置连接个数后进行监听,客户端进行对服务器端的连接,这样就可以进行相互通信了。
TcpService类
namespaceTCP
{
internalclassTcpServer:
IDisposable
{
//Thisisnotthemaxnumberofconnectionsyoucanhave,it'sthenumber
//thatcanqueueupwaitingforyoutoAcceptthem.IfmorethanMAXCONNECTION
//moreclientstrytoconnectwhileyouareservicinganother,OnConnectis
//probablytakingtoolong.
constintMAXCONNECTIONS=3;
#regionMembervariables
privateArrayListm_aryClients;
privateSocketm_sockListener;
privatevolatileboolm_bShuttingDown;
privateManualResetEventShutDownReady;
#endregion
//Returnanarrayoftheipaddressesassignedtothispc
publicstaticIPAddress[]GetAddresses()
{
IPAddress[]aryLocalAddr=null;
stringstrHostName="";
//NOTE:
DNSlookupsareniceandallbutquitetimeconsuming.
strHostName=Dns.GetHostName();
#ifUSING_NET11
IPHostEntryipEntry=Dns.GetHostByName(strHostName);
#else
IPHostEntryipEntry=Dns.GetHostEntry(strHostName);
#endif
aryLocalAddr=ipEntry.AddressList;
//VerifywegotanIPaddress.
if(aryLocalAddr==null||aryLocalAddr.Length<1)
{
thrownewException("Unabletogetlocaladdress");
}
returnaryLocalAddr;
}
publicTcpServer(intnPortListen)
{
_TcpServer(nPortListen,GetAddresses()[0]);
}
publicTcpServer(intnPortListen,IPAddressip)
{
_TcpServer(nPortListen,ip);
}
//Shutdownthelistener
publicvoidDispose()
{
//ShuttingdownisarealPITA.Youcan'tclosethelistener
//whilethereisanoutstandingasynccallactive.Andyoucan't
//canceltheasynccall.Grr.Asaworkaround,thisroutine
//makesaconnectiontotheport.TheOnConnectroutine,recognizing
//thatweareinshutdown,doesn'tcreateanewasynccall.
TcpClientt=null;
//Onlywantonethreadtobepreformingshutdownatatime.
lock(this)
{
//Havewealreadyshutdown?
if(!
m_bShuttingDown)
{
m_bShuttingDown=true;
//Disconnecteachclient
foreach(SockWrappersinm_aryClients)
{
try
{
s.Client.Shutdown(SocketShutdown.Both);
s.Client.Close();
}
catch{}
}
m_aryClients=null;
//Connecttotheporttotriggertheasynclistener
IPEndPointep=(IPEndPoint)m_sockListener.LocalEndPoint;
t=newTcpClient(ep.Address.ToString(),ep.Port);
}
}
if(t!
=null)
{
//Listenfortheasynclistenertoletgo.Thismustbedone
//outsidethecritsectionsincethelistenerneedstolockit.
ShutDownReady.WaitOne(3000,false);
lock(this)
{
//closeeverythingdown
m_sockListener.Close();
m_sockListener=null;
t.Close();
ShutDownReady.Close();
ShutDownReady=null;
}
}
}
~TcpServer()
{
//IfDisposeisnotcalledagainstourclassandthedestructoris
//called,someofthemembervariablesinthisclasshavealready
//beendisposed.Suchbeingthecase,there'snowaytocleanup
//nicely.Moral:
AlwayscallDispose.
//Dispose();
}
//Sendtoallconnectedclients
publicvoidSendToAll(MemoryStreamm)
{
_SendToAll(m.GetBuffer(),(int)m.Length);
}
publicvoidSendToAll(byte[]b)
{
_SendToAll(b,b.Length);
}
publicintConnections
{
get{returnm_aryClients.Count;}
}
publiceventTcpConnectedConnected;
publiceventTcpConnectedDisconnected;
publiceventTcpReceiveDataReceived;
publiceventTcpSendSend;
privatevoid_TcpServer(intnPortListen,IPAddressip)
{
try
{
//Initializemembervars
m_aryClients=newArrayList(5);
ShutDownReady=newManualResetEvent(false);
m_bShuttingDown=false;
//Createthelistenersocketinthismachine'sIPaddress
m_sockListener=newSocket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
m_sockListener.Bind(newIPEndPoint(ip,nPortListen));
m_sockListener.Listen(MAXCONNECTIONS);
//Setupacallbacktobenotifiedofconnectionrequests
m_sockListener.BeginAccept(newAsyncCallback(OnConnectRequest),m_sockListener);
}
catch
{
m_bShuttingDown=true;
throw;
}
}
privatevoid_SendToAll(byte[]b,intiLength)
{
lock(this)
{
if(!
m_bShuttingDown)
{
foreach(SockWrappersinm_aryClients)
{
_SendOne(s,b,iLength);
}
}
}
}
privatevoid_SendOne(SockWrappers,byte[]b,intiLength)
{
try
{
boolbSend=true;
if(Send!
=null)
Send(this,refs.obj,refbSend);
if(bSend)
s.Client.Send(b,iLength,SocketFlags.None);
}
catch
{
//Ignoretheerror.Iftheclientisdead,OnReceiveData
//willbecalledtoclosetheconnection.Iwouldremoveit
//anyway,exceptbadthingshappenifyouremoveanentry
//fromalistwhileusingforeach.
}
}
//Clienthasconnected
privatevoidOnConnectRequest(IAsyncResultar)
{
//Getthelistenerandclient
Socketlistener=(Socket)ar.AsyncState;
Socketclient=listener.EndAccept(ar);
lock(this)
{
if(!
m_bShuttingDown)
{
//Wraptheclientandaddittothearray
SockWrappers=newSockWrapper(client);
m_aryClients.Add(s);
//FiretheConnectedevent
if(Connected!
=null)
Connected(this,refs.obj);
//Setupanasyncwaitforpacketsfromtheclient
AsyncCallbackreceiveData=newAsyncCallback(OnReceivedData);
s.Client.BeginReceive(s.byBuff,0,s.byBuff.Length,SocketFlags.None,receiveData,s);
//(Re)Setupacallbacktobenotifiedofconnectionrequests
listener.BeginAccept(newAsyncCallback(OnConnectRequest),listener);
}
else
{
//Ifweareinshutdownmode,DON'Tadd
//theconnectiontothearray,DON'Tsetup
//theasynclisten,andDOsettheevent
//tosaywearedone.
ShutDownReady.Set();
}
}
}
//Clienthassentdata,orhasdisconnected
privatevoidOnReceivedData(IAsyncResultar)
{
//Socketwasthepassedinobject
SockWrappers=(SockWrapper)ar.AsyncState;
lock(this)
{
if(!
m_bShuttingDown)
{
//Checkifwegotanydata
try
{
intnBytesRec=s.Client.EndReceive(ar);
if(nBytesRec>0)
{
if(DataReceived!
=null)
DataReceived(this,refs.obj,refs.byBuff,nBytesRec);
//Restablishthecallback
AsyncCallbackreceiveData=newAsyncCallback(OnReceivedData);
s.Client.BeginReceive(s.byBuff,0,s.byBuff.Length,SocketFlags.None,receiveData,s);
}
else
{
//Ifnodatawasreceivedthentheconnectionisprobablydead
RemoveConnection(