卖逼视频免费看片|狼人就干网中文字慕|成人av影院导航|人妻少妇精品无码专区二区妖婧|亚洲丝袜视频玖玖|一区二区免费中文|日本高清无码一区|国产91无码小说|国产黄片子视频91sese日韩|免费高清无码成人网站入口

網(wǎng)絡(luò)音頻點播軟件的設(shè)計與開發(fā)實驗

網(wǎng)絡(luò)音頻點播軟件的設(shè)計與開發(fā)實驗一、實驗?zāi)康恼莆栈赟ocket?的C/S編程的方法掌握?Windows平臺Socket?網(wǎng)絡(luò)應(yīng)用程序的開發(fā)方法掌握?Windows平臺多線程網(wǎng)絡(luò)程序的開發(fā)方法二、實驗

網(wǎng)絡(luò)音頻點播軟件的設(shè)計與開發(fā)實驗

一、實驗?zāi)康?/p>

掌握基于Socket?的C/S編程的方法

掌握?Windows平臺Socket?網(wǎng)絡(luò)應(yīng)用程序的開發(fā)方法

掌握?Windows平臺多線程網(wǎng)絡(luò)程序的開發(fā)方法

二、實驗內(nèi)容

在?Windows2000平臺下,使用Microsoft?Visual?C 6.0,基于Socket?開發(fā)網(wǎng)絡(luò)音頻 點播程序,服務(wù)器端能夠捕捉音頻流并發(fā)送到需要點播的客戶端,客戶端接收音頻流 后播放。不同客戶端之間可以互相發(fā)送文本。

三、實驗原理?

1.Winsock 概述

在?Win32平臺上?Winsock是訪問網(wǎng)絡(luò)層協(xié)議的首選接口。而且在每個Windows 平 臺上,Winsock 都以不同形式存在著。Winsock?與Linux 的Socket?一樣,是網(wǎng)絡(luò)編程接 口, 而不是協(xié)議。Winsock ?是Unix 的Berkeley(BSD)套接字的基礎(chǔ)上發(fā)展起來的,Winsock??

有多個版本,從?Windows95、WinNt4?開始,系統(tǒng)就內(nèi)置了?Winsock1.1, 后來到了?Windows98、windows2000,它內(nèi)置的?Winsock?DLL已更新為?Winsock2.2。Winsock1.1?有?2?種?I/O?方,2?種?I/O?模型,到了?Winsock2.2,則有了?2?種?I/O?方式,5?種?I/O?模型。 另外,Winsock2.2?對?Socket?進行了很多擴充與改進,如重疊?I/O?模型、服務(wù)質(zhì)量控制 等。Winsock 的版本是向前兼容的,也就是說,使用Winsock1.1編程接口的應(yīng)用程序, 可以在?Winsock2.2的計算機上運行。?

2.Winsock 編程基礎(chǔ)?

Winsock 與Linux 的?socket?編程是基本一致的,Linux ?的?socket?編程的原理和方法, 在?Windows下依然適用。當然?Winsock有了更多的擴展。?

(1)Winsock的初始化和釋放

每個?Winsock應(yīng)用都必須加載?Winsock?Dll的相應(yīng)版本。如果調(diào)用Winsock 之前沒 有加載?Winsock庫,這個函數(shù)就會返回錯誤,錯誤信息是?WSANOTINITIALISED。加 載?Winsock庫是通過調(diào)用?WSAStartup函數(shù)實現(xiàn)的,這個函數(shù)定義為:?

int?WSAStartup(?

WORD?wVersionRequested,?

LPWSADA TA?lpWSAData?

)??

參數(shù)w?VersionRequested 指定加載的?Winsock?庫的版本,高位字節(jié)指定副版本,低 位字節(jié)指定主版本??梢允褂煤闙AKEWORD(X,Y)方便地指定合適的版本。?

lpWSAData 是一個與加載庫版本有關(guān)的信息, 在函數(shù)調(diào)用后系統(tǒng)會填充這個結(jié)構(gòu), 以獲得相應(yīng)的?Winsock庫的信息.WSADA TA 結(jié)構(gòu)聲明為:?

typedef?struct?WSAData?{?

WORD?wVersion??

WORD?wHighV ersion:?

char?szDescription [WSADESCRIPTION_LEN 1];?

char?szSystemStatus[WSASYS_STATUS_LEN 1]??

unsigned?short?iMaxSockets??

unsigned?short?iMaxUdpDg??

char?FAR??*?lpVendorInfo??

}WSADATA,??*LPWSADATA?

