Windows下多线程编程技术及其实现
生活随笔
收集整理的這篇文章主要介紹了
Windows下多线程编程技术及其实现
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
本文首先討論16位Windows下不具備的線程的概念,然后著重講述在32位Windows?95環(huán)境下多線程的編程技術(shù),最后給出利用該技術(shù)的一個(gè)實(shí)例,即基于Windows95下TCP/IP的可視電話的實(shí)現(xiàn)。??一、問(wèn)題的提出?
??作者最近在開(kāi)發(fā)基于Internet網(wǎng)上的可視電話過(guò)程中,碰到了這樣一個(gè)問(wèn)題。在基于Internet網(wǎng)上的可視電話系統(tǒng)中,同時(shí)要進(jìn)行語(yǔ)音采集、語(yǔ)音編解碼、圖象采集、圖象編解碼、語(yǔ)音和圖象?碼流的傳輸,?所有的這些事情,都要并行處理。特別是語(yǔ)音信號(hào),如果進(jìn)行圖象編解碼時(shí)間過(guò)長(zhǎng),語(yǔ)音信號(hào)得不到服務(wù),通話就有間斷,如果圖象或語(yǔ)音處理時(shí)間過(guò)長(zhǎng),而不能及時(shí)的傳輸碼流數(shù)據(jù),通信同樣也會(huì)中斷。這樣就要求我們實(shí)現(xiàn)一種并行編程,在只有一個(gè)CPU的機(jī)器上,也就是要將該CPU時(shí)間按照一定的優(yōu)先準(zhǔn)則分配給各個(gè)事件,定期處理某一事件而不會(huì)在某一事件處理過(guò)長(zhǎng),在32位Windows95或WindowsNT下,我們可以用多線程的編程技術(shù)來(lái)實(shí)現(xiàn)這種并行編程。實(shí)際上這種并行編程在很多場(chǎng)合下都是必須的。例如,在FileManager拷貝文件時(shí),它顯示一個(gè)對(duì)話框,?列出源文件和目標(biāo)文件的名稱,并在對(duì)話框中包含了一個(gè)Cancel按鈕。如果在文件拷貝過(guò)程中,點(diǎn)中Cancel按鈕,就會(huì)終止拷貝。?
??在16位Windows中,實(shí)現(xiàn)這類功能需要在FileCopy循環(huán)內(nèi)部周期性地調(diào)用PeekMessage函數(shù)。如果正在讀一個(gè)很大的數(shù)據(jù)塊,則只有當(dāng)這個(gè)塊讀完以后才能響應(yīng)這個(gè)按鈕動(dòng)作,如果從軟盤讀文件,則要花費(fèi)好幾秒的時(shí)間,由于機(jī)器反應(yīng)太遲鈍,你會(huì)頻繁地點(diǎn)中這個(gè)按鈕,以為系統(tǒng)不知道你想終止這個(gè)操作。如果把FileCopy指令放入另外一個(gè)線程,你就不需要在代碼中放一大堆PeekMessage函數(shù),處理?用戶界面的線程將與它分開(kāi)操作,這樣,點(diǎn)中Cancel按鈕后會(huì)立即得到響應(yīng)。同樣的道理,在應(yīng)用程序中創(chuàng)建一個(gè)單獨(dú)線程來(lái)處理所有打印任務(wù)也是很有用的,這樣,用戶可以在打印處理時(shí)繼續(xù)使用應(yīng)用程序。?
??二、線程的概念?
??為了了解線程的概念,我們必須先討論一下進(jìn)程的概念。?
??一個(gè)進(jìn)程通常定義為程序的一個(gè)實(shí)例。在Win32中,?進(jìn)程占據(jù)4GB的地址空間。與它們?cè)贛S-DOS和16位Windows操作系統(tǒng)中不同,?Win32進(jìn)程是沒(méi)有活力的。這就是說(shuō),一個(gè)Win32進(jìn)程并不執(zhí)行什么指令,它只是占據(jù)著4GB的地址空間,此空間中有應(yīng)用程序EXE文件的?代碼和數(shù)據(jù)。EXE需要的任意DLL也將它們的代碼和數(shù)據(jù)裝入到進(jìn)程的地址空間。除了地址空間,進(jìn)程還占有某些資源,比如文件、動(dòng)態(tài)內(nèi)存分配和線程。當(dāng)進(jìn)程終止時(shí),在它生命期中創(chuàng)建的各種資源將被清除。?
??但是進(jìn)程是沒(méi)有活力的,它只是一個(gè)靜態(tài)的概念。為了讓進(jìn)程完成一些工作,進(jìn)程必須至少占有一個(gè)線程,所以線程是描述進(jìn)程內(nèi)的執(zhí)行,正是線程負(fù)責(zé)執(zhí)行包含在進(jìn)程的地址空間中的代碼。實(shí)際上,單個(gè)進(jìn)程可以包含幾個(gè)線程,?它們可以同時(shí)執(zhí)行進(jìn)程的地址空間中的代碼。為了做到這一點(diǎn),每個(gè)線程有自己的一組CPU寄存器和堆棧。?
??每個(gè)進(jìn)程至少有一個(gè)線程在執(zhí)行其地址空間中的代碼,如果沒(méi)有線程執(zhí)行進(jìn)程?地址空間中的代碼,?進(jìn)程也就沒(méi)有繼續(xù)存在的理由,系統(tǒng)將自動(dòng)清除進(jìn)程及其地址空間。為了運(yùn)行所有這些線程,操作系統(tǒng)為每個(gè)獨(dú)立線程安排一些CPU?時(shí)間,操作系統(tǒng)以輪轉(zhuǎn)方式向線程提供時(shí)間片,這就給人一種假象,好象這些線程都在同時(shí)運(yùn)行。創(chuàng)建一個(gè)Win32進(jìn)程時(shí),它的第一個(gè)線程稱為主線程,它?由系統(tǒng)自動(dòng)生成,然后可由這個(gè)主線程生成額外的線程,這些線程,又可生成更多的線程。?
??三、線程的編程技術(shù)?
??1、編寫線程函數(shù)?
??所有線程必須從一個(gè)指定的函?數(shù)開(kāi)始執(zhí)行,該函數(shù)稱為線程函數(shù),它必須具有下列原型:?
????DWORDWINAPIYourThreadFunc(LPVOIDlpvThreadParm);?
??該函數(shù)輸入一個(gè)LPVOID型的參數(shù),可以是一個(gè)DWORD型的整數(shù),也可以是一個(gè)指向一個(gè)緩沖區(qū)的指針,?返回一個(gè)DWORD型的值。象WinMain函數(shù)一樣,這個(gè)函數(shù)并不由操作系統(tǒng)調(diào)用,?操作系統(tǒng)調(diào)用包含在KERNEL32.DLL中的非C運(yùn)行時(shí)的一個(gè)內(nèi)部函數(shù),如StartOfThread,然后由StartOfThread函數(shù)建立起一個(gè)異常處理框架后,調(diào)用我們的函數(shù)。?
??2、創(chuàng)建一個(gè)線程?
??一個(gè)進(jìn)程的主線程是由操作系統(tǒng)自動(dòng)生成,如果你要讓一個(gè)主線程創(chuàng)建額外的線程,你可以調(diào)用來(lái)CreateThread完成。?
??HANDLECreateThread(LPSECURITY_ATTRIBUTES?lpsa,DWORDcbstack,LPTHREAD_START_ROUTINElpStartAddr,?
LPVOID?lpvThreadParm,DWORDfdwCreate,LPDWORDlpIDThread);?
??其中l(wèi)psa參數(shù)為一個(gè)指向SECURITY_ATTRIBUTES結(jié)構(gòu)的指針。如果想讓對(duì)象為缺省安全屬性的話,可以傳一個(gè)NULL,如果想讓任一個(gè)子進(jìn)程都可繼承一個(gè)該線程對(duì)象句柄,必須指定一個(gè)SECURITY_ATTRIBUTES結(jié)構(gòu),其中bInheritHandle成員初始化為TRUE。參數(shù)cbstack表示線程為自己所用堆棧分配的地址空間大小,0表示采用系統(tǒng)缺省值。?
??參數(shù)lpStartAddr用來(lái)表示新線程開(kāi)始執(zhí)行時(shí)代碼所在函數(shù)的地址,即為線程函數(shù)。lpvThreadParm為傳入線程函數(shù)的參數(shù),fdwCreate參數(shù)指定控制線程創(chuàng)建的附加標(biāo)志,可以取兩種值。如果該參數(shù)為0,線程就會(huì)立即開(kāi)始執(zhí)行,如果該參數(shù)為CREATE_SUSPENDED,則系統(tǒng)產(chǎn)生線程后,初始化CPU,登記CONTEXT結(jié)構(gòu)的成員,準(zhǔn)備好執(zhí)行該線程函數(shù)中的第一條指令,但并不馬上執(zhí)行,而是掛起該線程。最后一個(gè)參數(shù)lpIDThread?是一個(gè)DWORD類型地址,返回賦給該新線程的ID值。?
??3、終止線程?
??如果某線程調(diào)用了ExitThread?函數(shù),就可以終止自己。?
??VOIDExitThread(UINTfuExitCode?);?
??這個(gè)函數(shù)為調(diào)用該函數(shù)的線程設(shè)置了退出碼fuExitCode后,?就終止該線程。調(diào)用TerminateThread函數(shù)亦可終止線程。?
??BOOLTerminateThread(HANDLE?hThread,DWORDdwExitCode);?
??該函數(shù)用來(lái)結(jié)束由hThread參數(shù)指定的線程,?并把dwExitCode設(shè)成該線程的退出碼。當(dāng)某個(gè)線程不在響應(yīng)時(shí),我們可以用其他線程調(diào)用該函數(shù)來(lái)終止這個(gè)不響應(yīng)的線程。?
??4、設(shè)定線程的相對(duì)優(yōu)先級(jí)?
??當(dāng)一個(gè)線程被首次創(chuàng)建時(shí),它的優(yōu)先級(jí)等同于它所屬進(jìn)程的優(yōu)先級(jí)。在單個(gè)進(jìn)程內(nèi)可以通過(guò)調(diào)用SetThreadPriority函數(shù)改變線程的相對(duì)優(yōu)先級(jí)。一個(gè)線程的優(yōu)先級(jí)是相對(duì)于其所屬的進(jìn)程的優(yōu)先級(jí)而言的。?
??BOOLSetThreadPriority(HANDLE?hThread,intnPriority);?
??其中參數(shù)hThread是指向待修改?優(yōu)先級(jí)線程的句柄,nPriority可以是以下的值:?
??THREAD_PRIORITY_LOWEST,?
??THREAD_PRIORITY_BELOW_NORMAL,?
??THREAD_PRIORITY_NORMAL,?
??THREAD_PRIORITY_ABOVE_NORMAL,?
??THREAD_PRIORITY_HIGHEST?
??5、掛起及恢復(fù)線程?
??先前我提到過(guò)可以創(chuàng)建掛起狀態(tài)的線程(通過(guò)傳遞CREATE_SUSPENDED標(biāo)志給函數(shù)CreateThread來(lái)實(shí)現(xiàn))。當(dāng)你這樣做時(shí),系統(tǒng)創(chuàng)建指定線程的核心對(duì)象,創(chuàng)建線程的棧,在CONTEXT結(jié)構(gòu)中初始化線程CPU注冊(cè)成員。然而,線程對(duì)象被分配了一個(gè)初始掛起計(jì)數(shù)值1,這表明了系統(tǒng)將不再分配CPU去執(zhí)行線程。要開(kāi)始執(zhí)行一個(gè)線程,另一個(gè)線程必須調(diào)用ResumeThread并傳遞給它調(diào)用CreateThread時(shí)返回的線程句柄。?
??DWORD?ResumeThread(HANDLEhThread);?
??一個(gè)線程可以被掛起多次。如果一個(gè)線程被掛起3次,?則該線程在它被分配CPU之前必須被恢復(fù)3次。除了在創(chuàng)建線程時(shí)使用CREATE_SUSPENDED標(biāo)志,你還可以用SuspendThread函數(shù)掛起線程。?
??DWORDSuspendThread(HANDLE?hThread);?
四、多線程編程技術(shù)的應(yīng)用?
??我在前面說(shuō)過(guò),為了實(shí)現(xiàn)基于TCP/IP下的可視電話,就必須“并行”地執(zhí)行語(yǔ)音采集、語(yǔ)音編解碼、圖象采集、圖象編解碼以及碼流數(shù)據(jù)的接收與發(fā)送。語(yǔ)音與圖象的采集由硬件采集卡進(jìn)行,我們的程序只需初始化該硬件采集卡,然后實(shí)時(shí)讀取采集數(shù)據(jù)即可,但語(yǔ)音和圖象數(shù)據(jù)的編解碼以及碼流數(shù)據(jù)的傳輸都必須由程序去協(xié)調(diào)執(zhí)行,決不能在某一件事件上處理過(guò)長(zhǎng),必須讓CPU輪流的為各個(gè)事件服務(wù),Windows95下的線程正是滿足這種要求的編程技術(shù)。?
??下面我給出了利用Windows95?環(huán)境下的多線程編程技術(shù)實(shí)現(xiàn)的基于TCP/IP的可視電話的部分源碼,其中包括主窗口過(guò)程函數(shù),以及主叫端與被叫端的TCP/IP接收線程函數(shù)和語(yǔ)音編解碼的線程函數(shù)。由于圖象編解碼的實(shí)時(shí)性比語(yǔ)音處理與傳輸模塊的實(shí)時(shí)性的?要求要低些,所以我以語(yǔ)音編解碼為事件去查詢圖象數(shù)據(jù),然后進(jìn)行圖象編解碼,而沒(méi)有為圖象編解碼去單獨(dú)實(shí)現(xiàn)一個(gè)線程。?
??在主窗口初始化時(shí),?我用CREATE_SUSPENDED標(biāo)志創(chuàng)建了兩個(gè)線程hThreadG7231和hThreadTCPRev。一個(gè)用于語(yǔ)音編解碼,它的線程函數(shù)為G723Proc,?該線程不斷查詢本地有無(wú)編好碼的語(yǔ)音和圖象的碼流,如有,則進(jìn)行H.223打包,然后通過(guò)TCP的端口發(fā)送給對(duì)方。另外一個(gè)線程用于TCP/IP的接收,它的線程函數(shù)為AcceptThreadProcRev,該線程不斷偵?測(cè)TCP/IP端口有無(wú)對(duì)方傳來(lái)的碼流,如有,就接收碼流,進(jìn)行H.223解碼后送入相應(yīng)的緩沖區(qū)。該緩沖區(qū)的內(nèi)容,由語(yǔ)音編解碼線程G723Proc查詢,并送入相應(yīng)的解碼器。由于使用了多線程的編程技術(shù),使得操作系統(tǒng)定時(shí)去服務(wù)語(yǔ)?音編解碼模塊和傳輸模塊,從而保證了通信的不中斷。?
??五、程序源碼?
//基于TCP/IP可視電話主窗口的窗口過(guò)程?
LONG?APIENTRY?MainWndProc(HWND?hWnd,UINT?message,UINT?wParam,?LONG?lParam)?
{?
?static?HANDLE?hThreadG7231,hThreadTCPListen,hThreadTCPRev;?
?DWORDThreadIDG7231,ThreadIDTCPListen,ThreadIDTCPRev;?
?static?THREADPACK?tp;?
?static?THREADPACK?tp1;?
?unsigned?char?Buf[80];?
?CAPSTATUS?capStatus;?
?switch?(message)?
??{?
???case?WM_CREATE:?
?????Init_Wsock(hWnd);?//初始化一些數(shù)據(jù)結(jié)構(gòu)?
?????Init_BS(2,&bs);?
?????vd_tx_pdu.V_S?=?0;vd_tx_pdu.N_S?=?0;?
?????vd_rx_pdu.V_R?=?0;vd_tx_sdu.bytes?=?0;?
?????if(?dnldProg?(?hWnd,?"h324g723.exe")?)?
??????{?
???????//裝入語(yǔ)音編解碼的DSP核心?
???????MessageBox(hWnd,"Load?G.723.1?Kernel?Error","Error",MB_OK);?
???????PostQuitMessage(0);?}?
?????else?
???????MessageBox(hWnd,"Load?G.723.1?Kernel?OK!","Indication",MB_OK);?
?????//創(chuàng)建語(yǔ)音編解碼的線程?
?????parag7231.hWnd?=?hWnd;?
?????hThreadG7231=CreateThread?(NULL,?0,(LPTHREAD_START_ROUTINE)G723Proc,?
???????????????????(G7231DATA?*)?g7231,?
???????????????????CREATE_SUSPENDED,(LPDWORD)&ThreadIDG7231);?
?????if?(!hThreadG7231)?
??????{?
???????wsprintf(Buf,?"Error?in?creating?G7231?thread:?%d",GetLastError());?
???????MessageBox?(hWnd,?Buf,?"WM_CREATE",?MB_OK);}?
?????//創(chuàng)建TCP/IP接收線程?
?????tp1.hWnd?=?hWnd;?
?????hThreadTCPRev?=?CreateThread?(NULL,?0,(LPTHREAD_START_ROUTINE)AcceptThreadProcRev,?
????????????????????(G7231DATA?*)&tp1,CREATE_SUSPENDED,?
????????????????????(LPDWORD)&ThreadIDTCPRev);?
?????if?(!hThreadTCPRev)?
??????{?
???????wsprintf(Buf,?"Error?in?creating?TCP?Receive?thread:?%d",GetLastError());?
???????MessageBox?(hWnd,?Buf,?"WM_CREATE",?MB_OK);}?
?????//開(kāi)始偵聽(tīng)網(wǎng)絡(luò)?
?????SendMessage(hWnd,WM_COMMAND,IDM_LISTEN,NULL);?
?????break;?
???case?WM_VIDEO_ENCODE:?//圖象編碼?
?????if(needencode)EncodeFunction(hWnd);?
?????needencode?=?SendVideoToBuff(&vd_tx_sdu,?buff);?
?????frameMode=TRUE;?
?????capPreview(capWnd,FALSE);?
?????capOverlay(capWnd,FALSE);?
?????capGrabFrameNoStop(capWnd);?
?????break;?
???case?WM_VIDEO_DECODE:?//圖象解碼?
?????Video_Decod_begin?=?1;?
?????play_movie();?
?????Video_Decod_begin?=?0;?
?????break;?
???case?WM_COMMAND:?
?????switch(LOWORD(wParam))?
??????{?
???????case?IDM_CONNECT:?//響應(yīng)對(duì)方的呼叫,接通可視電話?
?????????WskConnect(?hWnd?);?
?????????ResumeThread(hThreadTCPRev);?//運(yùn)行TCP/IP接收線程?
?????????ResumeThread(hThreadG7231);?//運(yùn)行語(yǔ)音編解碼線程?
?????????BeginG7231Codec();?//初始化圖象采集卡,并開(kāi)始采集圖象?
?????????frameMode?=?FALSE;?
?????????capWnd?=?capCreateCaptureWindow((LPSTR)"Capture?Window",?
??????????????????????????WS_CHILD?|?WS_VISIBLE,?
??????????????????????????100,?100,?176,144?,?
??????????????????????????(HWND)?hWnd,?(int)?0);?
?????????capSetCallbackOnError(capWnd,?(FARPROC)ErrorCallbackProc)?;?
?????????capSetCallbackOnStatus(capWnd,?(FARPROC)StatusCallbackProc)?;?
?????????capSetCallbackOnFrame(capWnd,?(FARPROC)FrameCallbackProc)?;?
?????????capDriverConnect(capWnd,?0);?
?????????CenterCaptureWindow(hWnd,?capWnd);?
?????????capDlgVideoSource(capWnd);?
?????????capDlgVideoFormat(capWnd);?
?????????capDlgVideoCompression(capWnd);?
?????????capGetStatus(capWnd,&capStatus,sizeof(CAPSTATUS));?
?????????StartNewVideoChannel(hWnd,?capWnd)?;?
?????????image?=?image_one;?
?????????frameMode?=?TRUE;?
?????????capPreview(capWnd,FALSE);?
?????????capOverlay(capWnd,FALSE);?
?????????capGrabFrameNoStop(capWnd);?
?????????break;?
???????case?IDM_LISTEN:?//撥對(duì)方號(hào)碼,呼叫對(duì)方?
?????????sock?=?socket(?AF_INET,?SOCK_STREAM,?0);?
?????????if?(sock?==?INVALID_SOCKET)?{?
??????????MessageBox(hWnd,?"socket()?failed",?"Error",?MB_OK);?
??????????closesocket(sock);?
??????????break;}?
?????????if?(!FillAddr(hWnd,?&local_sin,?FALSE?))?//獲取TCP/IP地址和端口號(hào)?
??????????break;?
?????????EnableMenuItem(GetMenu(?hWnd?),?IDM_LISTEN,?MF_GRAYED);?
?????????SetWindowText(?hWnd,?"Waiting?for?connection..");?
?????????bind?(?sock?,?(struct?sockaddr?FAR?*)&local_sin,sizeof(local_sin);?
?????????if?(listen(?sock,?MAX_PENDING_CONNECTS?)?<0)?
??????????{?
???????????sprintf(szBuff,?"%d?is?the?error",?
???????????????WSAGetLastError());?MessageBox(hWnd,?szBuff,?"listen(sock)?failed",?
???????????????MB_OK);?
???????????break;}?
?????????tp.hWnd="hWnd;?//開(kāi)始本地的TCP/IP接收線程"?
?????????_beginthread(AcceptThreadProc,0,&tp);?
?????????ResumeThread(hThreadG7231);?//?開(kāi)始本地語(yǔ)音編解碼的線程?
?????????break;?
???????case?IDM_DISCONNECT:?//掛斷可視電話?
?????????CloseG7231Codec();?
?????????SuspendThread(hThreadG7231);?
?????????SuspendThread(hThreadTCPRev);?
?????????WSACleanup();?
?????????Init_Video_Decod_Again();?
?????????capSetCallbackOnError(capWnd,?NULL);?
?????????capSetCallbackOnStatus(capWnd,?NULL);?
?????????InvalidateRect(hWnd,NULL,1);?capSetCallbackOnFrame(capWnd,?NULL);?
?????????capSetCallbackOnVideoStream(capWnd,?NULL);?
?????????capDriverDisconnect(capWnd);?
?????????Init_Wsock(hWnd);?
?????????MessageBox(hWnd,?"Now?closing?the?Video?telephone","",MB_OK);?
?????????SetDisConnectMenus(hWnd);?
?????????SendMessage(hWnd,?WM_COMMAND,IDM_LISTEN,NULL);?
?????????break;?
???????case?IDM_EXIT:?
?????????CloseG7231Codec();?
?????????SendMessage(hWnd,?WM_CLOSE,?0,?0l);?
?????????break;?default:?
??????return?(DefWindowProc(hWnd,?message,?wParam,?lParam));?
??????}?
??????break;?
????case?WM_CLOSE:?
??????if?(IDOK?!="MessageBox("?hWnd,?"OK?to?close?window?",?gszAppName,?
??????????????????MB_ICONQUESTION?|?MB_OKCANCEL?))break?;?
????case?WM_DESTROY:?
??????WSACleanup();?
??????CloseG7231Codec();?
??????TerminateThread(hThreadG7231,0);?
??????TerminateThread(hThreadTCPRev,0);?
??????capSetCallbackOnError(capWnd,?NULL);?
??????capSetCallbackOnStatus(capWnd,?NULL);?
??????capSetCallbackOnFrame(capWnd,?NULL);?
??????capSetCallbackOnVideoStream(capWnd,?NULL);?
??????capDriverDisconnect(capWnd);?
??????FreeAll();?
??????PostQuitMessage(0);?
??????break;?
???default:?/*?Passes?it?on?if?unproccessed?*/?
???????return?(DefWindowProc(hWnd,?message,?wParam,?lParam));?
???}?
?return?(0);?
}?
//主叫方TCP/IP接收線程?
DWORD?WINAPI?AcceptThreadProc(?PTHREADPACK?ptp?)?
{?
?SOCKADDR_IN?acc_sin;?/*?Accept?socket?address?internet?style?*/?
?int?acc_sin_len;?/*?Accept?socket?address?length?*/?
?int?status;?
?acc_sin_len="sizeof(acc_sin);"?
?//調(diào)用阻塞函數(shù)accept,一直到遠(yuǎn)端響應(yīng)為止?
?sock="accept("?sock,(struct?sockaddr?FAR?*)?&acc_sin,(int?FAR?*)?&acc_sin_len?);?
?if?(sock?<?0)?
??{?
???sprintf(szBuff,?"%d?is?the?error",?WSAGetLastError());?
???????MessageBox(ptp->hWnd,?szBuff,?"accept(sock)?failed",?MB_OK);?
???return?(1);?
??}?
?SetConnectMenus(?ptp->hWnd?);?//遠(yuǎn)端提機(jī),可視電話接通?
?BeginG7231Codec();?
?while?(1)?
??{?
???beg1:?
????status?=?recv((SOCKET)sock,?r_mux_buf,MY_MSG_LENGTH,?NO_FLAGS_SET?);?
???if?(status?==?SOCKET_ERROR)?{?
????status?=?WSAGetLastError();?
????if(?status?==?10054?){?
?????MessageBox(ptp->hWnd,"對(duì)方掛斷電話","Indication",?MB_OK);?
?????SendMessage(?ptp->hWnd,?WM_COMMAND,IDM_DISCONNECT,NULL);?
?????_endthread();?
?????return?(1);?
?????}?
????goto?beg1;?
????}?
???if?(status)?{?
?????r_mux_buf[?status?]?=?'\0';?
?????if?(?r_mux_buf_filled?==?1?)?
??????r_mux_buf_overwrite?=?1;?
?????else?
?????r_mux_buf_filled?=?1;?
?????r_mux_buf_length?=?status;?
????}?
???else?
????{?
?????MessageBox(?hWnd,?"Connection?broken",?"Error",?MB_OK);?
?????SendMessage(?ptp->hWnd,?WM_COMMAND,IDM_DISCONNECT,NULL);?
?????_endthread();?
?????return?(2);?
????}?
???demux();?//線路碼流H.223解碼?
???}?
?return?(0);?
}?
//被叫方TCP/IP接收線程?
DWORD?WINAPI?AcceptThreadProcRev(?PTHREADPACK?ptp?)?
{?
?int?status;?
?while?(1)?
??{?
??beg2:?
??status?=?recv((SOCKET)sock,?r_mux_buf,MY_MSG_LENGTH,?NO_FLAGS_SET?);?
??if?(status?==?SOCKET_ERROR)?
???{?
????status?=WSAGetLastError();?
????if(?status?==?10054?)?
?????{?
??????MessageBox(ptp->hWnd,"對(duì)方掛斷電話","Indication",?MB_OK);?
??????SendMessage(?ptp->hWnd,?WM_COMMAND,IDM_DISCONNECT,NULL);?
??????return?(1);?
?????}?
????goto?beg2;?
????}?
??if?(status)?
???{?
????r_mux_buf[?status?]?=?'\0';?
????if(?r_mux_buf_filled?==?1?)?
?????r_mux_buf_overwrite?=?1;?
????else?
?????r_mux_buf_filled?=?1;?
?????r_mux_buf_length?=?status;?
???}?
??else?
???{?
????MessageBox(?hWnd,?"Connection?broken",?"Error",?MB_OK);?
????SendMessage(?ptp->hWnd,?WM_COMMAND,IDM_DISCONNECT,NULL);?
????return?(2);?
???}?
??demux();?
?}?/*?while?(forever)?*/?
?return?(0);?
}?
//語(yǔ)音編解碼線程?
DWORD?WINAPI?G723Proc(G7231DATA?*data)?
{?
?int?i,len;?
?Audio_tx_pduad_tx_pdu;?
?unsigned?char?mux[MAX_MUX_PDU_SIZE];?
?do?
??{?
???len?=?0;?
???//檢測(cè)本地有無(wú)語(yǔ)音,圖象碼流要傳輸?
???i?=?DetectAudioVideoData();?
???switch(i)?
????{?
?????case?AUDIO_ONLY:?//只有語(yǔ)音碼流?
???????AL2_CRC_coder(&ad_tx_sdu,&ad_tx_pdu);?
???????//H.223打包?
???????len?=?AL2_To_MUX(&ad_tx_pdu,?mux);?
???????break;?
?????case?VIDEO_ONLY:?//只有圖象碼流?
???????SDU_To_PDU(&vd_tx_sdu,&vd_tx_pdu);?
???????tx_AL3_I_PDU(&vd_tx_pdu?,&bs?,?1);?//H.223打包?
???????len?=?AL3_To_MUX(&vd_tx_pdu,mux);?
???????break;?
?????case?AUDIO_VIDEO:?//語(yǔ)音和圖象碼流?
???????AL2_CRC_coder(&ad_tx_sdu,&ad_tx_pdu);?
???????SDU_To_PDU(&vd_tx_sdu,&vd_tx_pdu);?
???????tx_AL3_I_PDU(&vd_tx_pdu?,&bs?,?1);?
???????//H.223打包?
???????len?=?AL2_AL3_To_MUX(&ad_tx_pdu,&vd_tx_pdu,mux);?
???????break;?
?????case?NO_AUDIO_VIDEO:?//此刻無(wú)碼流要傳輸?
???????break;?
????}?
???//TCP/IP發(fā)送碼流?
???if(len?!=?0)?
???send((SOCKET)sock,mux,len,0);?
???//是否接收到待解碼的碼流,有就調(diào)用解碼器?
???PutVideoStreamToDecod();?
??}?
?while(1);?
?return?(0);?
}
??作者最近在開(kāi)發(fā)基于Internet網(wǎng)上的可視電話過(guò)程中,碰到了這樣一個(gè)問(wèn)題。在基于Internet網(wǎng)上的可視電話系統(tǒng)中,同時(shí)要進(jìn)行語(yǔ)音采集、語(yǔ)音編解碼、圖象采集、圖象編解碼、語(yǔ)音和圖象?碼流的傳輸,?所有的這些事情,都要并行處理。特別是語(yǔ)音信號(hào),如果進(jìn)行圖象編解碼時(shí)間過(guò)長(zhǎng),語(yǔ)音信號(hào)得不到服務(wù),通話就有間斷,如果圖象或語(yǔ)音處理時(shí)間過(guò)長(zhǎng),而不能及時(shí)的傳輸碼流數(shù)據(jù),通信同樣也會(huì)中斷。這樣就要求我們實(shí)現(xiàn)一種并行編程,在只有一個(gè)CPU的機(jī)器上,也就是要將該CPU時(shí)間按照一定的優(yōu)先準(zhǔn)則分配給各個(gè)事件,定期處理某一事件而不會(huì)在某一事件處理過(guò)長(zhǎng),在32位Windows95或WindowsNT下,我們可以用多線程的編程技術(shù)來(lái)實(shí)現(xiàn)這種并行編程。實(shí)際上這種并行編程在很多場(chǎng)合下都是必須的。例如,在FileManager拷貝文件時(shí),它顯示一個(gè)對(duì)話框,?列出源文件和目標(biāo)文件的名稱,并在對(duì)話框中包含了一個(gè)Cancel按鈕。如果在文件拷貝過(guò)程中,點(diǎn)中Cancel按鈕,就會(huì)終止拷貝。?
??在16位Windows中,實(shí)現(xiàn)這類功能需要在FileCopy循環(huán)內(nèi)部周期性地調(diào)用PeekMessage函數(shù)。如果正在讀一個(gè)很大的數(shù)據(jù)塊,則只有當(dāng)這個(gè)塊讀完以后才能響應(yīng)這個(gè)按鈕動(dòng)作,如果從軟盤讀文件,則要花費(fèi)好幾秒的時(shí)間,由于機(jī)器反應(yīng)太遲鈍,你會(huì)頻繁地點(diǎn)中這個(gè)按鈕,以為系統(tǒng)不知道你想終止這個(gè)操作。如果把FileCopy指令放入另外一個(gè)線程,你就不需要在代碼中放一大堆PeekMessage函數(shù),處理?用戶界面的線程將與它分開(kāi)操作,這樣,點(diǎn)中Cancel按鈕后會(huì)立即得到響應(yīng)。同樣的道理,在應(yīng)用程序中創(chuàng)建一個(gè)單獨(dú)線程來(lái)處理所有打印任務(wù)也是很有用的,這樣,用戶可以在打印處理時(shí)繼續(xù)使用應(yīng)用程序。?
??二、線程的概念?
??為了了解線程的概念,我們必須先討論一下進(jìn)程的概念。?
??一個(gè)進(jìn)程通常定義為程序的一個(gè)實(shí)例。在Win32中,?進(jìn)程占據(jù)4GB的地址空間。與它們?cè)贛S-DOS和16位Windows操作系統(tǒng)中不同,?Win32進(jìn)程是沒(méi)有活力的。這就是說(shuō),一個(gè)Win32進(jìn)程并不執(zhí)行什么指令,它只是占據(jù)著4GB的地址空間,此空間中有應(yīng)用程序EXE文件的?代碼和數(shù)據(jù)。EXE需要的任意DLL也將它們的代碼和數(shù)據(jù)裝入到進(jìn)程的地址空間。除了地址空間,進(jìn)程還占有某些資源,比如文件、動(dòng)態(tài)內(nèi)存分配和線程。當(dāng)進(jìn)程終止時(shí),在它生命期中創(chuàng)建的各種資源將被清除。?
??但是進(jìn)程是沒(méi)有活力的,它只是一個(gè)靜態(tài)的概念。為了讓進(jìn)程完成一些工作,進(jìn)程必須至少占有一個(gè)線程,所以線程是描述進(jìn)程內(nèi)的執(zhí)行,正是線程負(fù)責(zé)執(zhí)行包含在進(jìn)程的地址空間中的代碼。實(shí)際上,單個(gè)進(jìn)程可以包含幾個(gè)線程,?它們可以同時(shí)執(zhí)行進(jìn)程的地址空間中的代碼。為了做到這一點(diǎn),每個(gè)線程有自己的一組CPU寄存器和堆棧。?
??每個(gè)進(jìn)程至少有一個(gè)線程在執(zhí)行其地址空間中的代碼,如果沒(méi)有線程執(zhí)行進(jìn)程?地址空間中的代碼,?進(jìn)程也就沒(méi)有繼續(xù)存在的理由,系統(tǒng)將自動(dòng)清除進(jìn)程及其地址空間。為了運(yùn)行所有這些線程,操作系統(tǒng)為每個(gè)獨(dú)立線程安排一些CPU?時(shí)間,操作系統(tǒng)以輪轉(zhuǎn)方式向線程提供時(shí)間片,這就給人一種假象,好象這些線程都在同時(shí)運(yùn)行。創(chuàng)建一個(gè)Win32進(jìn)程時(shí),它的第一個(gè)線程稱為主線程,它?由系統(tǒng)自動(dòng)生成,然后可由這個(gè)主線程生成額外的線程,這些線程,又可生成更多的線程。?
??三、線程的編程技術(shù)?
??1、編寫線程函數(shù)?
??所有線程必須從一個(gè)指定的函?數(shù)開(kāi)始執(zhí)行,該函數(shù)稱為線程函數(shù),它必須具有下列原型:?
????DWORDWINAPIYourThreadFunc(LPVOIDlpvThreadParm);?
??該函數(shù)輸入一個(gè)LPVOID型的參數(shù),可以是一個(gè)DWORD型的整數(shù),也可以是一個(gè)指向一個(gè)緩沖區(qū)的指針,?返回一個(gè)DWORD型的值。象WinMain函數(shù)一樣,這個(gè)函數(shù)并不由操作系統(tǒng)調(diào)用,?操作系統(tǒng)調(diào)用包含在KERNEL32.DLL中的非C運(yùn)行時(shí)的一個(gè)內(nèi)部函數(shù),如StartOfThread,然后由StartOfThread函數(shù)建立起一個(gè)異常處理框架后,調(diào)用我們的函數(shù)。?
??2、創(chuàng)建一個(gè)線程?
??一個(gè)進(jìn)程的主線程是由操作系統(tǒng)自動(dòng)生成,如果你要讓一個(gè)主線程創(chuàng)建額外的線程,你可以調(diào)用來(lái)CreateThread完成。?
??HANDLECreateThread(LPSECURITY_ATTRIBUTES?lpsa,DWORDcbstack,LPTHREAD_START_ROUTINElpStartAddr,?
LPVOID?lpvThreadParm,DWORDfdwCreate,LPDWORDlpIDThread);?
??其中l(wèi)psa參數(shù)為一個(gè)指向SECURITY_ATTRIBUTES結(jié)構(gòu)的指針。如果想讓對(duì)象為缺省安全屬性的話,可以傳一個(gè)NULL,如果想讓任一個(gè)子進(jìn)程都可繼承一個(gè)該線程對(duì)象句柄,必須指定一個(gè)SECURITY_ATTRIBUTES結(jié)構(gòu),其中bInheritHandle成員初始化為TRUE。參數(shù)cbstack表示線程為自己所用堆棧分配的地址空間大小,0表示采用系統(tǒng)缺省值。?
??參數(shù)lpStartAddr用來(lái)表示新線程開(kāi)始執(zhí)行時(shí)代碼所在函數(shù)的地址,即為線程函數(shù)。lpvThreadParm為傳入線程函數(shù)的參數(shù),fdwCreate參數(shù)指定控制線程創(chuàng)建的附加標(biāo)志,可以取兩種值。如果該參數(shù)為0,線程就會(huì)立即開(kāi)始執(zhí)行,如果該參數(shù)為CREATE_SUSPENDED,則系統(tǒng)產(chǎn)生線程后,初始化CPU,登記CONTEXT結(jié)構(gòu)的成員,準(zhǔn)備好執(zhí)行該線程函數(shù)中的第一條指令,但并不馬上執(zhí)行,而是掛起該線程。最后一個(gè)參數(shù)lpIDThread?是一個(gè)DWORD類型地址,返回賦給該新線程的ID值。?
??3、終止線程?
??如果某線程調(diào)用了ExitThread?函數(shù),就可以終止自己。?
??VOIDExitThread(UINTfuExitCode?);?
??這個(gè)函數(shù)為調(diào)用該函數(shù)的線程設(shè)置了退出碼fuExitCode后,?就終止該線程。調(diào)用TerminateThread函數(shù)亦可終止線程。?
??BOOLTerminateThread(HANDLE?hThread,DWORDdwExitCode);?
??該函數(shù)用來(lái)結(jié)束由hThread參數(shù)指定的線程,?并把dwExitCode設(shè)成該線程的退出碼。當(dāng)某個(gè)線程不在響應(yīng)時(shí),我們可以用其他線程調(diào)用該函數(shù)來(lái)終止這個(gè)不響應(yīng)的線程。?
??4、設(shè)定線程的相對(duì)優(yōu)先級(jí)?
??當(dāng)一個(gè)線程被首次創(chuàng)建時(shí),它的優(yōu)先級(jí)等同于它所屬進(jìn)程的優(yōu)先級(jí)。在單個(gè)進(jìn)程內(nèi)可以通過(guò)調(diào)用SetThreadPriority函數(shù)改變線程的相對(duì)優(yōu)先級(jí)。一個(gè)線程的優(yōu)先級(jí)是相對(duì)于其所屬的進(jìn)程的優(yōu)先級(jí)而言的。?
??BOOLSetThreadPriority(HANDLE?hThread,intnPriority);?
??其中參數(shù)hThread是指向待修改?優(yōu)先級(jí)線程的句柄,nPriority可以是以下的值:?
??THREAD_PRIORITY_LOWEST,?
??THREAD_PRIORITY_BELOW_NORMAL,?
??THREAD_PRIORITY_NORMAL,?
??THREAD_PRIORITY_ABOVE_NORMAL,?
??THREAD_PRIORITY_HIGHEST?
??5、掛起及恢復(fù)線程?
??先前我提到過(guò)可以創(chuàng)建掛起狀態(tài)的線程(通過(guò)傳遞CREATE_SUSPENDED標(biāo)志給函數(shù)CreateThread來(lái)實(shí)現(xiàn))。當(dāng)你這樣做時(shí),系統(tǒng)創(chuàng)建指定線程的核心對(duì)象,創(chuàng)建線程的棧,在CONTEXT結(jié)構(gòu)中初始化線程CPU注冊(cè)成員。然而,線程對(duì)象被分配了一個(gè)初始掛起計(jì)數(shù)值1,這表明了系統(tǒng)將不再分配CPU去執(zhí)行線程。要開(kāi)始執(zhí)行一個(gè)線程,另一個(gè)線程必須調(diào)用ResumeThread并傳遞給它調(diào)用CreateThread時(shí)返回的線程句柄。?
??DWORD?ResumeThread(HANDLEhThread);?
??一個(gè)線程可以被掛起多次。如果一個(gè)線程被掛起3次,?則該線程在它被分配CPU之前必須被恢復(fù)3次。除了在創(chuàng)建線程時(shí)使用CREATE_SUSPENDED標(biāo)志,你還可以用SuspendThread函數(shù)掛起線程。?
??DWORDSuspendThread(HANDLE?hThread);?
四、多線程編程技術(shù)的應(yīng)用?
??我在前面說(shuō)過(guò),為了實(shí)現(xiàn)基于TCP/IP下的可視電話,就必須“并行”地執(zhí)行語(yǔ)音采集、語(yǔ)音編解碼、圖象采集、圖象編解碼以及碼流數(shù)據(jù)的接收與發(fā)送。語(yǔ)音與圖象的采集由硬件采集卡進(jìn)行,我們的程序只需初始化該硬件采集卡,然后實(shí)時(shí)讀取采集數(shù)據(jù)即可,但語(yǔ)音和圖象數(shù)據(jù)的編解碼以及碼流數(shù)據(jù)的傳輸都必須由程序去協(xié)調(diào)執(zhí)行,決不能在某一件事件上處理過(guò)長(zhǎng),必須讓CPU輪流的為各個(gè)事件服務(wù),Windows95下的線程正是滿足這種要求的編程技術(shù)。?
??下面我給出了利用Windows95?環(huán)境下的多線程編程技術(shù)實(shí)現(xiàn)的基于TCP/IP的可視電話的部分源碼,其中包括主窗口過(guò)程函數(shù),以及主叫端與被叫端的TCP/IP接收線程函數(shù)和語(yǔ)音編解碼的線程函數(shù)。由于圖象編解碼的實(shí)時(shí)性比語(yǔ)音處理與傳輸模塊的實(shí)時(shí)性的?要求要低些,所以我以語(yǔ)音編解碼為事件去查詢圖象數(shù)據(jù),然后進(jìn)行圖象編解碼,而沒(méi)有為圖象編解碼去單獨(dú)實(shí)現(xiàn)一個(gè)線程。?
??在主窗口初始化時(shí),?我用CREATE_SUSPENDED標(biāo)志創(chuàng)建了兩個(gè)線程hThreadG7231和hThreadTCPRev。一個(gè)用于語(yǔ)音編解碼,它的線程函數(shù)為G723Proc,?該線程不斷查詢本地有無(wú)編好碼的語(yǔ)音和圖象的碼流,如有,則進(jìn)行H.223打包,然后通過(guò)TCP的端口發(fā)送給對(duì)方。另外一個(gè)線程用于TCP/IP的接收,它的線程函數(shù)為AcceptThreadProcRev,該線程不斷偵?測(cè)TCP/IP端口有無(wú)對(duì)方傳來(lái)的碼流,如有,就接收碼流,進(jìn)行H.223解碼后送入相應(yīng)的緩沖區(qū)。該緩沖區(qū)的內(nèi)容,由語(yǔ)音編解碼線程G723Proc查詢,并送入相應(yīng)的解碼器。由于使用了多線程的編程技術(shù),使得操作系統(tǒng)定時(shí)去服務(wù)語(yǔ)?音編解碼模塊和傳輸模塊,從而保證了通信的不中斷。?
??五、程序源碼?
//基于TCP/IP可視電話主窗口的窗口過(guò)程?
LONG?APIENTRY?MainWndProc(HWND?hWnd,UINT?message,UINT?wParam,?LONG?lParam)?
{?
?static?HANDLE?hThreadG7231,hThreadTCPListen,hThreadTCPRev;?
?DWORDThreadIDG7231,ThreadIDTCPListen,ThreadIDTCPRev;?
?static?THREADPACK?tp;?
?static?THREADPACK?tp1;?
?unsigned?char?Buf[80];?
?CAPSTATUS?capStatus;?
?switch?(message)?
??{?
???case?WM_CREATE:?
?????Init_Wsock(hWnd);?//初始化一些數(shù)據(jù)結(jié)構(gòu)?
?????Init_BS(2,&bs);?
?????vd_tx_pdu.V_S?=?0;vd_tx_pdu.N_S?=?0;?
?????vd_rx_pdu.V_R?=?0;vd_tx_sdu.bytes?=?0;?
?????if(?dnldProg?(?hWnd,?"h324g723.exe")?)?
??????{?
???????//裝入語(yǔ)音編解碼的DSP核心?
???????MessageBox(hWnd,"Load?G.723.1?Kernel?Error","Error",MB_OK);?
???????PostQuitMessage(0);?}?
?????else?
???????MessageBox(hWnd,"Load?G.723.1?Kernel?OK!","Indication",MB_OK);?
?????//創(chuàng)建語(yǔ)音編解碼的線程?
?????parag7231.hWnd?=?hWnd;?
?????hThreadG7231=CreateThread?(NULL,?0,(LPTHREAD_START_ROUTINE)G723Proc,?
???????????????????(G7231DATA?*)?g7231,?
???????????????????CREATE_SUSPENDED,(LPDWORD)&ThreadIDG7231);?
?????if?(!hThreadG7231)?
??????{?
???????wsprintf(Buf,?"Error?in?creating?G7231?thread:?%d",GetLastError());?
???????MessageBox?(hWnd,?Buf,?"WM_CREATE",?MB_OK);}?
?????//創(chuàng)建TCP/IP接收線程?
?????tp1.hWnd?=?hWnd;?
?????hThreadTCPRev?=?CreateThread?(NULL,?0,(LPTHREAD_START_ROUTINE)AcceptThreadProcRev,?
????????????????????(G7231DATA?*)&tp1,CREATE_SUSPENDED,?
????????????????????(LPDWORD)&ThreadIDTCPRev);?
?????if?(!hThreadTCPRev)?
??????{?
???????wsprintf(Buf,?"Error?in?creating?TCP?Receive?thread:?%d",GetLastError());?
???????MessageBox?(hWnd,?Buf,?"WM_CREATE",?MB_OK);}?
?????//開(kāi)始偵聽(tīng)網(wǎng)絡(luò)?
?????SendMessage(hWnd,WM_COMMAND,IDM_LISTEN,NULL);?
?????break;?
???case?WM_VIDEO_ENCODE:?//圖象編碼?
?????if(needencode)EncodeFunction(hWnd);?
?????needencode?=?SendVideoToBuff(&vd_tx_sdu,?buff);?
?????frameMode=TRUE;?
?????capPreview(capWnd,FALSE);?
?????capOverlay(capWnd,FALSE);?
?????capGrabFrameNoStop(capWnd);?
?????break;?
???case?WM_VIDEO_DECODE:?//圖象解碼?
?????Video_Decod_begin?=?1;?
?????play_movie();?
?????Video_Decod_begin?=?0;?
?????break;?
???case?WM_COMMAND:?
?????switch(LOWORD(wParam))?
??????{?
???????case?IDM_CONNECT:?//響應(yīng)對(duì)方的呼叫,接通可視電話?
?????????WskConnect(?hWnd?);?
?????????ResumeThread(hThreadTCPRev);?//運(yùn)行TCP/IP接收線程?
?????????ResumeThread(hThreadG7231);?//運(yùn)行語(yǔ)音編解碼線程?
?????????BeginG7231Codec();?//初始化圖象采集卡,并開(kāi)始采集圖象?
?????????frameMode?=?FALSE;?
?????????capWnd?=?capCreateCaptureWindow((LPSTR)"Capture?Window",?
??????????????????????????WS_CHILD?|?WS_VISIBLE,?
??????????????????????????100,?100,?176,144?,?
??????????????????????????(HWND)?hWnd,?(int)?0);?
?????????capSetCallbackOnError(capWnd,?(FARPROC)ErrorCallbackProc)?;?
?????????capSetCallbackOnStatus(capWnd,?(FARPROC)StatusCallbackProc)?;?
?????????capSetCallbackOnFrame(capWnd,?(FARPROC)FrameCallbackProc)?;?
?????????capDriverConnect(capWnd,?0);?
?????????CenterCaptureWindow(hWnd,?capWnd);?
?????????capDlgVideoSource(capWnd);?
?????????capDlgVideoFormat(capWnd);?
?????????capDlgVideoCompression(capWnd);?
?????????capGetStatus(capWnd,&capStatus,sizeof(CAPSTATUS));?
?????????StartNewVideoChannel(hWnd,?capWnd)?;?
?????????image?=?image_one;?
?????????frameMode?=?TRUE;?
?????????capPreview(capWnd,FALSE);?
?????????capOverlay(capWnd,FALSE);?
?????????capGrabFrameNoStop(capWnd);?
?????????break;?
???????case?IDM_LISTEN:?//撥對(duì)方號(hào)碼,呼叫對(duì)方?
?????????sock?=?socket(?AF_INET,?SOCK_STREAM,?0);?
?????????if?(sock?==?INVALID_SOCKET)?{?
??????????MessageBox(hWnd,?"socket()?failed",?"Error",?MB_OK);?
??????????closesocket(sock);?
??????????break;}?
?????????if?(!FillAddr(hWnd,?&local_sin,?FALSE?))?//獲取TCP/IP地址和端口號(hào)?
??????????break;?
?????????EnableMenuItem(GetMenu(?hWnd?),?IDM_LISTEN,?MF_GRAYED);?
?????????SetWindowText(?hWnd,?"Waiting?for?connection..");?
?????????bind?(?sock?,?(struct?sockaddr?FAR?*)&local_sin,sizeof(local_sin);?
?????????if?(listen(?sock,?MAX_PENDING_CONNECTS?)?<0)?
??????????{?
???????????sprintf(szBuff,?"%d?is?the?error",?
???????????????WSAGetLastError());?MessageBox(hWnd,?szBuff,?"listen(sock)?failed",?
???????????????MB_OK);?
???????????break;}?
?????????tp.hWnd="hWnd;?//開(kāi)始本地的TCP/IP接收線程"?
?????????_beginthread(AcceptThreadProc,0,&tp);?
?????????ResumeThread(hThreadG7231);?//?開(kāi)始本地語(yǔ)音編解碼的線程?
?????????break;?
???????case?IDM_DISCONNECT:?//掛斷可視電話?
?????????CloseG7231Codec();?
?????????SuspendThread(hThreadG7231);?
?????????SuspendThread(hThreadTCPRev);?
?????????WSACleanup();?
?????????Init_Video_Decod_Again();?
?????????capSetCallbackOnError(capWnd,?NULL);?
?????????capSetCallbackOnStatus(capWnd,?NULL);?
?????????InvalidateRect(hWnd,NULL,1);?capSetCallbackOnFrame(capWnd,?NULL);?
?????????capSetCallbackOnVideoStream(capWnd,?NULL);?
?????????capDriverDisconnect(capWnd);?
?????????Init_Wsock(hWnd);?
?????????MessageBox(hWnd,?"Now?closing?the?Video?telephone","",MB_OK);?
?????????SetDisConnectMenus(hWnd);?
?????????SendMessage(hWnd,?WM_COMMAND,IDM_LISTEN,NULL);?
?????????break;?
???????case?IDM_EXIT:?
?????????CloseG7231Codec();?
?????????SendMessage(hWnd,?WM_CLOSE,?0,?0l);?
?????????break;?default:?
??????return?(DefWindowProc(hWnd,?message,?wParam,?lParam));?
??????}?
??????break;?
????case?WM_CLOSE:?
??????if?(IDOK?!="MessageBox("?hWnd,?"OK?to?close?window?",?gszAppName,?
??????????????????MB_ICONQUESTION?|?MB_OKCANCEL?))break?;?
????case?WM_DESTROY:?
??????WSACleanup();?
??????CloseG7231Codec();?
??????TerminateThread(hThreadG7231,0);?
??????TerminateThread(hThreadTCPRev,0);?
??????capSetCallbackOnError(capWnd,?NULL);?
??????capSetCallbackOnStatus(capWnd,?NULL);?
??????capSetCallbackOnFrame(capWnd,?NULL);?
??????capSetCallbackOnVideoStream(capWnd,?NULL);?
??????capDriverDisconnect(capWnd);?
??????FreeAll();?
??????PostQuitMessage(0);?
??????break;?
???default:?/*?Passes?it?on?if?unproccessed?*/?
???????return?(DefWindowProc(hWnd,?message,?wParam,?lParam));?
???}?
?return?(0);?
}?
//主叫方TCP/IP接收線程?
DWORD?WINAPI?AcceptThreadProc(?PTHREADPACK?ptp?)?
{?
?SOCKADDR_IN?acc_sin;?/*?Accept?socket?address?internet?style?*/?
?int?acc_sin_len;?/*?Accept?socket?address?length?*/?
?int?status;?
?acc_sin_len="sizeof(acc_sin);"?
?//調(diào)用阻塞函數(shù)accept,一直到遠(yuǎn)端響應(yīng)為止?
?sock="accept("?sock,(struct?sockaddr?FAR?*)?&acc_sin,(int?FAR?*)?&acc_sin_len?);?
?if?(sock?<?0)?
??{?
???sprintf(szBuff,?"%d?is?the?error",?WSAGetLastError());?
???????MessageBox(ptp->hWnd,?szBuff,?"accept(sock)?failed",?MB_OK);?
???return?(1);?
??}?
?SetConnectMenus(?ptp->hWnd?);?//遠(yuǎn)端提機(jī),可視電話接通?
?BeginG7231Codec();?
?while?(1)?
??{?
???beg1:?
????status?=?recv((SOCKET)sock,?r_mux_buf,MY_MSG_LENGTH,?NO_FLAGS_SET?);?
???if?(status?==?SOCKET_ERROR)?{?
????status?=?WSAGetLastError();?
????if(?status?==?10054?){?
?????MessageBox(ptp->hWnd,"對(duì)方掛斷電話","Indication",?MB_OK);?
?????SendMessage(?ptp->hWnd,?WM_COMMAND,IDM_DISCONNECT,NULL);?
?????_endthread();?
?????return?(1);?
?????}?
????goto?beg1;?
????}?
???if?(status)?{?
?????r_mux_buf[?status?]?=?'\0';?
?????if?(?r_mux_buf_filled?==?1?)?
??????r_mux_buf_overwrite?=?1;?
?????else?
?????r_mux_buf_filled?=?1;?
?????r_mux_buf_length?=?status;?
????}?
???else?
????{?
?????MessageBox(?hWnd,?"Connection?broken",?"Error",?MB_OK);?
?????SendMessage(?ptp->hWnd,?WM_COMMAND,IDM_DISCONNECT,NULL);?
?????_endthread();?
?????return?(2);?
????}?
???demux();?//線路碼流H.223解碼?
???}?
?return?(0);?
}?
//被叫方TCP/IP接收線程?
DWORD?WINAPI?AcceptThreadProcRev(?PTHREADPACK?ptp?)?
{?
?int?status;?
?while?(1)?
??{?
??beg2:?
??status?=?recv((SOCKET)sock,?r_mux_buf,MY_MSG_LENGTH,?NO_FLAGS_SET?);?
??if?(status?==?SOCKET_ERROR)?
???{?
????status?=WSAGetLastError();?
????if(?status?==?10054?)?
?????{?
??????MessageBox(ptp->hWnd,"對(duì)方掛斷電話","Indication",?MB_OK);?
??????SendMessage(?ptp->hWnd,?WM_COMMAND,IDM_DISCONNECT,NULL);?
??????return?(1);?
?????}?
????goto?beg2;?
????}?
??if?(status)?
???{?
????r_mux_buf[?status?]?=?'\0';?
????if(?r_mux_buf_filled?==?1?)?
?????r_mux_buf_overwrite?=?1;?
????else?
?????r_mux_buf_filled?=?1;?
?????r_mux_buf_length?=?status;?
???}?
??else?
???{?
????MessageBox(?hWnd,?"Connection?broken",?"Error",?MB_OK);?
????SendMessage(?ptp->hWnd,?WM_COMMAND,IDM_DISCONNECT,NULL);?
????return?(2);?
???}?
??demux();?
?}?/*?while?(forever)?*/?
?return?(0);?
}?
//語(yǔ)音編解碼線程?
DWORD?WINAPI?G723Proc(G7231DATA?*data)?
{?
?int?i,len;?
?Audio_tx_pduad_tx_pdu;?
?unsigned?char?mux[MAX_MUX_PDU_SIZE];?
?do?
??{?
???len?=?0;?
???//檢測(cè)本地有無(wú)語(yǔ)音,圖象碼流要傳輸?
???i?=?DetectAudioVideoData();?
???switch(i)?
????{?
?????case?AUDIO_ONLY:?//只有語(yǔ)音碼流?
???????AL2_CRC_coder(&ad_tx_sdu,&ad_tx_pdu);?
???????//H.223打包?
???????len?=?AL2_To_MUX(&ad_tx_pdu,?mux);?
???????break;?
?????case?VIDEO_ONLY:?//只有圖象碼流?
???????SDU_To_PDU(&vd_tx_sdu,&vd_tx_pdu);?
???????tx_AL3_I_PDU(&vd_tx_pdu?,&bs?,?1);?//H.223打包?
???????len?=?AL3_To_MUX(&vd_tx_pdu,mux);?
???????break;?
?????case?AUDIO_VIDEO:?//語(yǔ)音和圖象碼流?
???????AL2_CRC_coder(&ad_tx_sdu,&ad_tx_pdu);?
???????SDU_To_PDU(&vd_tx_sdu,&vd_tx_pdu);?
???????tx_AL3_I_PDU(&vd_tx_pdu?,&bs?,?1);?
???????//H.223打包?
???????len?=?AL2_AL3_To_MUX(&ad_tx_pdu,&vd_tx_pdu,mux);?
???????break;?
?????case?NO_AUDIO_VIDEO:?//此刻無(wú)碼流要傳輸?
???????break;?
????}?
???//TCP/IP發(fā)送碼流?
???if(len?!=?0)?
???send((SOCKET)sock,mux,len,0);?
???//是否接收到待解碼的碼流,有就調(diào)用解碼器?
???PutVideoStreamToDecod();?
??}?
?while(1);?
?return?(0);?
}
轉(zhuǎn)載于:https://www.cnblogs.com/qq78292959/archive/2008/10/13/2077108.html
總結(jié)
以上是生活随笔為你收集整理的Windows下多线程编程技术及其实现的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 查找xml文件中某接点的值
- 下一篇: UML轻松入门--类和对象