日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

孙鑫MFC笔记之十四--多线程同步与异步套接字编程

發(fā)布時(shí)間:2023/12/18 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 孙鑫MFC笔记之十四--多线程同步与异步套接字编程 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

線程同步有三種方式:

1.????? 互斥對(duì)象涉及方法:

HANDLE hMutex=CreateMutex(NULL,FALSE,NULL);?//第二個(gè)參數(shù)為FALSE,將互斥對(duì)象聲明為空閑狀態(tài)

WaitForSingleObject(hMutex,INFINITE);?//第二個(gè)參數(shù)為INFINITE表示一直等待,直到擁有互斥對(duì)象

ReleaseMutex(hMutex);?//使用完了,將互斥對(duì)象還給操作系統(tǒng)

????? 具體代碼及各種情況的分析見上一章,這里就不再敘述。

?

2.????? 事件對(duì)象:

事件對(duì)象也屬于內(nèi)核對(duì)象,包含一個(gè)使用計(jì)數(shù),一個(gè)用于指明該事件是一個(gè)自動(dòng)重置的事件還是一個(gè)人工重置的事件的布爾值,另一個(gè)用于指明該事件處于可用狀態(tài)還是不可用的布爾值。

有兩種不同類型的事件對(duì)象。一種是人工重置的事件,另一種是自動(dòng)重置的事件。當(dāng)人工重置的事件得到通知時(shí),等待該事件的所有線程均變?yōu)榭烧{(diào)度線程。當(dāng)一個(gè)自動(dòng)重置的事件得到通知時(shí),等待該事件的線程中只有一個(gè)線程變?yōu)榭烧{(diào)度線程。所以優(yōu)先選擇自動(dòng)重置的事件

說(shuō)明:CreateEvent方法第一個(gè)參數(shù)是關(guān)于的安全的結(jié)構(gòu)體,一般設(shè)置為NULL;第二個(gè)參數(shù)表示是人工重置還是自動(dòng)重置,TRUE代表人工重置,如果為TURE需要調(diào)用這個(gè)ResetEvent函數(shù)來(lái)人工重置為非信號(hào)狀態(tài);第三個(gè)參數(shù)表示初始化狀態(tài),如果為TURE初始化狀態(tài)信號(hào)為有信號(hào)的;第四個(gè)參數(shù)表示Event名稱,NULL的話,默認(rèn)。

BOOL ResetEvent(
? HANDLE
?hEvent?? // handle to event
);

?

?BOOL SetEvent(
? HANDLE
?hEvent?? // handle to event
);//設(shè)置信號(hào)為有信號(hào)狀態(tài)

?HANDLE g_hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);

?