,

在?Winsock應(yīng)用程序結(jié)束網(wǎng)絡(luò)程序后,需要釋放?Winsock?DLL的資源,釋放函 數(shù)為:?

int?WSACleanup?(void)?

(2)?Winsock的流套接字編程

下圖是使用?Winsock流套接字時服務(wù)器與客戶端的交互過程?

(3)Winsock編程接口支持庫?

Winsock 支持庫與?Winsock的版本有關(guān),在使用不同版本的?Winsock開發(fā)程序時, 需要注意使用?Winsock庫,下表列出了不同版本的?Winsock編程接口的支持庫。?

(1)名字解析

依用戶看來,IP?

地址是不容易記憶的。在指定機器時,大多數(shù)人喜歡用一個易記 的、友好的主機名而不是IP 地址。與此類似的是網(wǎng)絡(luò)域名,大家在訪問網(wǎng)頁時,喜歡 輸入的是服務(wù)器的域名地址如www.sina.com.cn , 而不是一個難記的IP 地址。?

Winsock 套接字提供了支持函數(shù),可以將主機名/域名解析為IP 地址。這個函數(shù)定 義為:struct?hostent?FAR?*gethostbyname(const?char?FAR?*name)??

該函數(shù)傳入域名字符串, 返回一個結(jié)構(gòu)hostent, 這個結(jié)構(gòu)包含了名字解析結(jié)構(gòu)信息:?struct?hosten{?

char?FAR?*????????h_name??

char?FAR?*?FAR?*??h_aliases:?

short?????????????h_addrtype?

,

short?????????????h_length??

char?FAR?*?FAR?*?h_addr_list??

}??

h_name?是正式的主機名或者域名,h_aliases?是一個由備用名字組成的空中止數(shù)組,?h_addrtype?返回地址家族,h_length表示解析的地址字段長度,h_addr_list?是解析后地 址數(shù)組。一般情況下應(yīng)當使用數(shù)組中的第一個地址,但如果返回的地址多于一個,可 以考慮使用其它地址。?

(2)查詢錯誤碼

對編寫程序而言,錯誤的查詢和控制是十分重要的,不能因為一個小錯誤導(dǎo)致網(wǎng) 絡(luò)程序的崩潰。對于?Winsock?來說,返回錯誤是常見的,但是在大多數(shù)情況下,這些 錯誤都是無關(guān)緊要的,通信仍可以繼續(xù)在套接字上進行。

不成功的?Winsock?接口函數(shù)返回的最常見的值是?SOCKER_ERROR?它的常量值被 定義為-1。如果需要查詢錯誤的具體情況,可以調(diào)用函數(shù)?WSAGetLastError獲得錯誤 代碼,了解錯誤的詳細信息,這個函數(shù)定義為:?

int?WSAGetLastError(void)??

函數(shù)返回的錯誤碼都是以預(yù)先定義的常量值,可以在相關(guān)的幫助或者?winsock?的 頭文件中找到它們的含義。?

4、Windows?多線程?

(1)線程的概念

為了了解線程的概念,必須先了解一下進程的概念。

一個進程通常定義為程序的一個實例。在?Win32中,進程占據(jù)4GB 的地址空間。 為了讓進程完成一些工作,進程必須至少占有一個線程,所以線程是描述進程內(nèi)的執(zhí) 行,正是線程負責執(zhí)行包含在進程的地址空間中的代碼。實際上,單個進程可以包含 幾個線程,它們可以同時執(zhí)行進程地址空間中的代碼。為了做到這一點,每個線程有 自己的一組CPU 寄存器和堆棧。

每個進程至少有一個線程在執(zhí)行其地址空間中的代碼,為了運行所有這些線程, 操作系統(tǒng)為每個獨立線程安排一些CPU 時間, 操作系統(tǒng)以輪轉(zhuǎn)方式向線程提供時間片。 創(chuàng)建一個?Win32?進程時,它的第一個線程稱為主線程,由系統(tǒng)自動生成,然后再由這 個主線程生成額外的線程,這些線程又可生成更多的線程。?

(2)編寫線程函數(shù)

所有線程必須從一個指定的函數(shù)開始執(zhí)行,該函數(shù)稱為線程函數(shù),它必須具有下 列原型:?

