1. 程式人生 > >windows C++程序間和執行緒間通訊

windows C++程序間和執行緒間通訊

程序間通訊

程序基本概念

In computer science, inter-process communication or interprocess communication (IPC) refers specifically to the mechanisms an operating system provides to allow processes it manages to share data. Typically, applications can use IPC categorized as clients and servers, where the client requests data and the server responds to client requests.Many applications are both clients and servers, as commonly seen in distributed computing. Methods for achieving IPC are divided into categories which vary based on software requirements

, such as performance and modularity requirements, and system circumstances, such as network bandwidth and latency.

Approaches

Method Short Description Provided by (operating systems or other environments)
File A record stored on disk, or a record synthesized on demand by a file server, which can be accessed by multiple processes. Most operating systems
Signal; also Asynchronous System Trap A system message sent from one process to another, not usually used to transfer data but instead used to remotely command the partnered process. Most operating systems
Socket A data stream sent over a network interface, either to a different process on the same computer or to another computer on the network. Typically byte-oriented, sockets rarely preserve message boundaries. Data written through a socket requires formatting to preserve message boundaries. Most operating systems
Message queue A data stream similar to a socket, but which usually preserves message boundaries. Typically implemented by the operating system, they allow multiple processes to read and write to the message queue without being directly connected to each other. Most operating systems
Pipe A unidirectional data channel. Data written to the write end of the pipe is buffered by the operating system until it is read from the read end of the pipe. Two-way data streams between processes can be achieved by creating two pipes utilizing standard input and output. All POSIX systems, Windows
Named pipe A pipe implemented through a file on the file system instead of standard input and output. Multiple processes can read and write to the file as a buffer for IPC data. All POSIX systems, Windows, AmigaOS 2.0+
Semaphore A simple structure that synchronizes multiple processes acting on shared resources. All POSIX systems, Windows, AmigaOS
Shared memory Multiple processes are given access to the same block of memory which creates a shared buffer for the processes to communicate with each other. All POSIX systems, Windows
Message passing Allows multiple programs to communicate using message queues and/or non-OS managed channels, commonly used in concurrency models. Used in RPC, RMI, and MPI paradigms, Java RMI, CORBA, DDS, MSMQ, MailSlots, QNX, others
Memory-mapped file A file mapped to RAM and can be modified by changing memory addresses directly instead of outputting to a stream. This shares the same benefits as a standard file. All POSIX systems, Windows

程序間通訊(IPC)實現

The following IPC mechanisms are supported by Windows:

  • Clipboard
  • COM
  • Data Copy
  • DDE
  • File Mapping
  • Mailslots
  • Pipes
  • RPC
  • Windows Sockets

程序間通訊之共享記憶體實現

File mapping enables a process to treat the contents of a file as if they were a block of memory in the process’s address space. The process can use simple pointer operations to examine and modify the contents of the file. When two or more processes access the same file mapping, each process receives a pointer to memory in its own address space that it can use to read or modify the contents of the file. The processes must use a synchronization object, such as a semaphore, to prevent data corruption in a multitasking environment.

Following are some of the Win32 APIs that are used when working with shared memory (memory mapped objects):

  • CreateFileMapping()
  • MapViewOfFile()
  • UnMapViewOfFile()
  • CloseHandle()

Key Point: Mailslots offer an easy way for applications to send and receive short messages. They also provide the ability to broadcast messages across all computers in a network domain.

#include <iostream>
#include <Windows.h>
#include <tchar.h>
#include <stdio.h>

using namespace std;
//-------------------------------
#define MAX_SH_MEM_SIZE             1024
#define MAX_READ_PROCESSES_ALLOWED  3
#define WAIT_TIME_OUT               100
//-------------------------------

//-------------------------------
/*
Global variables
*/


HANDLE  g_hReadEvent[MAX_READ_PROCESSES_ALLOWED];
TCHAR   g_szReadEventName[] = _T("My Read Event");

HANDLE  g_hWriteEvent = NULL;
TCHAR   g_szWriteEventName[] = _T("My Write Event");

HANDLE  g_hSharedMemory = NULL;
LPTSTR  g_pBuffer = NULL;
TCHAR   g_szSharedMemoryName[] = _T("My Shared Memory");

bool    Initialize();
void    DeInitialize();
void    DisplayOptions();
bool    RecvAndProcessOption();
void    WriteAndPrint();
void    ReadAndPrint();

int main(int argv, char* argc[], char* env[]){
    if (Initialize()){
        std::cout << "Initialization of process was successful" << endl;
    }
    else
    {
        DeInitialize();
        std::cout << "Initialization of the process was not successful" << endl;
        return 1;
    }

    bool bContinue = true;
    while (bContinue){
        DisplayOptions();
        bContinue = RecvAndProcessOption();
    }
    DeInitialize();
}