以下代碼會(huì)出現(xiàn)異常:在將CreateEvent的第二個(gè)參數(shù)設(shè)置為人工重置的時(shí)候,因?yàn)榈却撌录乃芯€程均變?yōu)榭烧{(diào)度線程,所以發(fā)現(xiàn)售票實(shí)例程序最終會(huì)出現(xiàn)0。所以最好還是選擇自動(dòng)重置的事件。

#include?<windows.h>

#include?<iostream.h>

?

DWORD?WINAPI ThreadProc1(LPVOID lpParameter);

DWORD?WINAPI ThreadProc2(LPVOID lpParameter);

?

int?ticket=100;

HANDLE?g_hEvent;

?

void?main()

{

??????g_hEvent=CreateEvent(NULL,TRUE,TRUE,NULL);//初始化代碼應(yīng)放在創(chuàng)建線程以前

?

????? HANDLE handle1=CreateThread(NULL,0,ThreadProc1,NULL,0,NULL);

????? HANDLE handle2=CreateThread(NULL,0,ThreadProc2,NULL,0,NULL);

????? CloseHandle(handle1);

????? CloseHandle(handle2);?

??????g_hEvent=CreateEvent(NULL,TRUE,TRUE,NULL);//放在最前面

????? Sleep(4000);

????? CloseHandle(g_hEvent);

}

?

DWORD?WINAPI ThreadProc1(LPVOID lpParameter)

{

????? while(TRUE)

????? {

?????????? WaitForSingleObject(g_hEvent,INFINITE); //第二個(gè)參數(shù)為INFINITE表示一直等待,直到擁有互斥對(duì)象

?????????? if(ticket>0)

?????????? {

???????????????? Sleep(1);

???????????????? cout<<"thread1 sale the ticket id is:"<<ticket--<<endl;

?????????? }

?????????? else

?????????????????break;

????? }

????? return 0;

}

?

DWORD?WINAPI ThreadProc2(LPVOID lpParameter)

{

????? while(TRUE)

????? {

?????????? WaitForSingleObject(g_hEvent,INFINITE);

?????????? if(ticket>0)

?????????? {

???????????????? Sleep(1);

???????????????? cout<<"thread2 sale the ticket id is:"<<ticket--<<endl;

?????????? }

?????????? else

?????????????????break;

????? }

????? return 0;

}

說(shuō)明:

如果一個(gè)線程循環(huán)內(nèi)部已經(jīng)調(diào)用了WaitForSingleObject(g_hEvent,INFINITE);但是在單個(gè)循環(huán)完成前沒(méi)有調(diào)用SetEvent(g_hEvent)將狀態(tài)設(shè)置成可用的話,下一次進(jìn)入循環(huán)時(shí)再次調(diào)用WaitForSingleObject時(shí)發(fā)現(xiàn)狀態(tài)不可用,所以一直等待,代碼例子將上面的代碼g_hEvent=CreateEvent(NULL,TRUE,TRUE,NULL);修改為g_hEvent=CreateEvent(NULL,FALSE,TRUE,NULL);則會(huì)出現(xiàn)這個(gè)問(wèn)題,其結(jié)果就是僅僅線程1售出了100這張票。如果在循環(huán)退出前調(diào)用SetEvent(g_hEvent);則問(wèn)題可以解決。

?

綜上所述,涉及到的方法:

HANDLE g_hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);

WaitForSingleObject(g_hEvent,INFINITE); //等待事件,如果事件可用,運(yùn)行下面的代碼,并且將事件狀態(tài)設(shè)置為不可用狀態(tài),如果事件不可用,一直等待。

SetEvent(g_hEvent)? //將事件設(shè)置為可用的狀態(tài)

ResetEvent(g_hEvent) //將事件設(shè)置為不可用狀態(tài)

一般情況下WaitForSingleObject和SetEvent配對(duì)使用。

?

3.????? 關(guān)鍵代碼段:

?

關(guān)鍵代碼段(臨界區(qū))是指一個(gè)小代碼段,在代碼能夠執(zhí)行前,它必須獨(dú)占對(duì)某些資源的訪問(wèn)權(quán)。

??????????? 可以將關(guān)鍵代碼段想象成電話亭資源:

?????????? CRITICAL_SECTION g_cs;

?????????? InitializeCriticalSection(&g_cs); //創(chuàng)建電話亭資源,一般放在構(gòu)造函數(shù)中

?????????? EnterCriticalSection(&g_cs); //判斷關(guān)鍵資源所有權(quán)是否可用,可用則進(jìn)入

?????????? LeaveCriticalSection(&g_cs);使用完關(guān)鍵資源后,釋放所有權(quán)

?????????? DeleteCriticalSection(&g_cs); //銷毀電話亭資源,一般放在析構(gòu)函數(shù)中

????? 其中InitializeCriticalSection和DeleteCriticalSection配對(duì)使用;

????? EnterCriticalSection和LeaveCriticalSection配對(duì)使用,中間存放訪問(wèn)共享資源的代碼。?

4.????? 互斥對(duì)象、事件對(duì)象與關(guān)鍵代碼段的比較