DWORD?WINAPI?YourThreadFunc(?PVOID?lpvThreadParm)??

該函數(shù)輸入一個LPVOID 類型的參數(shù),可以是一個DWORD 型的整數(shù),也可以是 一個指向一個緩沖區(qū)的指針,返回一個DWORD 型的值。?

(3)創(chuàng)建一個線程

一個進程的主線程是由操作系統(tǒng)自動生成的,如果要讓一個主線程創(chuàng)建額外的線 程,可以調(diào)用CreateThread 函數(shù),這個函數(shù)聲明為:?

HANDLE?CreateThread(?

LPSECURITY_ATTRIBUTES?lpThreadAttributes,?????????//SD?

DWORD?dwStackSize,???????????????????????????????//initial?stack?size?

LPTHREAD_START_ROUTINE?lpStartAddress,??????????//thread?function?

LPVOID?lpParameter,?//thread?argument?

DWORD?dwCreationFlags,????????????????????????????//creation?option

,

LPDWORD?lpThreadId???????????????????????????????//threa?identifier?

)??

其中,lpThreadAttributes 參數(shù)為一個指向SECURITY_ATTRIBUTES 結(jié)構(gòu)的指針。 如果想讓對象為缺省安全屬性,可以傳一個NULL ;參數(shù)?lpStartAddress用來表示新線 程開始執(zhí)行時代碼所在函數(shù)的地址,即為線程函數(shù)。lpParameter?為傳入線程函數(shù)的參 數(shù),dwCreationFlags?參數(shù)指定控制線程創(chuàng)建的附加標志,可以取兩種值。如果該參數(shù) 為?0,線程就會立即開始執(zhí)行,如果該參數(shù)為?CREA TE_SUSPENDED,則系統(tǒng)產(chǎn)生線 程后,掛起該線程。最后一個參數(shù)?lpThreadId?是一個?DWORD?類型的地址,返回賦給 新線程的ID 值。?

CreateThread 函數(shù)參數(shù)較多,但在常見的使用中,這些參數(shù)可以取默認值,如:?DWORD?dwThreadId??

HANDLE?hThread=CreateThread(NULL,0,ServiceThread,param?,0,&dwThreadId)??

(4)終止線程

如果某些線程調(diào)用了ExitThread 函數(shù),就可以終止自己。?

VOID?ExitThread(?

DWORD?dwExitCode??????????????????????????//exit?code?for?this?thread?

)??

這個函數(shù)為調(diào)用該函數(shù)的線程設(shè)置了退出碼dwExitCode 后,就終止該線程。調(diào)用?TerminateThread 函數(shù)也可以終止線程:?

BOOL?TerminateThread(?

HANDLE?hThread,???????????????????????//handle?to?thread?

DWORD?dwExitCode??????????????????????????//exit?code?

):?

該函數(shù)用來結(jié)束由hThread 參數(shù)指定的線程,并把dwExitCode 設(shè)成該線程的退出 碼。當某個線程不再響應(yīng)時,就可以用其它線程調(diào)用該函數(shù)來終止這個不響應(yīng)的線程?

(5)掛起及恢復(fù)線程

在線程被創(chuàng)建后的運行過程中,可以將線程掛起,線程在保存當前的運行環(huán)境后 進入睡眠狀態(tài),不再占用?CPU ;然后程序可以某個時刻“喚醒”這個線程,恢復(fù)運行 環(huán)境,然后繼續(xù)運行。掛起和恢復(fù)的函數(shù)分別為:?

DWORD?SuspendThread(HANDLE?hThread)??

DWORD?ResumeThread(HANDLE?hThread)??

四、實驗步驟?

1、需求分析

這是一個網(wǎng)絡(luò)音頻點播的工程,服務(wù)器端能夠捕捉音頻流并發(fā)送到需要點播的客 戶端,客戶端接收音頻流后播放。不同客戶端之間可以互相發(fā)送文本。顯然程序包含 兩個程序:服務(wù)器和客戶端,它們需要實現(xiàn)的功能為:

(1)服務(wù)器?

●音頻的捕捉:使用音頻編程接口捕捉服務(wù)器正在播放的音頻。

●音頻數(shù)據(jù)的緩存:保存捕捉的音頻數(shù)據(jù),并在合適的時刻交給網(wǎng)絡(luò)模塊發(fā)送。 ●音頻數(shù)據(jù)的發(fā)送:發(fā)送音頻數(shù)據(jù)流到客戶端。