bool    Initialize(){
    for (int i = 0; i < MAX_READ_PROCESSES_ALLOWED; i++)
    {
        g_hReadEvent[i] = NULL;
    }
    TCHAR szBuffer[32];
    /*
    原型
    int sprintf( char *buffer, const char *format, [ argument] … );
    引數列表
    buffer:char型指標,指向將要寫入的字串的緩衝區。
    format:格式化字串。
    [argument]...:可選引數,可以是任何型別的資料。
    返回值
    返回寫入buffer 的字元數,出錯則返回-1. 如果 buffer 或 format 是空指標,且不出錯而繼續,函式將返回-1,並且 errno 會被設定為 EINVAL。
    sprintf 返回被寫入buffer 的位元組數,結束字元‘\0’不計入內。即,如果“Hello”被寫入空間足夠大的buffer後,函式sprintf 返回5。
    */
    for (int i = 0; i < MAX_READ_PROCESSES_ALLOWED; i++)
    {
        _stprintf_s(szBuffer, _T("%s %d"), g_szReadEventName, i);
        g_hReadEvent[i] = CreateEvent(NULL, false, true, szBuffer);
        if (NULL == g_hReadEvent[i]){
            return false;
        }
    }
    /*
    HANDLE CreateEvent(
    LPSECURITY_ATTRIBUTESlpEventAttributes,// 安全屬性
    BOOLbManualReset,// 復位方式
    BOOLbInitialState,// 初始狀態
    LPCTSTRlpName // 物件名稱 事件物件的名稱
    );

    如果函式呼叫成功,函式返回事件物件的控制代碼。如果對於命名的物件,在函式呼叫前已經被建立,函式將返回存在的事件物件的控制代碼,而且在GetLastError函式中返回ERROR_ALREADY_EXISTS。
    如果函式失敗,函式返回值為NULL,如果需要獲得詳細的錯誤資訊,需要呼叫GetLastError。
    */
    g_hWriteEvent = CreateEvent(NULL, false, true, g_szWriteEventName);
    if (NULL == g_hWriteEvent){
        return false;
    }
    /*函式原型
    HANDLE WINAPI CreateFileMapping(
    _In_HANDLE hFile,
    _In_opt_LPSECURITY_ATTRIBUTES lpAttributes,
    _In_DWORD flProtect,
    _In_DWORD dwMaximumSizeHigh,
    _In_DWORD dwMaximumSizeLow,
    _In_opt_LPCTSTR lpName);
    建立一個新的檔案對映核心物件。
    */
    g_hSharedMemory = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, MAX_SH_MEM_SIZE, g_szSharedMemoryName);
    if (NULL == g_hSharedMemory || INVALID_HANDLE_VALUE == g_hSharedMemory){
        std::cout << "Error occured while creating file mapping object:" << GetLastError() << endl;
        return false;
    }
    /*
    函式原型
    LPVOID WINAPI MapViewOfFile(
    __in HANDLE hFileMappingObject,
    __in DWORD dwDesiredAccess,
    __in DWORD dwFileOffsetHigh,
    __in DWORD dwFileOffsetLow,
    __in SIZE_T dwNumberOfBytesToMap
    );
    MapViewOfFile,計算機語言。將一個檔案對映物件對映到當前應用程式的地址空間。MapViewOfFileEx允許我們指定一個基本地址來進行對映。
    引數1:hFileMappingObject 為CreateFileMapping()返回的檔案映像物件控制代碼。
    引數2:dwDesiredAccess 對映物件的檔案資料的訪問方式,而且同樣要與CreateFileMapping()函式所設定的保護屬性相匹配。 可取以下值:
    FILE_MAP_ALL_ACCESS 等價於CreateFileMapping的 FILE_MAP_WRITE|FILE_MAP_READ. 檔案對映物件被建立時必須指定PAGE_READWRITE 選項.
    FILE_MAP_COPYA 可以讀取和寫入檔案.寫入操作會導致系統為該頁面建立一份副本.在呼叫CreateFileMapping時必須傳入PAGE_WRITECOPY保護屬性.
    FILE_MAP_EXECUTE 可以將檔案中的資料作為程式碼來執行.在呼叫CreateFileMapping時可以傳入PAGE_EXECUTE_READWRITE或PAGE_EXECUTE_READ保護屬性.
    FILE_MAP_READ 可以讀取檔案.在呼叫CreateFileMapping時可以傳入PAGE_READONLY或PAGE_READWRITE保護屬性.
    FILE_MAP_WRITEA 可以讀取和寫入檔案.在呼叫CreateFileMapping時必須傳入PAGE_READWRITE保護屬性.
    引數3:dwFileOffsetHigh 表示檔案對映起始偏移的高32位.
    引數4:dwFileOffsetLow 表示檔案對映起始偏移的低32位.(64KB對齊不是必須的)
    引數5:dwNumberOfBytes 指定對映檔案的位元組數.

    */
    g_pBuffer = (LPTSTR)MapViewOfFile(g_hSharedMemory, FILE_MAP_ALL_ACCESS, 0, 0, MAX_SH_MEM_SIZE);
    if (NULL == g_pBuffer){
        std::cout << "Error occured while mapping view of the file:" << GetLastError() << endl;
        return false;
    }
    return true;
}


void    DeInitialize(){
    for (int i = 0; i < MAX_READ_PROCESSES_ALLOWED; i++)
    {
        CloseHandle(g_hReadEvent[i]);
    }
    CloseHandle(g_hWriteEvent);
    UnmapViewOfFile(g_pBuffer);
    CloseHandle(g_hSharedMemory);
}

void DisplayOptions(){
    cout << "--------------------------------------------------------------" << endl;
    cout << "------------Operation on Share Memory--(Read/Write)-----------" << endl;
    cout << "------------Enter 1 to write to the shared memory-------------" << endl;
    cout << "------------Enter 2 to read the shared memory-----------------" << endl;
    cout << "------------Enter 3 to exit the application-------------------" << endl;
    cout << "--------------------------------------------------------------" << endl;
    cout << "Enter option:";
}

bool RecvAndProcessOption(){
    int nInput;
    bool bReturnValue = true;
    cin >> nInput;
    switch (nInput){
    case 1:
        cout << "Write Operation selected" << endl;
        WriteAndPrint();
        bReturnValue = true;
        break;
    case 2:
        cout << "Read Operation selected" << endl;
        ReadAndPrint();
        bReturnValue = true;
        break;
    case 3:
        cout << "Quit Operation selected" << endl;
        bReturnValue = false;
        break;
    default:
        cout << "Invalid Operation selected" << endl;
        bReturnValue = true;
        break;
    }
    return  bReturnValue;
}