互斥對(duì)象和事件對(duì)象屬于內(nèi)核對(duì)象,利用內(nèi)核對(duì)象進(jìn)行線程同步,速度較慢,但利用互斥對(duì)象和事件對(duì)象這樣的內(nèi)核對(duì)象,可以在多個(gè)進(jìn)程中的各個(gè)線程間進(jìn)行同步。

關(guān)鍵代碼段是工作在用戶方式下,同步速度較快,但在使用關(guān)鍵代碼段時(shí),很容易進(jìn)入死鎖狀態(tài),因?yàn)樵诘却M(jìn)入關(guān)鍵代碼段時(shí)無(wú)法設(shè)定超時(shí)值。

?

5.????? 死鎖:

哲學(xué)家進(jìn)餐的問(wèn)題:每個(gè)哲學(xué)家手中只有一根筷子,要進(jìn)餐必須有兩根,但誰(shuí)也不愿意先給出自己的那根給別人。大家都處于等待狀態(tài)。

線程1擁有了臨界區(qū)對(duì)象A,等待臨界區(qū)對(duì)象B的擁有權(quán),線程2擁有了臨界區(qū)對(duì)象B,等待臨界區(qū)對(duì)象A的擁有權(quán),就造成了死鎖。

死鎖代碼:

#include?<windows.h>

#include?<iostream.h>

?

DWORD?WINAPI ThreadProc1(LPVOID lpParameter);

DWORD?WINAPI ThreadProc2(LPVOID lpParameter);

?

int ticket=100;

?

//創(chuàng)建兩個(gè)關(guān)鍵資源

CRITICAL_SECTION g_cs1;

CRITICAL_SECTION g_cs2;

?

void?main()

{

????? HANDLE handle1=CreateThread(NULL,0,ThreadProc1,NULL,0,NULL);

????? HANDLE handle2=CreateThread(NULL,0,ThreadProc2,NULL,0,NULL);

????? CloseHandle(handle1);

????? CloseHandle(handle2);???

????? InitializeCriticalSection(&g_cs1);//初始化要放在前面

????? InitializeCriticalSection(&g_cs2);

?

????? Sleep(4000);

????? DeleteCriticalSection(&g_cs1);

????? DeleteCriticalSection(&g_cs2);

}

?

DWORD?WINAPI ThreadProc1(LPVOID lpParameter)

{

????? while(TRUE)

????? {

?????????? EnterCriticalSection(&g_cs1);

?????????? Sleep(1);

?????????? EnterCriticalSection(&g_cs2);

?????????? if(ticket>0)

?????????? {

???????????????? Sleep(1);

???????????????? cout<<"thread1 sale the ticket id is:"<<ticket--<<endl;

?????????? }

?????????? else

?????????????????break;

?????????? LeaveCriticalSection(&g_cs1);

?????????? LeaveCriticalSection(&g_cs2);

????? }

????? return 0;

}

?

DWORD?WINAPI ThreadProc2(LPVOID lpParameter)

{

????? while(TRUE)

????? {

?????????? EnterCriticalSection(&g_cs2);

?????????? Sleep(1);

?????????? EnterCriticalSection(&g_cs1);

?????????? if(ticket>0)

?????????? {

???????????????? Sleep(1);

???????????????? cout<<"thread2 sale the ticket id is:"<<ticket--<<endl;

?????????? }

?????????? else

?????????????????break;

?????????? LeaveCriticalSection(&g_cs1);

?????????? LeaveCriticalSection(&g_cs2);

????? }

????? return 0;

}

?