●多客戶的支持和管理:應(yīng)該能夠支持多個客戶同時接收網(wǎng)絡(luò)音頻,并能夠監(jiān)控和 管理多個用戶。

●文本接收和發(fā)送功能:接收到一個客戶端發(fā)送的文本后,將文本信息轉(zhuǎn)發(fā)給相應(yīng) 的客戶端。

,

(2)客戶端

●音頻數(shù)據(jù)的接收。

●音頻數(shù)據(jù)的緩存。

●音頻數(shù)據(jù)的播放。

●文本接收和發(fā)送功能。?

2.程序的設(shè)計

這是一個標準的客戶/服務(wù)器程序,使用socket 套接字編程接口實現(xiàn)網(wǎng)絡(luò)功能。

(1) 套接字類型

由于這個工程是音頻和文本的傳輸,對可靠性要求較高,同時音頻數(shù)據(jù)的播放對 及時性和傳輸效率要求較高,所以應(yīng)當使用面向連接的流式套接字來實現(xiàn)網(wǎng)絡(luò)數(shù)據(jù)的 傳輸。

(2) 服務(wù)器模式

音頻的傳輸不需要服務(wù)器接收客戶端的信息,所以不用考慮服務(wù)器模式。而客戶 端的文本通信需要服務(wù)器來轉(zhuǎn)發(fā),這種文本通信具有并發(fā)的特點,所以應(yīng)當采用并發(fā) 服務(wù)器模式,而且由于傳輸?shù)男畔⒘坎皇呛艽?,所以可以考慮采用select?函數(shù)監(jiān)聽多客 戶端的設(shè)計方法,結(jié)構(gòu)如下圖所示。

(3)socket?類封裝

本實驗項目中兩種數(shù)據(jù)需要網(wǎng)絡(luò)傳輸:音頻數(shù)據(jù)和文本數(shù)據(jù)。而對于socket?來說, 它并不認為這兩種數(shù)據(jù)有什么不同。都是數(shù)據(jù)流,只需要發(fā)送而已。Socket?的初始化、 連接和接收發(fā)送等功能都與數(shù)據(jù)無關(guān)。所以應(yīng)當使用類來封裝socket ,實現(xiàn)基本的網(wǎng)絡(luò) 功能,并使用類的繼承或者對象組合擴展它的功能,實現(xiàn)文本和音頻流的傳輸。Socket?類可以在服務(wù)器程序和客戶端程序中使用。?

(4)音頻捕捉和播放

音頻捕捉和播放并不是本實驗的重點,可以使用很多種方法來實現(xiàn)這個功能???/p>

,

以使用面向?qū)ο蟮木幊谭椒▽⒁纛l捕捉和播放的實現(xiàn)細節(jié)封裝起來,并提供統(tǒng)一的使 用接口,供其它功能模塊使用。

本實驗提供了一個音頻捕捉和播放模塊的例子,在實驗?FTP?服務(wù)器的目錄下,它 使用了?DirectX?技術(shù),調(diào)用?DirectSoundCapture?和?DirectSound?接口分別實現(xiàn)音頻的捕 捉和播放,并提供了相應(yīng)的接口。?

(5)服務(wù)器設(shè)計

服務(wù)器依據(jù)的需求,應(yīng)當包含以下模塊:

●音頻捕捉模塊:可以開始和中止音頻的捕捉,并提供定時獲取音頻流數(shù)據(jù)接口。 ●數(shù)據(jù)發(fā)送模塊: 可以將數(shù)據(jù)發(fā)送到指定的客戶端。 支持多個和單個客戶端的發(fā)送。 ●客戶端服務(wù)模塊:接收客戶端的網(wǎng)絡(luò)數(shù)據(jù),并在解析客戶端請求后交給相應(yīng)模塊 處理。

●客戶維護和管理模塊:維護客戶列表。

●控制臺模塊:監(jiān)視服務(wù)器運行狀況、日值記錄和控制服務(wù)器。

各個模塊間的關(guān)系如下圖所示。?

(6)客戶端設(shè)計

客戶端依據(jù)需求,就當包含以下模塊:

●音頻播放模塊。

●數(shù)據(jù)接收模塊。

●文本發(fā)送模塊。

客戶端的結(jié)構(gòu)圖請自己繪出。?