void WriteAndPrint(){
    cout << "Trying to write operation to complete..." << endl;
    /**
    DWORD WINAPI WaitForSingleObject(
    __in HANDLE hHandle,
    __in DWORD dwMilliseconds
    );
    hHandle[in]物件控制代碼。可以指定一系列的物件,如Event、Job、Memory resource notification、Mutex、Process、Semaphore、Thread、Waitable timer等。
    dwMilliseconds[in]定時時間間隔,單位為milliseconds(毫秒).
    如果指定一個非零值,函式處於等待狀態直到hHandle標記的物件被觸發,或者時間到了。
    如果dwMilliseconds為0,物件沒有被觸發訊號,函式不會進入一個等待狀態,它總是立即返回。
    如果dwMilliseconds為INFINITE,物件被觸發訊號後,函式才會返回。

    返回值:
    WAIT_ABANDONED 0x00000080:當hHandle為mutex時,如果擁有mutex的執行緒在結束時沒有釋放核心物件會引發此返回值。
    WAIT_OBJECT_0 0x00000000 :指定的物件表現為有訊號狀態
    WAIT_TIMEOUT 0x00000102:等待超時
    WAIT_FAILED 0xFFFFFFFF :出現錯誤,可通過GetLastError得到錯誤程式碼
    */
    if (WAIT_OBJECT_0 == WaitForSingleObject(g_hWriteEvent, INFINITE)){
        cout << "Waiting for all read Operations to complete..." << endl;
        /*
        原型:DWORD WaitForMultipleObjects(
        DWORD nCount,
        const HANDLE* lpHandles,
        BOOL bWaitAll,
        DWORD dwMilliseconds
        );
        如果函式成功,返回值表示該事件導致該函式返回。這個值可以是下列之一。
        ValueMeaning
        WAIT_OBJECT_0到(WAIT_OBJECT_0 + nCount - 1如果bWaitAll為TRUE),則返回值表明所有指定物件的狀態訊號。
        如果bWaitAll為FALSE,則返回值減去不是WAIT_OBJECT_0表示lpHandles陣列的物件的滿意指數的等待。如果多個物件在通話過程中訊號成為,這是與所有的訊號物件的最小索引值的訊號物件的陣列索引。
        WAIT_ABANDONED_0至(WAIT_ABANDONED_0 + nCount - 1)如果bWaitAll為TRUE,則返回值表明所有指定物件的狀態是觸發的,並且至少物件之一,是一個廢棄的互斥物件。
        If bWaitAll is FALSE, the return value minus WAIT_ABANDONED_0 indicates the lpHandles array index of an abandoned mutex object that satisfied the wait
        如果bWaitAll為FALSE,則返回值減去WAIT_ABANDONED_0 表示一個廢棄的互斥物件在lpHandles陣列中的下標,滿足等待。
        WAIT_TIMEOUTThe超時間隔已過,由bWaitAll引數指定的條件得不到滿足。
        */
        DWORD dwWaitReadResult = WaitForMultipleObjects(MAX_READ_PROCESSES_ALLOWED, g_hReadEvent, TRUE, INFINITE);
        if (WAIT_OBJECT_0 == dwWaitReadResult){
            cout << "Enter a string (without spaces):";
            char c_text[100];
            //cin >> strText;
            //gets(c_text);
            fflush(stdin); //清空輸入緩衝區 c++
            /**
            stdin
            std = stand     in = input 
            stdin 就是標準輸入裝置,一般地就是鍵盤了
            stdin是重向定義,
            定義為標準輸入裝置(即健盤)
            stdout
            標準輸出裝置(顯示器)
            函式原型
            char *fgets(char *buf, int bufsize, FILE *stream);
            引數
            *buf: 字元型指標,指向用來儲存所得資料的地址。
            bufsize: 整型資料,指明儲存資料的大小。
            *stream: 檔案結構體指標,將要讀取的檔案流。
            */
            fgets(c_text, 100, stdin);

            //g_pBuffer=(LPTSTR)(LPCSTR)c_text;
            CopyMemory(g_pBuffer, c_text, strlen(c_text));
            cout << "Shared Memory:" << g_pBuffer << endl;
        }
        else{
            cout << "Error occured while waiting:" << GetLastError() << endl;
        }
        cout << "Setting the Write Event..." << endl;
        SetEvent(g_hWriteEvent);
        cout << "Setting the Read Events..." << endl;
        for (int i = 0; i < MAX_READ_PROCESSES_ALLOWED; i++)
        {
            SetEvent(g_hReadEvent[i]);
        }
    }
    else{
        cout << "Error occured while waiting:"<<GetLastError() << endl;
    }
}

void ReadAndPrint(){
    cout << "Trying to read and print the shared memory..." << endl;
    bool bContinue = true;
    while (bContinue){
        cout << "Waiting for write operation to complete..." << endl;
        DWORD dwWaitResult = WaitForSingleObject(g_hWriteEvent, INFINITE);
        if (WAIT_OBJECT_0 == dwWaitResult){
            bool bEventFound = false;
            for (int i = 0; i < MAX_READ_PROCESSES_ALLOWED; i++)
            {
                DWORD dwWaitResult = WaitForSingleObject(g_hReadEvent[i], WAIT_TIME_OUT);
                if (WAIT_OBJECT_0 == dwWaitResult){
                    bEventFound = true;
                    cout << "Setting the Write Event..." << endl;
                    SetEvent(g_hWriteEvent);
                    cout << "Setting the Write Event..." << endl;
                    SetEvent(g_hWriteEvent);

                    cout << "Shared Memory:" << g_pBuffer << endl;
                    cout << "Setting the Read Event..." << endl;
                    SetEvent(g_hReadEvent[i]);
                    bContinue = false;
                    break;
                }
                else
                {
                    continue;
                }
            }
            if (false == bEventFound){
                cout << "Setting the Write Event..." << endl;
                SetEvent(g_hWriteEvent);
                Sleep(WAIT_TIME_OUT);
            }
        }
        else
        {
            cout << "Error occured while waiting:"<<GetLastError() << endl;
        }
    }
}

Notes:

  • Source code is platform dependent.
  • Can only be used for processes that are on the same computer.
  • Once a memory mapped object is mapped into the process area, using it is very straightforward and easy.
  • Perhaps the fastest IPC mechanism.

程序間通訊之命名管道實現

There are two types of pipes for two-way communication: anonymous pipes and named pipes.

Anonymous pipes enable related processes to transfer information to each other. Typically, an anonymous pipe is used for redirecting the standard input or output of a child process so that it can exchange data with its parent process. To exchange data in both directions (duplex operation), you must create two anonymous pipes. The parent process writes data to one pipe using its write handle, while the child process reads the data from that pipe using its read handle. Similarly, the child process writes data to the other pipe and the parent process reads from it. Anonymous pipes cannot be used over a network, nor can they be used between unrelated processes.

Named pipes are used to transfer data between processes that are not related processes and between processes on different computers. Typically, a named-pipe server process creates a named pipe with a well-known name or a name that is to be communicated to its clients. A named-pipe client process that knows the name of the pipe can open its other end, subject to access restrictions specified by named-pipe server process. After both the server and client have connected to the pipe, they can exchange data by performing read and write operations on the pipe.

Key Point: Anonymous pipes provide an efficient way to redirect standard input or output to child processes on the same computer. Named pipes provide a simple programming interface for transferring data between two processes, whether they reside on the same computer or over a network.

Pipes are FIFO in behavior (First-In, First-Out). Pipes are of two types – Anonymous Pipes and Named Pipes.

An Anonymous Pipe is created using the CreatePipe() API. An anonymous pipe is local, and cannot be used to communicate over a network. An anonymous pipe is unnamed, one-way, and generally used to transfer data from a parent process to a child process.

Named pipes can be one-way or duplex. Named pipes will work between processes on the same computer as well as between processes across the network.

Following are some of the Win32 APIs that are used when working with Named Pipes:

  • CreateNamedPipe()
  • ConnectNamedPipe()
  • WaitNamedPipe()
  • DisconnectNamedPipe()
  • ReadFile()
  • WriteFile()
  • CloseHandle()

A Named Pipe name needs to be in the following format:

  • For named pipe server - \.\pipe\PipeName
  • For named pipe client - \ComputerName\pipe\PipeName

Source Code Of Client:

#include <Windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <tchar.h>

/*
1. [CreateFile](http://baike.baidu.com/view/1288759.htm)
*/
LPTSTR g_szPipeName = _T("\\\\.\\Pipe\\MyNamedPipe");
#define BUFFER_SIZE 1024
#define ACK_MESG_REVE "Message received successfully"

int main(int argv,char* argc[],char* env[]){

    HANDLE hPipe;
    /*
    HANDLE WINAPI CreateFile(
    _In_ LPCTSTR lpFileName,//普通檔名或者裝置檔名
    _In_ DWORD dwDesiredAccess, //訪問模式(寫/讀)
    _In_ DWORD dwShareMode,//共享模式
    _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, //指向安全屬性的指標
    _In_ DWORD dwCreationDisposition, //如何建立
    _In_ DWORD dwFlagsAndAttributes, //檔案屬性
    _In_opt_ HANDLE hTemplateFile //用於複製檔案控制代碼
    );
    */
    hPipe = CreateFile(g_szPipeName, 
        GENERIC_READ | GENERIC_WRITE, 
        0, 
        NULL, 
        OPEN_EXISTING,
        0, 
        NULL);

    if (INVALID_HANDLE_VALUE == hPipe){
        printf("\nError occurred while connecting to the server: %d",GetLastError());
        return  1;
    }
    else{
        printf("\nCreateFile() was successful.\n");

    }

    char szBuffer[BUFFER_SIZE];
    printf("\nEnter a message to be sent to the server\n");
    gets_s(szBuffer);
    DWORD cbBytes;

    BOOL bResult = WriteFile(hPipe, szBuffer, strlen(szBuffer) + 1, &cbBytes, NULL);
    if ((!bResult) || (strlen(szBuffer) + 1 != cbBytes)){
        printf("\n Error occurred while writing to the server: %d\n", GetLastError());
        CloseHandle(hPipe);
        return 1;
    }
    else{
        printf("\nWriteFile() was successful..\n");
    }

    bResult = ReadFile(hPipe, szBuffer, sizeof(szBuffer),&cbBytes, NULL);
    if ((!bResult) || (0 == cbBytes)){
        printf("\n Error occurred while reading from the server: %d\n", GetLastError());
        CloseHandle(hPipe);
        return 1;
    }
    else{
        printf("\nReadFile() was successful...\n");
    }
    printf("\nServer sent the following message:%s\n",szBuffer);
    CloseHandle(hPipe);
    system("pause");
    return 0;

}

Source Code Of Server:

#include <Windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <tchar.h>

TCHAR g_szPipeName[] = _T("\\\\\.\\Pipe\\MyNamedPipe");

#define BUFFER_SIZE 1024
#define ACK_MESG_RECV "Message reveived successfully..."

int main(int argc, char* argv[], char* env[]){
    HANDLE hPipe;
    /**
    HANDLE WINAPI CreateNamedPipe(
    _In_     LPCTSTR               lpName,
    _In_     DWORD                 dwOpenMode,
    _In_     DWORD                 dwPipeMode,
    _In_     DWORD                 nMaxInstances,
    _In_     DWORD                 nOutBufferSize,
    _In_     DWORD                 nInBufferSize,
    _In_     DWORD                 nDefaultTimeOut,
    _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes
    );
    CreateNamedPipe建立一個命名管道。返回的控制代碼由管道的伺服器端使用
    說明
    返回值
    Long,如執行成功,返回管道的控制代碼。INVALID_HANDLE_VALUE表示失敗。會設定GetLastError
    引數表
    引數 型別及說明
    lpName String,指定管道名,採用的形式是:\\.\管道\管道名。最多可達256個字元的長度,而且不用區分大小寫。如果存在指定名字的一個管道,則建立那個管道的一個新例項
    dwOpenMode Long,下述常陣列的一個組合
        下述常數之一(對於管道的所有例項都要一樣):
        PIPE_ACCESS_DUPLEX 管道是雙向的
        PIPE_ACCESS_INBOUND 資料從客戶端流到伺服器端
        PIPE_ACCESS_OUTBOUND 資料從伺服器端流到客戶端
        下述常數的任意組合
        FILE_FLAG_WRITE_THROUGH 在網路中建立的位元組型管道內,強迫資料在每次讀寫操作的時候通過網路傳輸。否則傳輸就可能延遲
        FILE_FLAG_OVERLAPPED 允許(但不要求)用這個管道進行非同步(重疊式)操作
        常數WRITE_DAC, WRITE_OWNER 和 ACCESS_ SYSTEM_SECURITY提供了附加的安全選項
    dwPipeMode Long,下述常陣列的一個組合:
        下述常數之一(管道的所有例項都必須指定相同的常數)
        PIPE_TYPE_BYTE 資料作為一個連續的位元組資料流寫入管道
        PIPE_TYPE_MESSAGE 資料用資料塊(名為“訊息”或“報文”)的形式寫入管道
        下述常數之一:
        PIPE_READMODE_BYTE 資料以單獨位元組的形式從管道中讀出
        PIPE_READMODE_MESSAGE 資料以名為“訊息”的資料塊形式從管道中讀出(要求指定PIPE_TYPE_MESSAGE)
        下述常數之一:
        PIPE_WAIT 同步操作在等待的時候掛起執行緒
        PIPE_NOWAIT(不推薦!) 同步操作立即返回。這樣可為非同步傳輸提供一種落後的實現方法,已由Win32的重疊式傳輸機制取代了
    nMaxInstances Long,這個管道能夠建立的最大例項數量。必須是1到常數PIPE_UNLIMITED_INSTANCES間的一個值。它對於管道的所有例項來說都應是相同的
    nOutBufferSize Long,建議的輸出緩衝區長度;零表示用預設設定
    nInBufferSize Long,建議的輸入緩衝區長度;零表示用預設設定
    nDefaultTimeOut Long,管道的預設等待超時。對一個管道的所有例項來說都應相同
    lpSecurityAttributes SECURITY_ATTRIBUTES,指定一個SECURITY_ATTRIBUTES結構,或者傳遞零值(將引數宣告為ByVal As Long,並傳遞零值),以便使用不允許繼承的一個預設描述符
    */
    hPipe = CreateNamedPipe(
        g_szPipeName, 
        PIPE_ACCESS_DUPLEX, 
        PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, 
        PIPE_UNLIMITED_INSTANCES,
        BUFFER_SIZE, 
        BUFFER_SIZE, 
        NMPWAIT_USE_DEFAULT_WAIT, 
        NULL);

    if (INVALID_HANDLE_VALUE == hPipe){
        printf("\n Error occurred while creating the pipe: %d\n",GetLastError());
        return 1;
    }
    else{
        printf("\nCreateNamedPipe() was successful.\n");
    }

    printf("\n Waiting for client connection...\n");
    /*
    BOOL WINAPI ConnectNamedPipe(
    _In_        HANDLE       hNamedPipe,
    _Inout_opt_ LPOVERLAPPED lpOverlapped
    );
    ConnectNamedPipe是指示一臺伺服器等待下去,直至客戶機同一個命名管道連線。
    返回值
    Long,如lpOverlapped為NULL,那麼:
        如管道已連線,就返回Ture(非零);如發生錯誤,或者管道已經連線,就返回零(GetLastError此時會返回ERROR_PIPE_CONNECTED)
        lpOverlapped有效,就返回零;如管道已經連線,GetLastError會返回ERROR_PIPE_CONNECTED;如重疊操作成功完成,就返回ERROR_IO_PENDING。在這兩種情況下,倘若一個客戶已關閉了管道,且伺服器尚未用DisconnectNamedPipe函式同客戶斷開連線,那麼GetLastError都會返回ERROR_NO_DATA
    引數表
    引數 型別及說明
    hNamedPipe Long,管道的控制代碼
    lpOverlapped OVERLAPPED,如設為NULL(傳遞ByVal As Long),表示將執行緒掛起,直到一個客戶同管道連線為止。否則就立即返回;此時,如管道尚未連線,客戶同管道連線時就會觸發lpOverlapped結構中的事件物件。隨後,可用一個等待函式來監視連線
    */
    BOOL bClientConnected = ConnectNamedPipe(hPipe, NULL);
    if (FALSE == bClientConnected){
        printf("\n Error occurred while connecting to the client:%d\n",GetLastError());
        CloseHandle(hPipe);
        return 1;
    }
    {
        printf("\nConnectNamePipe() was successful...\n");
    }

    /*
    BOOL WINAPI GetNamedPipeClientProcessId(
        _In_  HANDLE Pipe,
        _Out_ PULONG ClientProcessId
    );

    */
    //Get NamedPipeClientProcessId 
    PULONG ClientProcessID=0;
    GetNamedPipeClientProcessId(hPipe, ClientProcessID);
    if (FALSE == GetLastError()){
        printf("\nCan not Require processid of NamedPipeClient...\n");
    }
    else{
        printf("\n Current Client Process ID equal %ld\n",ClientProcessID);
    }

    //Get NamedPipeClient ComputerName
    /*
    BOOL WINAPI GetNamedPipeClientComputerName(
      _In_  HANDLE Pipe,
      _Out_ LPTSTR ClientComputerName,
      _In_  ULONG  ClientComputerNameLength
    );
    */
    LPTSTR ClientComputerName=NULL;
    ULONG ClientComputerNameLength = 1024;
    GetNamedPipeClientComputerName(hPipe, ClientComputerName, ClientComputerNameLength);
    if (FALSE == GetLastError()){
        printf("\nCan not require Client ComputerName of NamedPipeClient\n");
    }
    else{
        printf("current Client Computer name of NamedPipe equal %s\n",ClientComputerName);
    }

    char szBuffer[BUFFER_SIZE];
    DWORD cbBytes;

    BOOL bResult = ReadFile(hPipe, szBuffer, sizeof(szBuffer), &cbBytes, NULL);
    if ((!bResult) || (0 == cbBytes)){
        printf("\n Error occurred while reading from the client:%d\n",GetLastError());
        CloseHandle(hPipe);
        return 1;
    }
    else{
        printf("\n ReadFile() was successfule...\n");
    }
    printf("\n Client sent the following message: %s\n", szBuffer);
    strcpy_s(szBuffer, ACK_MESG_RECV);

    bResult = WriteFile(hPipe, szBuffer, strlen(szBuffer) + 1, &cbBytes, NULL);
    if ((!bResult) || (strlen(szBuffer) + 1 != cbBytes)){
        printf("\n Error occurred while writing to the client: %d", GetLastError());
        CloseHandle(hPipe);
        return 1;
    }
    else{
        printf("\n WriteFile() was successful...\n");
    }
    CloseHandle(hPipe);
    system("pause");
    return 0;
}
  • The source code is platform dependent.
  • A named pipe is easy to use.
  • A named pipe works across the network.