說(shuō)明:首先線程1得到資源1的所有權(quán),然后睡眠1毫秒,線程1就讓出了執(zhí)行權(quán),這個(gè)時(shí)候線程2得到執(zhí)行權(quán),運(yùn)行,得到資源2的所有權(quán),線程2然后睡眠1毫秒,線程2就讓出了執(zhí)行權(quán),這個(gè)時(shí)候線程1得到執(zhí)行權(quán),線程1繼續(xù)執(zhí)行,想得到資源2的資源,但發(fā)現(xiàn)資源2被線程1所占用,等待。當(dāng)線程1的事件片過(guò)了以后,線程2得到執(zhí)行權(quán),繼續(xù)執(zhí)行,想得到資源1的所有權(quán),但發(fā)現(xiàn)資源1被線程1占用,所以也繼續(xù)等待,這樣線程1和線程2都互相等待,造成死鎖。

?

1.??????異步套接字編程:

Windows套接字在兩種模式下執(zhí)行I/O操作,阻塞和非阻塞在阻塞模式下,在I/O操作完成前,執(zhí)行操作的Winsock函數(shù)會(huì)一直等待下去,不會(huì)立即返回程序(將控制權(quán)交還給程序)。而在非阻塞模式下,Winsock函數(shù)無(wú)論如何都會(huì)立即返回。采用異步套接字,可有效改善程序的運(yùn)行性能。

Windows Sockets為了支持Windows消息驅(qū)動(dòng)機(jī)制,使應(yīng)用程序開發(fā)者能夠方便地處理網(wǎng)絡(luò)通信,它對(duì)網(wǎng)絡(luò)事件采用了基于消息的異步存取策略。

Windows Sockets的異步選擇函數(shù)WSAAsyncSelect()提供了消息機(jī)制的網(wǎng)絡(luò)事件選擇,當(dāng)使用它登記的網(wǎng)絡(luò)事件發(fā)生時(shí),Windows應(yīng)用程序相應(yīng)的窗口函數(shù)將收到一個(gè)消息,消息中指示了發(fā)生的網(wǎng)絡(luò)事件,以及與事件相關(guān)的一些信息。

??????在上一章中編寫的Chat程序中,因?yàn)榻邮粘绦蚍旁诹艘粋€(gè)線程中,所以雖然它是阻塞的,也沒(méi)有影響到主線程的運(yùn)行性能。?

2.??????編寫基于異步套接字的聊天室程序:

?

相關(guān)函數(shù):

int WSAEnumProtocols( LPINT lpiProtocols, LPWSAPROTOCOL_INFO lpProtocolBuffer, ILPDWORD lpdwBufferLength );
Win32平臺(tái)支持多種不同的網(wǎng)絡(luò)協(xié)議,采用Winsock2,就可以編寫可直接使用任何一種協(xié)議的網(wǎng)絡(luò)應(yīng)用程序了。通過(guò)WSAEnumProtocols函數(shù)可以獲得系統(tǒng)中安裝的網(wǎng)絡(luò)協(xié)議的相關(guān)信息。
lpiProtocols,一個(gè)以NULL結(jié)尾的協(xié)議標(biāo)識(shí)號(hào)數(shù)組。這個(gè)參數(shù)是可選的,如果lpiProtocols為NULL,則返回所有可用協(xié)議的信息,否則,只返回?cái)?shù)組中列出的協(xié)議信息。
lpProtocolBuffer,[out],一個(gè)用WSAPROTOCOL_INFO結(jié)構(gòu)體填充的緩沖區(qū)。 WSAPROTOCOL_INFO結(jié)構(gòu)體用來(lái)存放或得到一個(gè)指定協(xié)議的完整信息。
lpdwBufferLength,[in, out],在輸入時(shí),指定傳遞給WSAEnumProtocols()函數(shù)的lpProtocolBuffer緩沖區(qū)的長(zhǎng)度;在輸出時(shí),存有獲取所有請(qǐng)求信息需傳遞給WSAEnumProtocols ()函數(shù)的最小緩沖區(qū)長(zhǎng)度。這個(gè)函數(shù)不能重復(fù)調(diào)用,傳入的緩沖區(qū)必須足夠大以便能存放所有的元素。這個(gè)規(guī)定降低了該函數(shù)的復(fù)雜度,并且由于一個(gè) 機(jī)器上裝載的協(xié)議數(shù)目往往是很少的,所以并不會(huì)產(chǎn)生問(wèn)題。

?

???SOCKET WSASocket( int af, int type, int protocol, LPWSAPROTOCOL_INFO lpProtocolInfo, GROUP g, DWORD dwFlags );
前三個(gè)參數(shù)和socket()函數(shù)的前三個(gè)參數(shù)含義一樣。
lpProtocolInfo,一個(gè)指向WSAPROTOCOL_INFO結(jié)構(gòu)體的指針,該結(jié)構(gòu)定義了所創(chuàng)建的套接字的特性。如果lpProtocolInfo為NULL,則WinSock2 DLL使用前三個(gè)參數(shù)來(lái)決定使用哪一個(gè)服務(wù)提供者,它選擇能夠支持規(guī)定的地址族、套接字類型和協(xié)議值的第一個(gè)傳輸提供者。如果lpProtocolInfo不為NULL,則套接字綁定到與指定的結(jié)構(gòu)WSAPROTOCOL_INFO相關(guān)的提供者。
g,保留的。
dwFlags,套接字屬性的描述。

?

?

?

a.??????因?yàn)镸FC自帶的AfxSocketInit函數(shù)初始化支持的是1.1版本的套接字,不適合異步套接字,我們需要調(diào)用的是Winsock2版本的套接字,那么加載套接字庫(kù)的過(guò)程只能使用WSAStartup了。在CChatAppInitInstance初始化函數(shù)中添加:

?

WORD wVersionRequested;

??????WSADATA wsaData;

??????int?err;?????

??????wVersionRequested = MAKEWORD( 2, 2 );????

??????err = WSAStartup( wVersionRequested, &wsaData );

??????if?( err != 0 ) {

???????????return;

??????}?????????

??????if?( LOBYTE( wsaData.wVersion ) != 2 ||

????????HIBYTE( wsaData.wVersion ) != 2 ) {??????????

???????????WSACleanup( );

???????????return;

}

b.??????在StdAfx.h里添加#include <winsock2.h>,在setting里添加ws2_32.lib庫(kù)文件。

c.??????給CChatApp類添加析構(gòu)函數(shù),在其中添加WSACleanup來(lái)終止對(duì)套接字庫(kù)的使用。

d.??????給CChatDlg類添加成員變量SOCKET m_socket,并在構(gòu)造函數(shù)中初始化為0

e.??????給CChatDlg類添加析構(gòu)函數(shù),添加:

if(m_socket) //判斷socket是否有值

??????closesocket(m_socket);

f.???????創(chuàng)建初始化函數(shù)InitSocket(),代碼如下:

說(shuō)明:在Winsock2版本中提供的WSASocket這樣一個(gè)擴(kuò)展方法用于創(chuàng)建套接字,對(duì)應(yīng)于socket方法;bind方法在winsock2中沒(méi)有提供相應(yīng)的擴(kuò)展方法。然后調(diào)用WSAAsyncSelect方法請(qǐng)求一個(gè)windows基于消息的網(wǎng)絡(luò)事件通知。

m_socket=WSASocket(AF_INET,SOCK_DGRAM,0,NULL,0,0);

???if(INVALID_SOCKET==m_socket)

???{

????????MessageBox("創(chuàng)建套接字失敗!");

????????return?FALSE;

???}

??

???SOCKADDR_IN addrSock;

???addrSock.sin_addr.S_un.S_addr=htonl(INADDR_ANY);

???addrSock.sin_family=AF_INET;

???addrSock.sin_port=htons(1234);

??

???int?retVal;

???retVal=bind(m_socket,(SOCKADDR*)&addrSock,sizeof(SOCKADDR));

???if(SOCKET_ERROR==retVal)

???{

????????MessageBox("套接字綁定失敗!");

????????return?FALSE;

???}

說(shuō)明:WSAAsyncSelect方法第二個(gè)參數(shù)表示網(wǎng)絡(luò)事件發(fā)生時(shí)用來(lái)接收消息的窗口,第三個(gè)參數(shù)表示處理響應(yīng)的消息,第四個(gè)參數(shù)表示網(wǎng)絡(luò)事件類型,采用或操作。我們當(dāng)前采用讀這樣一個(gè)事件,網(wǎng)絡(luò)上一旦有數(shù)據(jù)到來(lái)的時(shí)候就會(huì)觸發(fā)這個(gè)事件,系統(tǒng)就會(huì)通過(guò)我們自定義的消息UM_SOCK來(lái)通知我們進(jìn)行處理if(SOCKET_ERROR==WSAAsyncSelect(m_socket,m_hWnd,UM_SOCK,FD_READ))

????????????{

?????????????????MessageBox("注冊(cè)網(wǎng)絡(luò)讀取事件失敗!");

?????????????????return?FALSE;

???};

return?TRUE;

g.??????消息響應(yīng)函數(shù)的處理:

1.??????創(chuàng)建自定以的消息UM_SOCK,注意:在消息響應(yīng)函數(shù)的申明中還是要添加WPARAMLPARAM參數(shù),因?yàn)榫W(wǎng)絡(luò)上的數(shù)據(jù)是通過(guò)這兩個(gè)參數(shù)傳遞給消息響應(yīng)函數(shù)進(jìn)行處理的。

2.??????參看MSDNWSAAsyncSelect方法的說(shuō)明如下:
When one of the nominated network events occurs on the specified socket?s, the application's window?hWnd?receives message?wMsg.?The?wParam?parameter identifies the socket on which a network event has occurred. The low word of?lParamspecifies the network event that has occurred. The high word of?lParam?contains any error code.

3.??????WSARecvFrom函數(shù)的第二個(gè)參數(shù)可表示一個(gè)WSABUF的結(jié)構(gòu)體數(shù)組,可用于存放多個(gè)從網(wǎng)絡(luò)上接收到的信息塊,當(dāng)然也可以將所有信息放在一個(gè)結(jié)構(gòu)體中,然后將自己關(guān)心的信息塊取出,但這樣做比較麻煩,可以直接用WSABUF結(jié)構(gòu)體數(shù)組接收不同信息的塊即可。(沒(méi)有具體的實(shí)際操作經(jīng)驗(yàn))

在消息響應(yīng)函數(shù)中添加如下代碼

?

?int WSARecvFrom( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags, struct sockaddr FAR *lpFrom, LPINT lpFromlen, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine );?
s,標(biāo)識(shí)套接字的描述符。
lpBuffers,[in, out],一個(gè)指向WSABUF結(jié)構(gòu)體的指針。每一個(gè)WSABUF結(jié)構(gòu)體包含一個(gè)緩沖區(qū)的指針和緩沖區(qū)的長(zhǎng)度。

typedef struct __WSABUF {
? u_long??????len;
? char FAR??? *buf;
} WSABUF, FAR * LPWSABUF;


dwBufferCount, lpBuffers數(shù)組中WSABUF結(jié)構(gòu)體的數(shù)目。
lpNumberOfBytesRecvd,[out],如果接收操作立即完成,則為一個(gè)指向本次調(diào)用所接收的字節(jié)數(shù)的指針。
lpFlags,[in, out],一個(gè)指向標(biāo)志位的指針。
lpFrom,[out],可選指針,指向重疊操作完成后存放源地址的緩沖區(qū)。
lpFromlen,[in, out],指向from緩沖區(qū)大小的指針,僅當(dāng)指定了lpFrom才需要。
lpOverlapped,一個(gè)指向WSAOVERLAPPED結(jié)構(gòu)體的指針(對(duì)于非重疊套接字則忽略)。
lpCompletionRoutine,一個(gè)指向接收操作完成時(shí)調(diào)用的完成例程的指針(對(duì)于非重疊套接字則忽略)。

?