3、開發(fā)實現(xiàn)

由于工程較大,開發(fā)實現(xiàn)的具體步驟將不再敘述,下面介紹一些需要注意的問題。?

(1)C/S通信協(xié)議

在客戶端和服務(wù)器間進行通信時,惟一的方式是發(fā)送和接收數(shù)據(jù)。而數(shù)據(jù)又可能 有多種數(shù)據(jù)類型,如音頻數(shù)據(jù)、文本數(shù)據(jù)、控制信息等。服務(wù)器和客戶端必須為不同 的數(shù)據(jù)類型定義不同的數(shù)據(jù)傳輸格式,以便服務(wù)器和客戶端之間能夠正常的“通話” , 否則它們接收到的數(shù)據(jù)就只能是一堆無法理解的密文。

常見的設(shè)計方法是定義服務(wù)器和客戶端通信協(xié)議,為每種數(shù)據(jù)類型和操作規(guī)定詳 細的解釋和行為。如是單純發(fā)送文本數(shù)據(jù)時,可以在發(fā)送的文本數(shù)據(jù)前添加字段的長

,

度。這是一種簡單的形式。在本實驗中上,由于涉及復(fù)雜的數(shù)據(jù)類型,所以通信協(xié)議 也相對復(fù)雜,能夠支持全部數(shù)據(jù)類型。

的長度,最后是實際要發(fā)送的數(shù)據(jù)。?

(2)發(fā)送數(shù)據(jù)

在使用socket?編程接口發(fā)送數(shù)據(jù)時,使用send?函數(shù),如:?

char?buf[1024*4]??

int?r=send(sockfd,buf,1024*4,0)??

對于上面的代碼,返回值的表示實際發(fā)送的數(shù)據(jù),在正常情況發(fā)送情況下,有可 能小于發(fā)送緩沖區(qū)的大小。對于TCP 來說,一個主要的原因是窗口大小的問題,接收 端會對窗口大小進行調(diào)整,指出它可以接收多少數(shù)據(jù),如果有大量數(shù)據(jù)涌入接收端, 它就會減少窗口大小甚至設(shè)置窗口為0.?

在上面的例子中,如果一次只能發(fā)送1024字節(jié)的數(shù)據(jù),則發(fā)送就不完整,所以應(yīng) 當處理這種情況,常見的方式是循環(huán)發(fā)送緩沖區(qū)中的數(shù)據(jù),直至全部發(fā)出,如:?

char?buf[1024*]??

int?toal_bytes=1024*4??

int?send_bytes=0?

while?(sendbytes

{?

r=send(sockfd,buf? ?send_bytes,total_bytes?–?sendbytes,0)??

if(r<0)?

return?r???????????????//發(fā)生錯誤?

send_bytes? ?=r??

}?

return?0??

(3)音頻捕捉設(shè)置

需要正確設(shè)置音頻的捕捉源,由于實驗是捕捉聲卡發(fā)出的聲音,所以需要設(shè)置捕 捉源為相應(yīng)類型。 在Windows 的錄音控制中(控制面版?聲音和音頻控制?音頻?音量)?選中“Wave?Out?Mix”(或波形輸出混音) 下的“選擇 ”復(fù)選框,這個參數(shù)在不同的聲卡 中可能有細微差別。?

(4)運行程序

在一臺計算機上運行服務(wù)器程序,在運行之前首先開始播放音頻。在其它幾臺計 算機上運行客戶端程序,并連接到服務(wù)器,收聽網(wǎng)絡(luò)點播的音頻

思考題?

1.?Winsock1.1?版本的應(yīng)用程序是否可以與?Winsock2.2?的應(yīng)用程序進行正常的通信? 請說明原因。?

2.?在文本數(shù)據(jù)傳輸?shù)姆?wù)器模式中使用并發(fā)服務(wù)器模式,可以采用?select?函數(shù)監(jiān)聽多 客戶端的設(shè)計方法,也可以采用一個線程對應(yīng)一個客戶端的方法并發(fā)服務(wù)器,這兩 種方法哪一種更適合這個實驗程序,為什么??

3.?在調(diào)用recv 接收數(shù)據(jù)時,接收到數(shù)據(jù)并不一定就是發(fā)送方發(fā)出的數(shù)據(jù)大小,而是可 能大于或者小于這個值,如何在編程中處理這個問題?

標簽: