多線程非阻塞模式實例

多線程非阻塞模式到現在算是告一段落吧 雖然還有一些小的bug需要修正 總結一下  準備向後面進發


 實現功能: 本程序主要實現遠程計算的功能 通過非阻塞套接字和多線程的結合 讓通信變得高效  服務器通過維護一個客戶端鏈表來實現對多個客戶響應  客戶端自身驗證表達式的正確性 當輸入Byebye時 服務器回覆OK  此時客戶端斷開連接退出

     主要模塊和線程管理
     
     服務器:
               主線程:初始化服務器
                           運行服務器
                     退出服務器

               監聽線程:
                           接收客戶端連接請求
                           創建客戶端節點
                           啓動對客戶端節點的服務

               清理線程:
                           當服務器運行時:
                                定期檢查清理已退出的客戶節點
                           當服務器斷開時 
                                 斷開所有對客戶端的連接
                                 清理客戶節點知道客戶端鏈表爲空

               客戶端類:接收線程:
                                           接收客戶端的數據 並且處理
                              發送線程:
                                           發送已經處理後的數據

       客戶端:
               主線程:
                         初始化客戶端
                         連接服務器
                         執行處理輸入輸出
                         退出客戶端

               接收數據線程:
                         接收服務器數據 並且通知主線程顯示
               發送數據線程:
                         發送主線程處理好的數據


關於線程同步:
從客戶端說起:
                    各個線程維持一個運行狀態變量bConning 用於線程循環外側檢測退     
                    主線程在成功連接至服務器後 bConning = TRUE 創建接收和發送線程 
                    
                    這三個線程之間的交互:
                    一。正常交互
                                        1.主線程在執行輸入處理完數據後放入SendBuf 通過hEventSendData來通知發送線程(對SendBuf互斥訪問)
                                        2.接收線程在將接收數據拷貝到RecvBuf後 通過hEventShowData來通知主線程顯示數據(對RecvBuf互斥訪問)

                    二。退出交互
                                        1.接收線程先退出 設置bConning爲FALSE 並SetEvent(hEventShowData)
                                                                   對於發送線程  檢測到bConning==FALSE 立即退出
                                                                   對於主線程     在顯示數據前檢測bConning==FALSE 直接跳出 之後調用WaitForMutiObjects等                                      待發送線程和接收線程均退出
                                         2.發送線程先退出 設置bConning爲FALSE 並SetEvent(hEventShowData)  如果不調用這條 主線程有可能無限等待顯示數據
                                                                    對於接收線程 檢測到bConning爲FALSE 退出循環
                                                                    對於主線程     在顯示數據前檢測bConning==FALSE 跳出 等待接收和發送均退出

                                         3.主線程退出         當服務器回覆"OK" 表示用戶輸入Byebye 此時斷開連接  等待接收和發送退出
                                                                     發送和接收線程均檢測到bConning爲FALSE 跳出循環

服務器:

                   服務器的客戶端類:
                      一。正常交互:
                                           1.接收線程接收到數據後進行處理後 將數據拷貝到數據緩衝 用hEvent通知發送線程發送(對數據緩衝互斥訪問)
                                            2.發送線程發送數據 並且恢復hEvent爲無信號

                      二。退出交互:這裏的發送線程在退出循環後通過WaitForSingleObject(hThreadRecv)來設置整個客戶端的m_exit狀態
                                            1.接收線程先退出 設置bConning爲FALSE SetEvent(hEvent)
                                                                        發送線程在等待到hEvent時 先判斷bConning==FALSE時 退出循環
                                            2.發送線程先退出:設置bConning爲FALSE  在跳出循環後 等待接收線程退出 設置m_exit=TRUE 用於清理線程清理節點                                                            接收線程檢測到bConning=FALSE 跳出循環 退出


                    服務器三個總維護線程:
                    一。正常交互:   類似前面 三個線程維護同一個狀態變量bServerRunning
                                             1.服務器主線程 初始化服務器 並且根據用戶輸入打開和斷開服務器
                                                                    在打開服務器時 設置bServerRunning = TRUE創建監聽和清理線程
                                                                    在斷開服務器時 設置bServerRunning=FALSE 並且通過清理線程結束的信號量hServerEvent退出服務器      
                                             2.服務器監聽線程 不斷監聽來自客服端的連接請求 添加新的客戶端節點到客戶端鏈表  並交由清理線程維護                                                                                
                                             3.客戶端清理線程 測各個節點是否退出(m_Exit==TRUE) 並且移除鏈表 清理節點內存 當服務器退出時 斷開所有客戶端連接  確保所有的客戶節點均已退出(cClientList.size() == 0)

                   二。退出交互:    1.服務器主線程先退出:服務器主線程不會先退出 在接收到斷開服務器請求時 只是bServerRunning=FALSE
                                                                                之後監聽線程停止  服務器等待清理線程結束事hServerEvent後 退出
                                              2.監聽線程先退出:        設置bServerRunning爲FALSE 並退出
                                                                                 之後清理線程開始斷開所有客戶端連接 清理完成後SetEvent(hServerEvent)
                                                                                 而服務器界面仍在等待輸入是否退出服務器 一旦用戶輸入後(即使不是退出命令) 服務器也將立即跳出循環 根據hServerEvent狀態退出(bug之一)
                                              3.清理線程不會主動退出(除非遇到內存引用錯誤)

                    總的三個模塊交互:
                                             客戶端退出 那麼服務器客戶端類的SendThread或RecvThread先後遇到錯誤退出 導致節點m_Exit==TRUE 被清理                                       服務器退出 那麼客戶端的發送和接收將失敗 也將導致退出
                                             服務器通過清理線程來同時斷開所有客戶連接
                                             服務器客戶端類通過m_Exit來告訴清理線程可以清理該節點
                                             客戶端通過回覆內容爲OK來得知用戶輸入了Byebye 並且開始退出自身

總結:
     不管用何種方式通信  相關聯的幾個線程總會用一個變量來控制所有的其他線程
     對於非阻塞套接字 Recv Send Connect Accept等都需要套上一個基於共同控制變量或者永真的循環來實現對WSAEWOULDBLOCK的返回重試
     對於通過事件信號量來通知的兩個線程 例如生產者 消費者(生產者生產好了通過hEvent通知消費者) 當生產者退出時 一定要通過該信號量來通知消費者 以免消費者阻塞於WaitForSingleObject  而消費者在等到信號量時  也一定要檢測生產者是否已經退出(或者是說在這裏的斷開了連接)  以免發送或接收未知的數據
     對於有信號量控制的兩個同步線程 要注意是否有共同訪問的數據 要時刻記得對數據進行互斥訪問

代碼地址:
http://download.csdn.net/detail/wudaijun/4709715
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章