《電子技術(shù)應(yīng)用》
您所在的位置:首頁 > 通信與網(wǎng)絡(luò) > 業(yè)界動(dòng)態(tài) > 基于TCP/IP的多線程通信及其在遠(yuǎn)程監(jiān)控系統(tǒng)中的應(yīng)用

基于TCP/IP的多線程通信及其在遠(yuǎn)程監(jiān)控系統(tǒng)中的應(yīng)用

2009-05-19
作者:王金廉 謝劍英 賈 青

  摘 要: 提出了一種在Windows NT下基于TCP/IP協(xié)議的多線程通信的設(shè)計(jì)與實(shí)現(xiàn)方法,在此基礎(chǔ)上給出了多線程通信在蓄電池遠(yuǎn)程監(jiān)控系統(tǒng)中的應(yīng)用實(shí)例。
  關(guān)鍵詞: 多線程 實(shí)時(shí)性 TCP/IP協(xié)議 遠(yuǎn)程監(jiān)控系統(tǒng)


  傳統(tǒng)的應(yīng)用程序都是單線程的,即在程序運(yùn)行期間,由單個(gè)線程獨(dú)占CPU的控制權(quán),負(fù)責(zé)執(zhí)行所有任務(wù)。在這種情況下,程序在執(zhí)行一些比較費(fèi)時(shí)的任務(wù)時(shí),就無法及時(shí)響應(yīng)用戶的操作,影響了應(yīng)用程序的實(shí)時(shí)性能。在監(jiān)控系統(tǒng),特別是遠(yuǎn)程監(jiān)控系統(tǒng)中,應(yīng)用程序往往不但要及時(shí)把監(jiān)控對象的最新信息反饋給監(jiān)視客戶(通過圖形顯示),還要處理本地機(jī)與遠(yuǎn)程機(jī)之間的通信以及對控制對象的實(shí)時(shí)控制等任務(wù),這時(shí) ,僅僅由單個(gè)線程來完成所有任務(wù),顯然無法滿足監(jiān)控系統(tǒng)的實(shí)時(shí)性要求。在DOS系統(tǒng)下,這些工作可以由中斷來完成。而在Windows NT下,中斷機(jī)制對用戶是不透明的。為此,可引進(jìn)多線程機(jī)制,主線程專門負(fù)責(zé)消息的響應(yīng),使程序能夠響應(yīng)命令和其他事件。輔助線程可以用于完成其他比較費(fèi)時(shí)的工作,如通信、圖形顯示和后臺(tái)打印等,這樣就不至于影響主線程的運(yùn)行。
1 Windows NT 多線程概述
  Windows NT是一個(gè)真正的搶占式多任務(wù)操作系統(tǒng)。在Windows NT中,啟動(dòng)一個(gè)應(yīng)用程序就是啟動(dòng)該應(yīng)用程序的一個(gè)實(shí)例,即進(jìn)程。進(jìn)程由一個(gè)或多個(gè)線程構(gòu)成,擁有內(nèi)存和資源,但自己不能執(zhí)行自己,而是進(jìn)程中的線程被調(diào)度執(zhí)行。進(jìn)程至少要有一個(gè)線程,當(dāng)創(chuàng)建一個(gè)進(jìn)程時(shí),就創(chuàng)建了一個(gè)線程,即主線程。主線程可以創(chuàng)建其他輔助線程,由主線程創(chuàng)建的線程又可創(chuàng)建線程。每個(gè)線程都可指定優(yōu)先級,操作系統(tǒng)根據(jù)線程的優(yōu)先級調(diào)度線程的執(zhí)行。
  Windows NT中使用多線程的方法有三種:
  · 使用C多線程庫函數(shù);
  · 使用CreateThread() 等Win32函數(shù);
  · 使用MFC類。
  本文采用第三種方法。在Visual C++5.0 中,MFC應(yīng)用程序用CWinThread 對象表示線程。基本操作如下:
  · 創(chuàng)建新線程:調(diào)用MFC全局函數(shù)AfxBeginThread( )創(chuàng)建新線程。AfxBeginThread( )啟動(dòng)新線程并返回控制,然后,新線程和調(diào)用AfxBeginThread( )的線程同時(shí)運(yùn)行。它的返回值為指向CWinThread對象的指針;
  · 暫停/恢復(fù)線程:調(diào)用CWinThread類成員函數(shù)SuspendThread( )暫停線程的運(yùn)行,調(diào)用ResumeThread( )成員函數(shù)恢復(fù)線程的運(yùn)行;
  · 終止線程:在線程內(nèi)部可調(diào)用全局函數(shù)AfxEndThread( )終止線程的運(yùn)行,否則,線程執(zhí)行結(jié)束后,線程自動(dòng)從線程函數(shù)返回并釋放線程占有的資源。
2 基于TCP/IP 的多線程編程
  TCP/IP是Internet上廣泛使用的一種協(xié)議,可用于異種機(jī)之間的互聯(lián)。TCP/IP協(xié)議本身是非常復(fù)雜的,然而在網(wǎng)絡(luò)編程中,程序員不必考慮TCP/IP的實(shí)現(xiàn)細(xì)節(jié),只需利用協(xié)議的網(wǎng)絡(luò)編程接口Socket(亦稱套接字)即可。在Windows中,網(wǎng)絡(luò)編程接口是Windows Sockets,它包含標(biāo)準(zhǔn)的Berkley Sockets的功能調(diào)用的集合,以及為Windows 所做的一些擴(kuò)展。TCP/IP協(xié)議的應(yīng)用一般采用客戶/服務(wù)器模式,面向連接的應(yīng)用調(diào)用如圖1所示。


  根據(jù)上述順序調(diào)用函數(shù)建立連接后,通信雙方便可交換數(shù)據(jù)[1]。然而,在調(diào)用帶*號的函數(shù)時(shí),操作常會(huì)阻塞,特別是當(dāng)套接字工作在同步阻塞模式(Blocking Mode)時(shí)。這時(shí),程序無法響應(yīng)任何消息。為了避免出現(xiàn)這種情況,本文引進(jìn)輔助線程。在執(zhí)行含有可能阻塞的函數(shù)的任務(wù)時(shí),動(dòng)態(tài)創(chuàng)建新的線程,專門處理該任務(wù)。主線程把任務(wù)交給輔助線程后,不再對輔助線程加以控制與調(diào)度。本文分別針對connect()、accept()、receive()、send()等可能阻塞的函數(shù)創(chuàng)建了相應(yīng)的線程,如表1所示。


  多線程編程常常還要考慮線程間的通信。線程間的通信可以采用全局變量、指針參數(shù)和文件映射等方式。本文采用指針參數(shù)方式。在調(diào)用AfxBeginThread()函數(shù)時(shí),通過傳遞指針參數(shù)的方式在主線程與輔助線程間通信。
  AfxBeiginThread( )函數(shù)的用法如下:
  CWinThread* AfxBeginThread( AFX_THREADPROC pfnThreadProc,
  LPVOID pParam,
  int nPriority=THREAD_PRIORITY_NORMAL,
  UINT nStackSize=0,
  DWORD dwCreateFlags=0,
  LPSECURITY_ATTRIBUTES lpSecurityAttrs=NULL );
  參數(shù)pfnThreadProc指定線程函數(shù),必須如下定義:
  UINT MyControllingFunction( LPVOID pParam );
  參數(shù)pParam 是調(diào)用線程傳遞給線程函數(shù)pfnThreadProc的參數(shù);
  其他參數(shù)一般只需采用缺省值。
  指針參數(shù)通信方式就是通過參數(shù)pParam在線程間通信的,它可為指向任何數(shù)據(jù)類型的指針。本文中,定義了一個(gè)名叫EXCHANGE_INFO的結(jié)構(gòu)如下:
  typedef struct
  {  SOCKET sServerSocket;
    SOCKET *psClientSocket;
    SOCKADDR_IN *pClientAddr;
    BOOL *pbConnected;
    unsigned char *pucBuffer;
    int *pnMessageLen;
  } EXCHANGE_INFO;
  在需要通信時(shí),先聲明一個(gè)結(jié)構(gòu)變量,再把變量的指針作為pParam參數(shù),調(diào)用AfxBeginThread((AFX_THREADPROC) CSocketThread::WaitForConnectThread, (LPVOID)& m_ExchangeInfo)函數(shù)即可。
  為了利用面向?qū)ο蠹夹g(shù)編程所具有的模塊性強(qiáng)、便于修改、可移植性好等優(yōu)點(diǎn),本文還把表1中的線程封裝為父類為CWinThread的自定義類CSocketThread中。還自定義了一個(gè)叫CSocketComm的新類,封裝了一些函數(shù),如CreateSocket、ConnectToServer、WaitForClient、ReadMessage、SendMessage等,這些函數(shù)屏蔽了面向連接的通信程序的實(shí)現(xiàn)細(xì)節(jié),如創(chuàng)建、連接、發(fā)送和接收等,在這些函數(shù)里,動(dòng)態(tài)創(chuàng)建輔助線程。
  下面以CSocketComm類中的等待客戶連接請求的函數(shù)WaitForClient()為例,注釋說明多線程編程的具體細(xì)節(jié)。
  BOOL CSocketComm::WaitForClient()
  {
  if( m_bConnected ) return( TRUE );
  //配置bind函數(shù)的參數(shù),即服務(wù)器的套接字地址結(jié)構(gòu)
  SOCKADDR_IN Addr;
  memset( &Addr, 0, sizeof( SOCKADDR_IN ) );
  Addr.sin_family = AF_INET;
  Addr.sin_port = htons( m_nPort );
  Addr.sin_addr.s_addr = htonl( INADDR_ANY );
  //將套接字地址結(jié)構(gòu)賦予套接字(綁定),以指定本地半相關(guān)
  int nReturnValue;
  nReturnValue = ::bind( m_sServerSocket, (LPSOCKADDR) &Addr,sizeof( SOCKADDR_IN ) );
  if( nReturnValue == SOCKET_ERROR ) return( FALSE );
   //配置傳給WaitForConnectThread線程函數(shù)的參數(shù)m_ExchangeInfo
  m_ExchangeInfo.sServerSocket = m_sServerSocket;
  m_ExchangeInfo.psClientSocket = &m_sClientSocket;
  m_ExchangeInfo.pClientAddr = &m_ClientAddr;
  m_ExchangeInfo.pbConnected = &m_bConnected;
  //以m_ExchangeInfo的指針為參數(shù)調(diào)用WaitforConnectThread線程等待客戶端連接
  AfxBeginThread((AFX_THREADPROC)CSocketThread::
  WaitForConnectThread, (LPVOID) & m_ExchangeInfo );
  return( TRUE );
  }
  //等待連接線程
  UINT CSocketThread::WaitForConnectThread(LPVOIDpParam)
  {
  EXCHANGE_INFO*pExchangeInfo=(EXCHANGE_INFO *) pParam;
  int nReturnValue, nClientAddrSize = sizeof( SOCKADDR_IN );
  //偵聽連接
  nReturnValue = ::listen(pExchangeInfo ->sServerSocket, 1 );
  if( nReturnValue == SOCKET_ERROR ) return( 0 );
  //阻塞調(diào)用accept,直至有客戶連接請求
  *pExchangeInfo->psClientSocket = ::accept(pExchangeInfo->sServerSocket, (LPSOCKADDR) pExchangeInfo ->pClientAddr, &nClientAddrSize );
  if( (*pExchangeInfo ->psClientSocket) != INVALID_SOCKET )
  //通過pExchangeInfo的指針在線程間通信
  *pExchangeInfo->pbConnected = TRUE;
  return( 0 );
  }
