• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            Windows系統編程之進程間通信
            北極星2003 當前離線


            Windows 的IPC(進程間通信)機制主要是異步管道和命名管道。(至于其他的IPC方式,例如內存映射、郵槽等這里就不介紹了)
            管道(pipe)是用于進程間通信的共享內存區域。創建管道的進程稱為管道服務器,而連接到這個管道的進程稱為管道客戶端。一個進程向管道寫入信息,而另外一個進程從管道讀取信息。
            異步管道是基于字符和半雙工的(即單向),一般用于程序輸入輸出的重定向;命名管道則強大地多,它們是面向消息和全雙工的,同時還允許網絡通信,用于創建客戶端/服務器系統。
            一、異步管道(實現比較簡單,直接通過實例來講解)
            實驗目標:當前有sample.cpp, sample.exe, sample.in這三個文件,sample.exe為sample.cpp的執行程序,sample.cpp只是一個簡單的程序示例(簡單求和),如下:
            代碼:
            #include <iostream.h>
            int main()
            {
              int a, b ;
              while ( cin >> a >> b && ( a || b ) )
                cout << a + b << endl ;
              return 0;
            }
            
            Sample.in文件是輸入文件,內容:
            32 433
            542 657
            0 0
            要求根據sample.exe和它的輸入數據,把輸出數據重定向到sample.out
            流程分析:實際這個實驗中包含兩個部分,把輸入數據重定向到sample.exe 和把輸出數據重定向到sample.out。在命令行下可以很簡單的實現這個功能“sample <sample.in >sample.out”,這個命令也是利用管道特性實現的,現在我們就根據異步管道的實現原理自己來實現這個功能。
            管道是基于半雙工(單向)的,這里有兩個重定向的過程,顯然需要創建兩個管道,下面給出流程圖:
             
            異步管道實現的流程圖說明:
            1)。父進程是我們需要實現的,其中需要創建管道A,管道B,和子進程,整個實現流程分為4個操作。
            2)。管道A:輸入管道
            3)。管道B:輸出管道
            4)。操作A:把輸入文件sample.in的數據寫入輸入管道(管道A)
            5)。操作B:子進程從輸入管道中讀取數據,作為該進程的加工原料。通常,程序的輸入數據由標準的輸入設備輸入,這里實現輸入重定向,即把輸入管道作為輸入設備。
            6)。操作C:子進程把加工后的成品(輸出數據)輸出到輸出管道。通常,程序的輸出數據會輸出到標準的輸出設備,一般為屏幕,這里實現輸出重定向,即把輸出管道作為輸出設備。
            7)。操作D:把輸出管道的數據寫入輸出文件
            需要注意的是,管道的本質只是一個共享的內存區域。這個實驗中,管道區域處于父進程的地址空間中,父進程的作用是提供環境和資源,并協調子進程進行加工。
            程序源碼:
            代碼:
            #include <windows.h> 
            #include <iostream.h>
            const int BUFSIZE = 4096 ; 
            HANDLE  hChildStdinRd, hChildStdinWr, hChildStdinWrDup, 
                   hChildStdoutRd,hChildStdoutWr,hChildStdoutRdDup, 
                hSaveStdin,    hSaveStdout; 
            BOOL CreateChildProcess(LPTSTR); 
            VOID WriteToPipe(LPTSTR); 
            VOID ReadFromPipe(LPTSTR); 
            VOID ErrorExit(LPTSTR); 
            VOID ErrMsg(LPTSTR, BOOL); 
            void main( int argc, char *argv[] ) 
            {  
              // 處理輸入參數
              if ( argc != 4 )
                return ;
              // 分別用來保存命令行,輸入文件名(CPP/C),輸出文件名(保存編譯信息)
              LPTSTR lpProgram = new char[ strlen(argv[1]) ] ;
              strcpy ( lpProgram, argv[1] ) ;
              LPTSTR lpInputFile = new char[ strlen(argv[2]) ];
              strcpy ( lpInputFile, argv[2] ) ;
              LPTSTR lpOutputFile = new char[ strlen(argv[3]) ] ;
              strcpy ( lpOutputFile, argv[3] ) ;    
              
              SECURITY_ATTRIBUTES saAttr; 
              saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
              saAttr.bInheritHandle = TRUE; 
              saAttr.lpSecurityDescriptor = NULL; 
               
              /************************************************
               *    redirecting child process's STDOUT  *
               ************************************************/
              hSaveStdout = GetStdHandle(STD_OUTPUT_HANDLE); 
              
              if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) 
                ErrorExit("Stdout pipe creation failed\n"); 
                
              if (! SetStdHandle(STD_OUTPUT_HANDLE, hChildStdoutWr)) 
                ErrorExit("Redirecting STDOUT failed"); 
              
              BOOL fSuccess = DuplicateHandle(
                GetCurrentProcess(), 
                hChildStdoutRd,
                    GetCurrentProcess(), 
                &hChildStdoutRdDup ,
                0,
                    FALSE,
                    DUPLICATE_SAME_ACCESS);
                if( !fSuccess )
                    ErrorExit("DuplicateHandle failed");
                CloseHandle(hChildStdoutRd);
              
              /************************************************
               *    redirecting child process's STDIN    *
               ************************************************/
              hSaveStdin = GetStdHandle(STD_INPUT_HANDLE); 
              if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) 
                ErrorExit("Stdin pipe creation failed\n"); 
              
              if (! SetStdHandle(STD_INPUT_HANDLE, hChildStdinRd)) 
                ErrorExit("Redirecting Stdin failed"); 
              
              fSuccess = DuplicateHandle(
                GetCurrentProcess(), 
                hChildStdinWr, 
                GetCurrentProcess(),
                &hChildStdinWrDup, 
                0, 
                FALSE,                 
                DUPLICATE_SAME_ACCESS); 
              if (! fSuccess) 
                ErrorExit("DuplicateHandle failed"); 
              CloseHandle(hChildStdinWr);   
              /************************************************
               *      創建子進程(即啟動SAMPLE.EXE)    *
               ************************************************/
              fSuccess = CreateChildProcess( lpProgram );
              if ( !fSuccess ) 
                ErrorExit("Create process failed"); 
              
              // 父進程輸入輸出流的還原設置
              if (! SetStdHandle(STD_INPUT_HANDLE, hSaveStdin)) 
                ErrorExit("Re-redirecting Stdin failed\n"); 
              if (! SetStdHandle(STD_OUTPUT_HANDLE, hSaveStdout)) 
                ErrorExit("Re-redirecting Stdout failed\n"); 
              WriteToPipe( lpInputFile ) ;
              ReadFromPipe( lpOutputFile ); 
                      delete lpProgram ;
                      delete lpInputFile ;
                      delete lpOutputFile ;
            } 
            BOOL CreateChildProcess( LPTSTR lpProgram ) 
            { 
              PROCESS_INFORMATION piProcInfo; 
              STARTUPINFO siStartInfo;
              BOOL bFuncRetn = FALSE; 
              
              ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
              ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
              siStartInfo.cb = sizeof(STARTUPINFO); 
              
              bFuncRetn = CreateProcess ( NULL, lpProgram, NULL, NULL, TRUE, \
                            0, NULL, NULL, &siStartInfo, &piProcInfo);
              if (bFuncRetn == 0) 
              {
                ErrorExit("CreateProcess failed\n");
                return 0;
              } 
              else 
              {
                CloseHandle(piProcInfo.hProcess);
                CloseHandle(piProcInfo.hThread);
                return bFuncRetn;
              }
            }
            VOID WriteToPipe( LPTSTR lpInputFile ) 
            { 
              HANDLE hInputFile = CreateFile(lpInputFile, GENERIC_READ, 0, NULL, 
                OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); 
              if (hInputFile == INVALID_HANDLE_VALUE) 
                return ;
              BOOL fSuccess ;
              DWORD dwRead, dwWritten; 
              CHAR chBuf[BUFSIZE] = {0} ; 
              
              for (;;) 
              { 
                fSuccess = ReadFile( hInputFile, chBuf, BUFSIZE, &dwRead, NULL) ;
                if ( !fSuccess || dwRead == 0)
                  break; 
                fSuccess = WriteFile( hChildStdinWrDup, chBuf, dwRead, &dwWritten, NULL) ;
                if ( !fSuccess ) 
                  break; 
              } 
                
              if (! CloseHandle(hChildStdinWrDup)) 
                ErrorExit("Close pipe failed\n"); 
              CloseHandle ( hInputFile ) ;
            } 
            VOID ReadFromPipe( LPTSTR lpOutputFile ) 
            { 
              HANDLE hOutputFile = CreateFile( lpOutputFile, GENERIC_READ|GENERIC_WRITE, 
                FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 
              if (hOutputFile == INVALID_HANDLE_VALUE) 
                return ;
              BOOL fSuccess ;
              DWORD dwRead, dwWritten; 
              CHAR chBuf[BUFSIZE] = { 0 }; 
              
              if (!CloseHandle(hChildStdoutWr)) 
                ErrorExit("Closing handle failed"); 
              
              for (;;) 
              { 
                fSuccess = ReadFile( hChildStdoutRdDup, chBuf, BUFSIZE, &dwRead, NULL) ;
                if( !fSuccess || dwRead == 0) 
                {
                  break; 
                }
                fSuccess = WriteFile( hOutputFile, chBuf, dwRead, &dwWritten, NULL) ;
                if ( !fSuccess ) 
                  break; 
              } 
              CloseHandle ( hOutputFile ) ;
            } 
            VOID ErrorExit (LPTSTR lpszMessage) 
            { 
              MessageBox( 0, lpszMessage, 0, 0 ); 
            }
            
            二、命名管道
            命名管道具有以下幾個特征:
            (1)命名管道是雙向的,所以兩個進程可以通過同一管道進行交互。
            (2)命名管道不但可以面向字節流,還可以面向消息,所以讀取進程可以讀取寫進程發送的不同長度的消息。
            (3)多個獨立的管道實例可以用一個名稱來命名。例如幾個客戶端可以使用名稱相同的管道與同一個服務器進行并發通信。
            (4)命名管道可以用于網絡間兩個進程的通信,而其實現的過程與本地進程通信完全一致。
            實驗目標:在客戶端輸入數據a和b,然后發送到服務器并計算a+b,然后把計算結果發送到客戶端??梢远鄠€客戶端與同一個服務器并行通信。
            界面設計:
             http://bbs.pediy.com/upload/2006/41/image/namedpipe.gif 
            難點所在:
            實現的過程比較簡單,但有一個難點。原本當服務端使用ConnectNamedPipe函數后,如果有客戶端連接,就可以直接進行交互。原來我在實現過程中,當管道空閑時,管道的線程函數會無限(INFINITE)阻塞。若現在需要停止服務,就必須結束所有的線程,TernimateThread可以作為一個結束線程的方法,但我基本不用這個函數。一旦使用這個函數之后,目標線程就會立即結束,但如果此時的目標線程正在操作互斥資源、內核調用、或者是操作共享DLL的全局變量,可能會出現互斥資源無法釋放、內核異常等現象。這里我用重疊I/0來解決這個問題,在創建PIPE時使用FILE_FLAG_OVERLAPPED標志,這樣使用ConnectNamedPipe后會立即返回,但線程的阻塞由等待函數WaitForSingleObject來實現,等待OVERLAPPED結構的事件對象被設置。
            客戶端主要代碼:
            代碼:
            void CMyDlg::OnSubmit() 
            {
              // 打開管道
              HANDLE hPipe = CreateFile("\\\\.\\Pipe\\NamedPipe", GENERIC_READ | GENERIC_WRITE, \
                0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL) ;
              if ( hPipe == INVALID_HANDLE_VALUE )
              {
                this->MessageBox ( "打開管道失敗,服務器尚未啟動,或者客戶端數量過多" ) ;
                return ;
              }
              DWORD nReadByte, nWriteByte ;
              char szBuf[1024] = {0} ;
              // 把兩個整數(a,b)格式化為字符串
              sprintf ( szBuf, "%d %d", this->nFirst, this->nSecond ) ;
              // 把數據寫入管道
              WriteFile ( hPipe, szBuf, strlen(szBuf), &nWriteByte, NULL ) ;
              memset ( szBuf, 0, sizeof(szBuf) ) ;
              // 讀取服務器的反饋信息
              ReadFile ( hPipe, szBuf, 1024, &nReadByte, NULL ) ;
              // 把返回信息格式化為整數
              sscanf ( szBuf, "%d", &(this->nResValue) ) ;
              this->UpdateData ( false ) ;
              CloseHandle ( hPipe ) ;
            }
            
            服務端主要代碼:
            代碼:
            // 啟動服務
            void CMyDlg::OnStart() 
            {
              CString lpPipeName = "\\\\.\\Pipe\\NamedPipe" ;
              for ( UINT i = 0; i < nMaxConn; i++ )
              {
                // 創建管道實例
                PipeInst[i].hPipe =  CreateNamedPipe ( lpPipeName, PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED, \
                      PIPE_TYPE_BYTE|PIPE_READMODE_BYTE|PIPE_WAIT, nMaxConn, 0, 0, 1000, NULL ) ;
                if ( PipeInst[i].hPipe == INVALID_HANDLE_VALUE )
                {
                  DWORD dwErrorCode = GetLastError () ;
                  this->MessageBox ( "創建管道錯誤!" ) ;
                  return ;
                }
                // 為每個管道實例創建一個事件對象,用于實現重疊IO
                PipeInst[i].hEvent  =  CreateEvent ( NULL, false, false, false ) ;
                // 為每個管道實例分配一個線程,用于響應客戶端的請求
                PipeInst[i].hTread = AfxBeginThread ( ServerThread, &PipeInst[i], THREAD_PRIORITY_NORMAL ) ;
              }
              
              this->SetWindowText ( "命名管道實例之服務器(運行)" ) ;
              this->MessageBox ( "服務啟動成功" ) ;
            }
            // 停止服務
            void CMyDlg::OnStop() 
            {
              DWORD dwNewMode = PIPE_TYPE_BYTE|PIPE_READMODE_BYTE|PIPE_NOWAIT ;
              for ( UINT i = 0; i < nMaxConn; i++ )
              {
                SetEvent ( PipeInst[i].hEvent ) ;
                CloseHandle ( PipeInst[i].hTread ) ;
                CloseHandle ( PipeInst[i].hPipe ) ;
              }
                
              this->SetWindowText ( "命名管道實例之服務器" ) ;
              this->MessageBox ( "停止啟動成功" ) ;
            }
            // 線程服務函數
            UINT ServerThread ( LPVOID lpParameter )
            {
              DWORD  nReadByte = 0, nWriteByte = 0, dwByte = 0 ;  
              char  szBuf[MAX_BUFFER_SIZE] = {0} ;
              PIPE_INSTRUCT  CurPipeInst = *(PIPE_INSTRUCT*)lpParameter ;
              OVERLAPPED OverLapStruct = { 0, 0, 0, 0, CurPipeInst.hEvent } ;
              while ( true )
              {
                memset ( szBuf, 0, sizeof(szBuf) ) ;  
                // 命名管道的連接函數,等待客戶端的連接(只針對NT)
                ConnectNamedPipe ( CurPipeInst.hPipe, &OverLapStruct ) ;
                // 實現重疊I/0,等待OVERLAPPED結構的事件對象
                WaitForSingleObject ( CurPipeInst.hEvent, INFINITE ) ;
                // 檢測I/0是否已經完成,如果未完成,意味著該事件對象是人工設置,即服務需要停止
                if ( !GetOverlappedResult ( CurPipeInst.hPipe, &OverLapStruct, &dwByte, true ) )
                  break ;
                // 從管道中讀取客戶端的請求信息
                if ( !ReadFile ( CurPipeInst.hPipe, szBuf, MAX_BUFFER_SIZE, &nReadByte, NULL ) )
                {
                  MessageBox ( 0, "讀取管道錯誤!", 0, 0 ) ;
                  break ;
                }
                
                int a, b ;
                sscanf ( szBuf, "%d %d", &a, &b ) ;
                pMyDlg->nFirst    = a ;
                pMyDlg->nSecond    = b ;
                pMyDlg->nResValue  = a + b ;
                memset ( szBuf, 0, sizeof(szBuf) ) ;
                sprintf ( szBuf, "%d", pMyDlg->nResValue ) ;
                // 把反饋信息寫入管道
                WriteFile ( CurPipeInst.hPipe, szBuf, strlen(szBuf), &nWriteByte, NULL ) ;
                pMyDlg->SetDlgItemInt ( IDC_FIRST, a, true ) ;
                pMyDlg->SetDlgItemInt ( IDC_SECOND, b, true ) ;
                pMyDlg->SetDlgItemInt ( IDC_RESULT, pMyDlg->nResValue, true ) ;
                // 斷開客戶端的連接,以便等待下一客戶的到來
                DisconnectNamedPipe ( CurPipeInst.hPipe ) ;
              }
              return 0 ;
            }
            
            Posted on 2011-02-15 12:30 鄭興鋒 閱讀(792) 評論(0)  編輯 收藏 引用
            久久99精品国产麻豆婷婷| 伊人久久精品影院| 久久精品嫩草影院| 99久久综合国产精品二区| 久久久中文字幕日本| 亚洲AV无码久久精品成人 | 蜜桃麻豆www久久| 久久er99热精品一区二区| 久久发布国产伦子伦精品| 久久久精品久久久久久| 性高湖久久久久久久久| 久久国产成人| 久久精品国产亚洲77777| 久久久久久久亚洲精品| 国产午夜精品久久久久免费视 | 久久99精品久久久久久久久久| 日日噜噜夜夜狠狠久久丁香五月| 国产精品亚洲综合专区片高清久久久| 看全色黄大色大片免费久久久 | 亚洲国产精品无码久久一线| 婷婷久久综合九色综合98| 久久久久久精品免费免费自慰 | 亚洲婷婷国产精品电影人久久| 久久丫精品国产亚洲av不卡| 四虎久久影院| 久久亚洲国产精品五月天婷| 国产精品久久久久久影院| 综合人妻久久一区二区精品| 久久久久无码精品| 久久亚洲高清观看| 2020久久精品国产免费| 欧美午夜精品久久久久免费视| 欧美亚洲国产精品久久| 性做久久久久久免费观看| 国产农村妇女毛片精品久久| 一本大道久久a久久精品综合| 久久精品国产网红主播| 久久九九全国免费| 亚洲国产精品久久久久久| 99久久国产综合精品五月天喷水| 国产成人久久精品一区二区三区|