?

switch(LOWORD(lParam)) {?//lParam的低字節(jié)指明網(wǎng)絡(luò)事件的類型

??????case?FD_READ:?//我們當(dāng)前只有讀取這樣一個(gè)事件,這是在WSAAsyncSelect中設(shè)定的

???????????WSABUF wsabuf;

???????????wsabuf.buf=new?char[200];?//網(wǎng)絡(luò)上接收到的數(shù)據(jù)

???????????wsabuf.len=200;

???????????DWORD dwRead;

???????????DWORD dwFlag=0;

???????????SOCKADDR_IN addrFrom;

???????????int?len=sizeof(addrFrom);

???????????CString str;

????????????if(SOCKET_ERROR==WSARecvFrom(m_socket,&wsabuf,1,&dwRead,&dwFlag,(SOCKADDR*)&addrFrom,&len,NULL,NULL))

???????????{

?????????????????//下面的消息框基本不會(huì)運(yùn)行,因?yàn)?/span>WSARecvFrom方法是在有網(wǎng)絡(luò)數(shù)據(jù)的情況下才會(huì)被調(diào)用的,所以運(yùn)行到這段,基本是有數(shù)據(jù)的,做這樣一個(gè)判斷,只是出于編程風(fēng)格一致而已

?????????????????MessageBox("接收數(shù)據(jù)失敗!");

?????????????????return;

???????????}

??????str.Format("from %s said:%s",inet_ntoa(addrFrom.sin_addr),wsabuf.buf);

???????????CString temp;

???????????GetDlgItemText(IDC_EDIT_RECV,temp);

???????????temp+="/r/n"+str;

???????????SetDlgItemText(IDC_EDIT_RECV,temp);

???????????break;

}

h.??????信息的發(fā)送:

?

?int WSASendTo( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesSent, DWORD dwFlags, const struct sockaddr FAR *lpTo, int iToLen, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine );
s,標(biāo)識(shí)一個(gè)套接字(可能已連接)的描述符。
lpBuffers,一個(gè)指向WSABUF結(jié)構(gòu)體的指針。每一個(gè)WSABUF結(jié)構(gòu)體包含一個(gè)緩沖區(qū)的指針和緩沖區(qū)的長(zhǎng)度。
dwBufferCount,?lpBuffers數(shù)組中WSABUF結(jié)構(gòu)體的數(shù)目。
lpNumberOfBytesSent,[out],如果發(fā)送操作立即完成,則為一個(gè)指向本次調(diào)用所發(fā)送的字節(jié)數(shù)的指針。
dwFlags,指示影響操作行為的標(biāo)志位。
lpTo,可選指針,指向目標(biāo)套接字的地址。
iToLen,lpTo中地址的長(zhǎng)度。
lpOverlapped,一個(gè)指向WSAOVERLAPPED結(jié)構(gòu)的指針(對(duì)于非重疊套接字則忽略)。
lpCompletionRoutine,一個(gè)指向接收操作完成時(shí)調(diào)用的完成例程的指針(對(duì)于非重疊套接字則忽略)。

?

DWORD dwIP;?//控件上填寫的IP地址

???CString strSend;?//需要發(fā)送的信息內(nèi)容

???WSABUF wsbuf;?//需要發(fā)送的信息內(nèi)容

???DWORD dwSend;

???((CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS1))->GetAddress(dwIP);

???GetDlgItemText(IDC_EDIT_SEND,strSend);

?

???SOCKADDR_IN addrTo;

???addrTo.sin_addr.S_un.S_addr=htonl(dwIP);

???addrTo.sin_family=AF_INET;

???addrTo.sin_port=htons(1234);

?

???//GetBuffer函數(shù)將CString類型轉(zhuǎn)換為char*類型

???wsbuf.buf=strSend.GetBuffer(strSend.GetLength());

???wsbuf.len=strSend.GetLength()+1;?//多一個(gè)字節(jié)用于存放結(jié)束操作符