3 應(yīng)用實(shí)例-高層協(xié)議的設(shè)計(jì)
  在電廠和電站中,為了保證安全工作,保護(hù)系統(tǒng)必不可少。保護(hù)系統(tǒng)的電源供應(yīng)通常使用兩種方式。一般情況下,使用交流電系統(tǒng)對保護(hù)系統(tǒng)進(jìn)行供電;當(dāng)交流電系統(tǒng)出現(xiàn)故障時(shí),立即使用后備的蓄電池系統(tǒng)對保護(hù)系統(tǒng)進(jìn)行供電。為了對蓄電池系統(tǒng)進(jìn)行監(jiān)控和管理,以保證蓄電池在關(guān)鍵時(shí)刻能正常工作,設(shè)計(jì)了在Windows NT環(huán)境下具有遠(yuǎn)程通訊功能和動(dòng)態(tài)人機(jī)界面的智能蓄電池遠(yuǎn)程監(jiān)控系統(tǒng) 。該系統(tǒng)由蓄電池智能管理、充電機(jī)控制、母線絕緣在線檢測、聲光報(bào)警、系統(tǒng)組態(tài)、遠(yuǎn)程通信等子系統(tǒng)組成,實(shí)現(xiàn)對蓄電池/充電機(jī)智能化遠(yuǎn)程管理和控制,對整個(gè)系統(tǒng)的運(yùn)行狀態(tài)進(jìn)行實(shí)時(shí)監(jiān)控,具有多媒體報(bào)警、事件處理、動(dòng)態(tài)數(shù)據(jù)庫、趨勢畫面和動(dòng)態(tài)畫面顯示、操作提前提醒等功能。系統(tǒng)框圖如圖2所示。在遠(yuǎn)程通信模塊中,遠(yuǎn)程監(jiān)控機(jī)需把監(jiān)控客戶的操作命令及時(shí)傳給本地機(jī),本地機(jī)根據(jù)命令控制充電機(jī),使之按照一定的方式工作,而本地機(jī)需定時(shí)向遠(yuǎn)程監(jiān)控機(jī)反饋實(shí)時(shí)的充電機(jī)狀態(tài)信息。它們之間的通信是基于TCP/IP的廣域網(wǎng)通信,而且,我們引進(jìn)了多線程機(jī)制以保證系統(tǒng)具有良好的實(shí)時(shí)性。


  下面以其中的充電機(jī)控制系統(tǒng)為例談?wù)勅绾问褂肅SocketComm類進(jìn)行遠(yuǎn)程通信。為簡單起見,假定本地機(jī)與遠(yuǎn)程監(jiān)控機(jī)之間通信的信息僅有下面三種類型:
  ·本地機(jī)接收到該命令后,控制充電機(jī)按照穩(wěn)壓模式運(yùn)行,輸出電壓為電壓給定值;
  ·本地機(jī)接收到該命令后,控制充電機(jī)按照穩(wěn)流定時(shí)模式運(yùn)行,輸出電流為電流給定值;
  ·本地機(jī)向遠(yuǎn)程監(jiān)控機(jī)發(fā)送充電機(jī)的實(shí)時(shí)狀態(tài)數(shù)據(jù)(包括輸出電壓、輸出電流、狀態(tài)指示和故障類型指示)。
  在基于TCP/IP的面向連接的網(wǎng)絡(luò)通信中,客戶與服務(wù)器之間傳送的是有序可靠的字節(jié)流(Byte Stream),所以程序員有必要在傳輸層TCP上定義自己的高層協(xié)議,設(shè)計(jì)幀結(jié)構(gòu),將字節(jié)流變成有意義的信息。在CSocketComm類中由AssembleMessage( )函數(shù)把數(shù)據(jù)組合成一定的幀結(jié)構(gòu)。幀結(jié)構(gòu)為:

  
  其中@為幀起始標(biāo)志,#為幀終結(jié)標(biāo)志
  對應(yīng)的結(jié)構(gòu)定義如下:
  typedef struct
  { int MessageType; //信息類型
  int ChargerNo; //充電機(jī)編號
  int DataNo; //數(shù)據(jù)類型
  float Data; //數(shù)據(jù)
  } MessageStruct;
  需要通信時(shí),先聲明一個(gè)MessageStruct變量,根據(jù)信息內(nèi)容對各成員變量賦值,傳給 AssembleMessage()函數(shù)組合成幀,再調(diào)用SendMessage()函數(shù)發(fā)送給接受方。接受方接到數(shù)據(jù)后,對數(shù)據(jù)內(nèi)容的解釋,是由CSocketComm類中的AnalyzeMessage()函數(shù)完成的。AnalyzeMessage()函數(shù)返回一個(gè)MessageStruct變量。應(yīng)用程序就可根據(jù)它的各成員變量控制充電機(jī)或動(dòng)態(tài)顯示充電機(jī)的狀態(tài)。
  總之,把多線程機(jī)制引進(jìn)通信,有利于提高應(yīng)用程序的實(shí)時(shí)性,充分利用系統(tǒng)資源。對于大型的工程應(yīng)用來說,不同的線程完成不同的任務(wù),也有利于提高程序的模塊化,便于維護(hù)和擴(kuò)展。本文給出了一種在Windows NT下基于TCP/IP協(xié)議的多線程通信的基本方法,根據(jù)該方法進(jìn)行修改和擴(kuò)充,便可設(shè)計(jì)出符合具體應(yīng)用的高質(zhì)量的多線程通信程序。
參考文獻(xiàn)
1 蔣東興,林鄂華.Windows Socket 網(wǎng)絡(luò)程序設(shè)計(jì)指南.北京:清華大學(xué)出版社,1995
2 Rajagopal Raj, Monica Subodh P.Windows NT4高級程序設(shè)計(jì).北京:機(jī)械工業(yè)出版社,1998

本站內(nèi)容除特別聲明的原創(chuàng)文章之外,轉(zhuǎn)載內(nèi)容只為傳遞更多信息,并不代表本網(wǎng)站贊同其觀點(diǎn)。轉(zhuǎn)載的所有的文章、圖片、音/視頻文件等資料的版權(quán)歸版權(quán)所有權(quán)人所有。本站采用的非本站原創(chuàng)文章及圖片等內(nèi)容無法一一聯(lián)系確認(rèn)版權(quán)者。如涉及作品內(nèi)容、版權(quán)和其它問題,請及時(shí)通過電子郵件或電話通知我們,以便迅速采取適當(dāng)措施,避免給雙方造成不必要的經(jīng)濟(jì)損失。聯(lián)系電話:010-82306118;郵箱:aet@chinaaet.com。