程序間通訊之WinSock實現

Windows Sockets is a protocol-independent interface. It takes advantage of the communication capabilities of the underlying protocols. In Windows Sockets 2, a socket handle can optionally be used as a file handle with the standard file I/O functions.

Windows Sockets are based on the sockets first popularized by Berkeley Software Distribution (BSD). An application that uses Windows Sockets can communicate with other socket implementation on other types of systems. However, not all transport service providers support all available options.

Key Point: Windows Sockets is a protocol-independent interface capable of supporting current and emerging networking capabilities.


此程式Client和Server都必須包含ws2_32.lib

WinSock provides very high level networking capabilities. It supports TCP/IP (the most widely used protocol), along with many other protocols – AppleTalk, DECNet, IPX/SPX etc.

WinSock supports Berkeley sockets, along with many other Windows specific extensions.

Following are some of the WinSock calls:

  • socket()
  • bind()
  • listen()
  • accept()
  • connect()
  • send()
  • recv()

Source Code of Server:

#include <WinSock2.h>
#include <stdio.h>
#include <stdlib.h>
#pragma comment(lib,"ws2_32.lib")

#define ACK_MESG_RECV "Message received successfully"
int main(int argc, char* argv[], char* env[]){

    WSADATA wsaData;
    int nResult;
    nResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (NO_ERROR != nResult){
        printf("\n Error occurred while executing WSAStartup()...");
        return 1;
    }
    else{
        printf("\nWSAStartup() was successful...\n");
    }
    SOCKET listensocket, remotesocket;
    int port_no, client_length;
    char szBuffer[256];
    struct sockaddr_in ServerAddress, ClientAddress;
    listensocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (INVALID_SOCKET == listensocket){
        printf("\nError occurred while opening socket: %ld.\n",WSAGetLastError());
        WSACleanup();
        return 0;
    }
    else{
        printf("\n socket() was successful...\n");
    }
    ZeroMemory((char*)&ServerAddress, sizeof(ServerAddress));
    port_no = atoi("4455");

    ServerAddress.sin_family = AF_INET;
    ServerAddress.sin_addr.S_un.S_addr = INADDR_ANY;
    ServerAddress.sin_port = htons(port_no);

    if (SOCKET_ERROR == bind(listensocket, (struct sockaddr*)&ServerAddress, sizeof(ServerAddress))){
        closesocket(listensocket);
        printf("\nError occurred while binding...\n");
        WSACleanup();
        return 0;
    }
    else{
        printf("bind() was successful..\n");
    }

    if (SOCKET_ERROR == listen(listensocket, SOMAXCONN)){
        closesocket(listensocket);
        printf("\n Error occurred while listening...\n");
        WSACleanup();
        return 0;
    }
    else{
        printf("\nlisten() was successful...\n");
    }
    client_length = sizeof(ClientAddress);

    remotesocket = accept(listensocket, (struct sockaddr *)&ClientAddress, &client_length);

    if (INVALID_SOCKET == remotesocket){
        closesocket(listensocket);
        printf("\nError occurred while accepting socket:%ld..\n", WSAGetLastError());
        WSACleanup();
        return 0;
    }
    else{
        printf("\naccept() was successful...\n");
    }

    printf("\n Client connected from :%s\n", inet_ntoa(ClientAddress.sin_addr));

    ZeroMemory(szBuffer, 256);

    int nBytesSent;
    int nBytesRecv;

    nBytesRecv = recv(remotesocket, szBuffer, 255, 0);
    if (SOCKET_ERROR == nBytesRecv){
        closesocket(listensocket);
        closesocket(remotesocket);
        printf("\nError occurred while receiving from socket...\n");
        WSACleanup();
        return 1;
    }
    else{
        printf("\nrecv() was successful...\n");
    }
    printf("\nThe following message was received:%s\n", szBuffer);
    nBytesSent = send(remotesocket, ACK_MESG_RECV, strlen(ACK_MESG_RECV), 0);
    if (SOCKET_ERROR == nBytesSent){
        closesocket(listensocket);
        closesocket(remotesocket);
        printf("\nError occurred while writing to socket.\n");
        WSACleanup();
        return 0;
    }
    else{
        printf("\nsend() was successful...\n");
    }

    closesocket(listensocket);
    closesocket(remotesocket);
    WSACleanup();
    system("pause");
    return 0;
}