???if(SOCKET_ERROR==WSASendTo(m_socket,&wsbuf,1,&dwSend,0,(SOCKADDR*)&addrTo,sizeof(SOCKADDR),NULL,NULL))

???{

????????MessageBox("發(fā)送數(shù)據(jù)失敗!");

????????return;

???}

???else

???{

????????SetDlgItemText(IDC_EDIT_SEND,"");

}

i.????????綜上所述,創(chuàng)建一個(gè)基于winsock2版本的異步套接字的網(wǎng)絡(luò)聊天室程序有以下幾個(gè)步驟:

1.??????調(diào)用WSAStartup加載套接字庫(kù)

2.??????調(diào)用WSASocket創(chuàng)建套接字

3.?????調(diào)用WSAAsyncSelect請(qǐng)求基于windows消息的網(wǎng)絡(luò)事件通知

4.??????創(chuàng)建自定義的消息響應(yīng)函數(shù),來(lái)處理捕獲的網(wǎng)絡(luò)事件

5.??????在消息響應(yīng)函數(shù)內(nèi)部調(diào)用WSARecvFrom來(lái)處理接收到的數(shù)據(jù)

6.??????調(diào)用WSASendTo處理發(fā)送數(shù)據(jù)?

?

3.通過(guò)主機(jī)名稱實(shí)現(xiàn)聊天:
??????? //IP轉(zhuǎn)換為主機(jī)名稱
??????? //str.Format("from %s said:%s",inet_ntoa(addrFrom.sin_addr),wsabuf.buf);修改為以下代碼
?HOSTENT *pHost;
?pHost = gethostbyaddr((char*)&addrFrom.sin_addr.S_un.S_addr,4,AF_INET);????
?str.Format("from %s said:%s",pHost->h_name,wsabuf.buf);?

?

??????? //主機(jī)名稱轉(zhuǎn)換為IP
?HOSTENT * pHost;
??????? SOCKADDR_IN addrTo;?
?CString strHostName;

?

?if (GetDlgItemText(IDC_EDIT_HOSTNAME,strHostName),strHostName=="")
?{
??((CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS1))->GetAddress(dwIP);
??addrTo.sin_addr.S_un.S_addr=htonl(dwIP);
?}
?else
?{
??pHost = gethostbyname(strHostName);
??addrTo.sin_addr.S_un.S_addr=*((DWORD*)pHost->h_addr_list[0]);
?}

??

4.??????小結(jié):

當(dāng)前程序?qū)⑾⒌慕邮蘸桶l(fā)送放在了同一個(gè)線程中,即主線程中。如果采用先前使用過(guò)的阻塞套接字的話,程序會(huì)因?yàn)榻邮蘸瘮?shù)的調(diào)用導(dǎo)致主線程的暫停運(yùn)行,就無(wú)法及時(shí)的發(fā)送消息了。但是采用異步套接字可使得發(fā)送和接收放在同一個(gè)線程中而不會(huì)有相互的影響。

如果采用異步套接字加上多線程編程,則大大會(huì)提高網(wǎng)絡(luò)運(yùn)用程序的性能。

在第十四課中講winsock1.1的編程中一般將接收函數(shù)放在一個(gè)while循環(huán)中,來(lái)使得程序一直處于接收響應(yīng)狀態(tài),在異步套接字中,利用了在程序初始化的時(shí)候調(diào)用了WSAAsyncSelect方法來(lái)聲明程序的網(wǎng)絡(luò)的事件有相應(yīng)的自定義消息來(lái)處理,其真正的核心部分還是封裝在MFC
本文來(lái)自CSDN博客,轉(zhuǎn)載請(qǐng)標(biāo)明出處:http://blog.csdn.net/roger_ge/archive/2008/09/09/2903337.aspx

總結(jié)

以上是生活随笔為你收集整理的孙鑫MFC笔记之十四--多线程同步与异步套接字编程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。