近期在用MFC做一個軟體,軟體與服務器通訊,服務器與客戶端均在本機,客戶端下發指令給服務器,服務器上發相應內容給客戶端。發送由用戶點擊按鈕,呼叫send函式發送;接收則采用異步接收,重寫了CAsyncSocket類的Onreceive函式。希望的效果是下發之后500ms內收到相應回復資料。
目前遇到一個問題,想做一個用戶確認的功能,目前做法是用MessageBox彈框,確認之后再進行下發,下發在另外開啟的執行緒中進行。
但是發現加入了這個MessageBox之后,經常會出現點擊確認之后Onreceive函式不被呼叫,但是在下發超時之后(也就是發送失敗之后)才會收到資料。從服務器端日志來看,服務器端確實在合適的時間將資料上發了,但是就是無法觸發,最后一次發送失敗之后收到正確資料。因為是本機網路,不考慮網路延時。
剛開始以為網路有問題,一直找不到原因。直到偶然把提示的確認框相關代碼注釋掉之后發現這個Onreceive函式可以被正常觸發。所以感覺是MessageBox這邊出了問題。
t = MessageBox(this->GetSafeHwnd(),"確認下發引數?","下發引數",MB_YESNO | MB_ICONWARNING);
if(t == IDNO) return;
所以不知道這種情況由何引起,又該如何解決?請大家幫忙看看
uj5u.com熱心網友回復:
我也遇到過 你可以在界面上換個控制元件顯示資訊 Messagebox會彈出來的時候 你是接收不到資料的uj5u.com熱心網友回復:
你試下用afxmessagebox看看uj5u.com熱心網友回復:
messafebox會可能影響訊息回圈。導致aocket色訊息也被影響uj5u.com熱心網友回復:
我又確實需要根據用戶的選擇來決定是否下發,所以此處必須得有阻塞。請問大家有沒有其他的控制元件可以解決這個問題呢?uj5u.com熱心網友回復:
下發在另外開啟的執行緒中進行 是什么意思?要注意在默認情況下,CAsyncSocket和CSocket物件不能跨執行緒使用。
uj5u.com熱心網友回復:
就是在用戶確認過messageBox之后,呼叫發送函式,函式中啟動執行緒下發引數,用send。執行緒會等待一段時間(這里設計的是500ms),這段時間內如果收到資料,標志位置位,退出執行緒。
在發送函式中等待執行緒結束,使用的是waitforMultipleobjects,同時也建立有訊息回圈保證訊息不被阻塞。
等待執行緒結束部分是這么寫的:
RefreshResult=AfxBeginThread(DownloadParaThread,(LPVOID)&index);
if (RefreshResult != NULL)
{
DWORD dwRet=0;
MSG msg;
while (TRUE)
{
dwRet = MsgWaitForMultipleObjects (1, &RefreshResult->m_hThread, FALSE, INFINITE, QS_ALLINPUT);
if(dwRet==WAIT_OBJECT_0)
break;
else if(dwRet==WAIT_OBJECT_0 + 1)
{
PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
DispatchMessage(&msg);
}
else
break;
}
RefreshResult = NULL;
}
直觀上感覺是在下發send函式呼叫之后,Onreceive函式就無法被觸發,但是偶爾又會有被觸發的情況,也不是每次都失敗,只是失敗的概率比較大。但是如果將前面MessageBox代碼段刪掉之后,OnReceive函式就可以正常觸發。所以才定位到MessageBox的影響。你們感覺如何呢?
uj5u.com熱心網友回復:
而且我不僅僅是在MessageBox存在的時候不能觸發,關掉MessageBox之后也不能觸發。uj5u.com熱心網友回復:
把messageBox提前放到其他地方,或者在界面UI執行緒中處理uj5u.com熱心網友回復:
自己做個 Modeless 對話框 (create), 不行嗎 ?uj5u.com熱心網友回復:
MessageBox處理結束之后才會呼叫函式啟動執行緒,就是感覺是不是MessageBox雖然通過點擊OK或者cancek結束了但是它還是會影響到后面執行緒的啟動。uj5u.com熱心網友回復:
我正準備自己畫一個對話框,然后DoModal呼叫,看看是否存在問題。uj5u.com熱心網友回復:
是不是要 呼叫 WM_CANCELMODE ??void CCancelModeDlg::OnSetfocusEdit2()
{
if(!FindWindow(0,"yyy"))// change : AFX_IDS_APP_TITLE="yyy"
{
AfxMessageBox("SetfocusEdit2");
// w.o this, the cursor is 'I' anywhere ,means focus in edit2 !!!
// mouse has captured by edit2
m_Edit2.PostMessage(WM_CANCELMODE,0,0);
}
}
uj5u.com熱心網友回復:
自己做了一個對話框,用DoModal()啟動,發現Onreceive函式還是會被推遲觸發的情況。不知道WM_CANCELMODE 是什么意思呢??uj5u.com熱心網友回復:
SUMMARYIn the Microsoft Windows graphical environment, the WM_CANCELMODE message informs a window that it should cancel any internal state. This message is sent to the window with the focus when a dialog box or a message box is displayed, giving the window the opportunity to cancel states such as mouse capture.
When a control has the focus, it receives a WM_CANCELMODE message when the EnableWindow function disables the control or when a dialog box or a message box is displayed. When a control receives this message, it should cancel modes, such as mouse capture, and delete any timers it has created. A control must cancel these modes because an application may use a notification from the control to display a dialog box or a message box.
The DefWindowProc function processes WM_CANCELMODE by calling the ReleaseCapture function, which cancels the mouse capture for whatever window has the capture. The DefWindowProc function does not cancel any other modes.
uj5u.com熱心網友回復:
CAsyncSocket有做防無限重入,避免你這樣MessageBox阻塞之后又接受到新訊息導致無數個MessageBox被彈出來。你應該重新考慮下界面設計,比如用狀態欄或者串列視圖來顯示資訊。
uj5u.com熱心網友回復:
“將前面MessageBox代碼段” 改為 afxDump (不柱塞)看看uj5u.com熱心網友回復:
換TRACEuj5u.com熱心網友回復:
總結一下,是因為沒有做好訊息泵,沒有把所有的資訊都傳入到訊息回圈,有一段代碼如下:while (TRUE)
{
//wait for m_hThread to be over,and wait for
//QS_ALLINPUT(Any message is in the queue)
dwRet = MsgWaitForMultipleObjects (1, &cThread->m_hThread, FALSE, INFINITE, QS_ALLINPUT);
switch(dwRet)
{
case WAIT_OBJECT_0:
break; //break the loop
case WAIT_OBJECT_0 + 1:
//get the message from Queue
//and dispatch it to specific window
while(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
continue;
default:
break; // unexpected failure
}
break;
}
cThread = NULL;
}
紅色部分就是問題的關鍵,因為之前沒有加那個回圈,導致的后果就是一旦出現對話框,將msg回圈占用,就會導致異步網路接收函式的阻塞,最終導致問題的產生,加入回圈之后問題解決,對話框的彈出與否不再影響網路的異步接收。
到這里看起來似乎問題已經結束,但是經過這個問題,我發現自己對于一些技術的理解還是太膚淺了,找到解決方案之后沒辦法去弄明白其中的具體道理,比如為什么加入了while回圈之后訊息回圈就可以正常運行了,之前對話框的出現是為什么會導致這些問題的產生也都只是個人感覺問題所在,并沒有參考書籍文獻材料,以前看侯俊杰前輩的《深入淺出MFC》也只是略過一眼,沒有去仔細專研其中細節深奧的部分。。
這里希望有相關經驗或者對這個問題感興趣的朋友們能夠不吝賜教,講解一下關于MFC訊息回圈的原理以及什么可能會影響訊息回圈。
uj5u.com熱心網友回復:
我認為你的異步設計有問題,導致了復雜的解決方案。CAsycSocket實際上不是在作業者執行緒中等待接收資料的,它是運行在主執行緒環境中的,也就是和你的MessageBox所在的執行緒在一起的。之所以接收是異步的,應該是底層用了Overlapped IO之類的技術,而Overlapped IO的讀寫完成的通知是基于訊息的,MessageBox阻塞了訊息回圈,使得CAsyncSocket不能及時觸發OnReceived。你的解決方案是修改訊息回圈。
我個人不喜歡自定義訊息回圈這種高度依賴OS平臺、造成程式不易讀的技術。我倒以為,你應該將接收程序和發送程序一樣,徹底放到另一個作業者執行緒中。這樣一,不用自定義訊息回圈,有利于以后程式維護,也有利于程式移值到其它平臺;不用CAsyncSocket,代碼邏輯清晰。
uj5u.com熱心網友回復:
另外,覺得樓主異步發送后,等待發送執行緒結束那段代碼太復雜,還用訊息泵(我樓上稱為訊息回圈,不嚴謹)。不如修改下,異步發送執行緒在結束前,清除另一個標志位,這個標志位表明是否在發送等待500ms中。這樣,主執行緒就可以在用戶重復發送前,判斷此標志,如果標志置位,則提示用戶“上一次發送正等回復中”之類的。主執行緒完全不必使用訊息泵。個人認為,越復雜就越容易出錯,也不易后續維護和擴展,象MsgWaitForMultipleObjects 這種API坑很多的。
uj5u.com熱心網友回復:
MessageBox函式可以阻塞程式的正常執行的,轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/108107.html
標籤:進程/線程/DLL
上一篇:大家寫串口用什么?我過去用的MSCOMM控制元件,現在準備用serialport,發現serialport庫的版本實在太多了。差別也挺大。很多函式不通用。
