退出服務器
多線程非阻塞模式實例
多線程非阻塞模式到現在算是告一段落吧 雖然還有一些小的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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.