Source Code of Client:

//#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <WinSock2.h>
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#pragma comment(lib, "ws2_32.lib")
/*
*/
int main(int argc, char* argv[], char* env[]){

    WSADATA wsaData;
    /*
    WSAStartup,是Windows Sockets Asynchronous的啟動命令、Windows下的網路程式設計介面軟體 Winsock1 或 Winsock2 裡面的一個命令。
    int WSAStartup ( WORD wVersionRequested, LPWSADATA lpWSAData );
    ⑴ wVersionRequested:一個WORD(雙位元組)型數值,在最高版本的Windows Sockets支援呼叫者使用,高階位元組指定小版本(修訂本)號,低位位元組指定主版本號。
    ⑵lpWSAData 指向WSADATA資料結構的指標,用來接收Windows Sockets實現的細節。
    WindowsSockets API提供的呼叫方可使用的最高版本號。高位位元組指出副版本(修正)號,低位位元組指明主版本號。

    本函式必須是應用程式或DLL呼叫的第一個Windows Sockets函式。它允許應用程式或DLL指明Windows Sockets API的版本號及獲得特定Windows Sockets實現的細節。應用程式或DLL只能在一次成功的WSAStartup()呼叫之後才能呼叫進一步的Windows Sockets API函式。
    返回值
    0 成功。
    否則返回下列的錯誤程式碼之一。注意通常依靠應用程式呼叫WSAGetLastError()機制獲得的錯誤程式碼是不能使用的,因為Windows Sockets DLL可能沒有建立“上一錯誤”資訊儲存的客戶資料區域。
    關於Windows Sockets提供者的說明:
    每一個Windows Sockets應用程式必須在進行其它Windows Sockets API呼叫前進行WSAStartup()呼叫。這樣,本函式就可以用於初始化的目的。
    */
    int nResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (NO_ERROR != nResult){
        printf("\n Error occurred while executing WSAStartup()...");
        return 1;
    }
    else{
        printf("\nWSAStartup() successful...");
    }
    //客戶端的socket物件結構
    SOCKET clientsocket;
    //連線伺服器的埠
    int port_no;
    //伺服器的socket資訊
    struct  sockaddr_in ServerAddress;
    //struct  hostent *Server;

    //輸入輸出緩衝區大小
    char szBuffer[256];

    //char* to int
    port_no = atoi("4455");
    /*
    socket()函式用於根據指定的地址族、資料型別和協議來分配一個套介面的描述字及其所用的資源。如果協議protocol未指定(等於0),則使用預設的連線方式。
    對於使用一給定地址族的某一特定套介面,只支援一種協議。但地址族可設為AF_UNSPEC(未指定),這樣的話協議引數就要指定了。協議號特定於進行通訊的“通訊域”。
    int socket( int af, int type, int protocol);
    af:一個地址描述。目前僅支援AF_INET格式,也就是說ARPA Internet地址格式。
    type:指定socket型別。新套介面的型別描述型別,如TCP(SOCK_STREAM)和UDP(SOCK_DGRAM)。常用的socket型別有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等。
    protocol:顧名思義,就是指定協議。套介面所用的協議。如呼叫者不想指定,可用0。常用的協議有,IPPROTO_TCP、IPPROTO_UDP、IPPROTO_STCP、IPPROTO_TIPC等,它們分別對應TCP傳輸協議、UDP傳輸協議、STCP傳輸協議、TIPC傳輸協議。
    若無錯誤發生,socket()返回引用新套介面的描述字。否則的話,返回INVALID_SOCKET錯誤,應用程式可通過WSAGetLastError()獲取相應錯誤程式碼。
    */
    clientsocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (INVALID_SOCKET == clientsocket){
        printf("\n Error occurred while opening socket: %d\n", WSAGetLastError());
        WSACleanup();
        return 0;
    }
    else{
        printf("\n socket() successful.\n");
    }
    //int ret = getaddrinfo("127.0.0.1", "4455",)

    /*Server = gethostbyname("127.0.0.1");
    if (Server == NULL){
        closesocket(clientsocket);
        printf("\nError occurred no such host\n");
        WSACleanup();
        return 0;
    }
    else{
        printf("\nGethostbyname() was successful\n");
    }*/
    //清理緩衝區
    ZeroMemory((char*)&ServerAddress,sizeof(ServerAddress));
    /*
    socket_in成員屬性
    sin_family指代協議族,在socket程式設計中只能是AF_INET
    sin_port儲存埠號(使用網路位元組順序),在linux下,埠號的範圍0~65535,同時0~1024範圍的埠號已經被系統使用或保留。
    sin_addr儲存IP地址,使用in_addr這個資料結構
    sin_zero是為了讓sockaddr與sockaddr_in兩個資料結構保持大小相同而保留的空位元組。
    */
    ServerAddress.sin_family = AF_INET;
    ServerAddress.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    ServerAddress.sin_port = htons(port_no);
    //CopyMemory((char*)&ServerAddress.sin_addr.S_un.S_addr, (char*)Server->h_addr_list, Server->h_length);
    //ServerAddress.sin_port = htons(port_no);


    //if (SOCKET_ERROR == connect(clientsocket, reinterpret_cast<const struct sockaddr *>(&ServerAddress), sizeof(ServerAddress))){
    /*
    函式原型: int connect(int s, const struct sockaddr * name, int namelen);
    引數:
    s:標識一個未連線socket
    name:指向要連線套接字的sockaddr結構體的指標
    namelen:sockaddr結構體的位元組長度
    若無錯誤發生,則connect()返回0。否則的話,返回SOCKET_ERROR錯誤,應用程式可通過WSAGetLastError()獲取相應錯誤程式碼。
    */
    if (SOCKET_ERROR == connect(clientsocket, (SOCKADDR*)&ServerAddress, sizeof(SOCKADDR))){
        closesocket(clientsocket);
        printf("\nError occurred while connecting...");
        WSACleanup();
        return 1;
    }
    else{
        printf("\nconnect() successful...");
    }
    ZeroMemory(szBuffer, 256);
    printf("\nPlease enter message to be sent to server..\n");

    fflush(stdin);
    fgets(szBuffer, 256, stdin);

    int nBytesSent; //傳送的位元組大小
    int nBytesRecv; //接受的位元組大小
    /*
    send()用於向一個已經連線的socket傳送資料,如果無錯誤,返回值為所傳送資料的總數,否則返回SOCKET_ERROR。[1]  send也是一個英文單詞。
    向一個已連線的套介面傳送資料。
    #include <winsock.h>
    int PASCAL FAR send( SOCKET s, const char FAR* buf, int len, int flags);
    s:一個用於標識已連線套介面的描述字。
    buf:包含待發送資料的緩衝區。
    len:緩衝區中資料的長度。
    flags:呼叫執行方式。

    若無錯誤發生,send()返回所傳送資料的總數(請注意這個數字可能小於len中所規定的大小)。否則的話,返回SOCKET_ERROR錯誤,應用程式可通過WSAGetLastError()獲取相應錯誤程式碼。
    */
    nBytesSent = send(clientsocket, szBuffer, strlen(szBuffer), 0);

    if (SOCKET_ERROR == nBytesSent){
        closesocket(clientsocket);
        printf("\nError occurred while writing to socket...");
        WSACleanup();
        return 0;
    }
    else{
        printf("\nsend() was successful...");
    }

    ZeroMemory(szBuffer, 256);
    /*
    函式原型int recv( _In_ SOCKET s, _Out_ char *buf, _In_ int len, _In_ int flags);
    返回值:
    若無錯誤發生,recv()返回讀入的位元組數。如果連線已中止,返回0。如果發生錯誤,返回-1,應用程式可通過perror()獲取相應錯誤資訊。
    */
    nBytesRecv = recv(clientsocket, szBuffer, 255, 0);
    if (SOCKET_ERROR == nBytesRecv){
        closesocket(clientsocket);
        printf("\n Error occurred while reading from socket\n");
        WSACleanup();
        return 0;
    }
    else{
        printf("\nrecv() was successful...\n");
    }

    printf("\n%s\n", szBuffer);
    /*
    本函式關閉一個套介面。更確切地說,它釋放套介面描述字s,以後對s的訪問均以WSAENOTSOCK錯誤返回。若本次為對套介面的最後一次訪問,則相應的名字資訊及資料佇列都將被釋放。
    關閉一個套介面。
    #include <winsock.h>
    int PASCAL FAR closesocket( SOCKET s);
    s:一個套介面的描述字。
    返回值:
    如無錯誤發生,則closesocket()返回0。否則的話,返回SOCKET_ERROR錯誤,應用程式可通過WSAGetLastError()獲取相應錯誤程式碼。
    */
    closesocket(clientsocket);
    /*
    WSACleanup()的功能是 終止Winsock 2 DLL (Ws2_32.dll) 的使用.
    標頭檔案
    #include <Winsock2.h>
    引用庫
    #pragma comment(lib, "ws2_32.lib")
    靜態加入一個lib檔案也就是庫檔案ws2_32.lib檔案,提供相關API的支援,否則需要動態載入ws2_32.dll。
    函式原型
    int PASCAL FAR WSACleanup (void);
    返回值
    操作成功返回值為0;否則返回值為SOCKET_ERROR,可以通過呼叫WSAGetLastError獲取錯誤程式碼。
    在一個多執行緒的環境下,WSACleanup()中止了Windows Sockets在所有執行緒上的操作.
    */
    WSACleanup();

    system("pause");
    return 0;
}
  • Widely used, and works on the same computer as well as across networks. Moreover, it can be used across various platforms and protocols.

  • Using WinSock requires a knowledge of relatively advanced networking concepts.

程序間通訊之郵路實現

Mailslots provide one-way communication. Any process that creates a mailslot is a mailslot server. Other processes, called mailslot clients, send messages to the mailslot server by writing a message to its mailslot. Incoming messages are always appended to the mailslot. The mailslot saves the messages until the mailslot server has read them. A process can be both a mailslot server and a mailslot client, so two-way communication is possible using multiple mailslots.

A mailslot client can send a message to a mailslot on its local computer, to a mailslot on another computer, or to all mailslots with the same name on all computers in a specified network domain. Messages broadcast to all mailslots on a domain can be no longer than 400 bytes, whereas messages sent to a single mailslot are limited only by the m