多线程命名管道

来源:互联网 发布:单片机编程经典pdf 编辑:程序博客网 时间:2024/05/22 13:05

    由于项目要求,迫切需要掌握多线程通信的技术,参考了网上的一些好文章,实现了多线程命名管道。我尽量把注释写全一些,这样读者看起来要轻松一些。

 

一、服务器端的实现。

服务器端完成以下几项工作:

1、创建多线程,每一个线程处理一个客户机的请求,可以满足多个客户端同时连接到服务器。

2、每一个线程负责连接一个客户机,并创建一个命名管道。

3、每一个命名管道创建一个监听程序,以服务客户机。

4、当有客户机退出连接时,服务器应该重新建立一个服务线程,以维持总的活跃管道数量不变。

 

服务器端代码如下,有详尽的注释,比较容易理解。

#include "stdafx.h"#include <Windows.h>#include <stdio.h>#include <conio.h>#define BUFFERSIZE 256    //缓冲区大小#define MAX_NUM_PIPES 5  //线程个数,即允许同时连接的客户机个数DWORD WINAPI PipeInstanceProc(LPVOID lpParameter);  //多线程回调函数DWORD WINAPI ListenPipeProc(LPVOID lpParameter);  //监听管道回调函数int _tmain(int argc, _TCHAR* argv[]){    HANDLE hThreadHandle;//线程句柄    DWORD threadId;//线程号    //创建五个线程,分别处理一个客户机连接    for (int i=0;i<MAX_NUM_PIPES;i++)    {        hThreadHandle=CreateThread(            NULL,//WinNT后永久设为NULL            0,//线程初始化堆栈大小,一般设为0            PipeInstanceProc,//线程回调函数的指针,即函数名            NULL,//传输给回调函数的参数,通过它实现对回调函数的控制            0,//线程创建完毕后的状态,0表示创建后执行线程,CREATE_SUSPENDED表示暂时挂起,等待叫醒            &threadId//线程ID值的地址            );        if(hThreadHandle==NULL)        {            printf("创建线程%d失败!%d\n",i,GetLastError());            return 0;        }        printf("创建命名管道线程%d成功!",i);    }        //等待线程结束    WaitForSingleObject(hThreadHandle,INFINITE);    CloseHandle(hThreadHandle);    hThreadHandle=NULL;    return 0;}/**    函数名称: PipeInstanceProc* 函数参数: (in)LPVOID:     附加信息*     函数作用: 处理一个命名管道实例的线程* 函数返回值:(DWORD)若顺利完成,则返回1;若获取域名失败,则返回0*/DWORD WINAPI PipeInstanceProc(LPVOID lpParameter){    HANDLE hPipeHandle;//命名管道的句柄    HANDLE hThreadListen;//管道监听句柄    /*    * 函数名称: CreateNamedPipe    * 函数参数: (in)LPCTSTR       命名管道名字,UNC标准( \\IP地址\Pipe\唯一标识文件路径 )    *      (in)DWORD       命名管道模式,单双向,读写控制,安全模式( 均被宏定义好,可位或操作 )    *      (in)DWORD       命名管道读,写,等待模式( 均被宏定义好,可位或操作 )    *      (in)DWORD       命名管道最多可创建的实例句柄个数    *      (in)DWORD       命名管道输出缓冲区大小    *      (in)DWORD       命名管道输入缓冲区大小    *      (in)DWORD       命名管道默认超时时间    *      (in)LPSECURITY_ATTRIBUTES 命名管道安全描述符,若为NULL,则句柄不可继承的默认安全。    * 函数作用: 创建一个命名管道实例    * 函数返回值:(HANDLE)成功则返回命名管道实例的句柄,失败则返回值INVALID_HANDLE_VALUE    */    hPipeHandle = CreateNamedPipe(        _T("\\\\.\\pipe\\TrackerService"),        PIPE_ACCESS_DUPLEX,        PIPE_TYPE_BYTE | PIPE_READMODE_BYTE |PIPE_WAIT,        PIPE_UNLIMITED_INSTANCES,        BUFFERSIZE,        BUFFERSIZE,        0,        NULL        );    if (hPipeHandle == INVALID_HANDLE_VALUE)    {        printf("创建命名管道失败!%d\n",GetLastError());        return 0;    }    printf("创建命名管道成功!\n");    HANDLE h;//线程句柄,用于一个客户机退出连接之后,服务器重新建立一个服务线程    //处理客户端信息    while (1)    {        /*        *    函数名称: ConnectNamedPipe        * 函数参数: (in)HANDLE:     命名管道实例句柄        *      (in)LPOVERLAPPED    是否锁定式命名管道        *     函数作用: 建立命名管道连接并监听        * 函数返回值:(bool)若顺利建立,则返回true;若顺利失败,则返回false        */        if (ConnectNamedPipe(hPipeHandle,NULL))        {            printf("有客户端连接命名管道!\n");            //Create listen pipe thread            hThreadListen = CreateThread(                NULL,                0,                ListenPipeProc,                hPipeHandle,                0,                NULL                );            if (hThreadListen==NULL)            {                printf("创建监听线程失败!\n");                return 0;            }                        printf("创建监听线程成功!\n");            WaitForSingleObject(hThreadListen,INFINITE);            //Close handle            CloseHandle(hThreadListen);            //set handle null            hThreadListen = NULL;        }        else        {            printf("有客户端断开连接!%d\n",GetLastError());            DWORD threadId;            //服务器重新建立一个服务线程            h=CreateThread(                NULL,//WinNT后永久设为NULL                0,//线程初始化堆栈大小,一般设为0                PipeInstanceProc,//线程回调函数的指针,即函数名                NULL,//传输给回调函数的参数,通过它实现对回调函数的控制                0,//线程创建完毕后的状态,0表示创建后执行线程,CREATE_SUSPENDED表示暂时挂起,等待叫醒                &threadId//线程ID值的地址                );            if(h==NULL)            {                printf("创建线程失败!%d\n",GetLastError());                return 0;            }            printf("创建命名管道线程成功!");            WaitForSingleObject(h,INFINITE);            CloseHandle(h);            h=NULL;            //销毁监听线程            CloseHandle(hThreadListen);            return 0;        }    }    CloseHandle(hPipeHandle);    return 1;}DWORD WINAPI ListenPipeProc(LPVOID lpParameter){    DWORD nBytesWritten;//发出的写入信息字节数    char cBuffer[BUFFERSIZE]="The MultiThread Test!...........";//字节存储数组    BOOL fSuccess = FALSE;    HANDLE hPipe=(HANDLE)lpParameter;//接收回调函数传来的参数    while (TRUE)    {        fSuccess= WriteFile(            hPipe,            cBuffer,            sizeof(cBuffer),            &nBytesWritten,            NULL);        if (!fSuccess)        {            break;        }    }    //刷新缓冲区    FlushFileBuffers(hPipe);    //断开命名管道的连接    if (DisconnectNamedPipe(hPipe) == 0)    {        printf("断开命名管道失败!%d\n",GetLastError());        CloseHandle(hPipe);        return 0;    }    CloseHandle(hPipe);    hPipe=NULL;    return 1;} 

二、客户机端的实现

客户机端完成以下工作:

1、检测服务器是否可以连接

2、打开命名管道

3、完成作业

 

客户机端代码如下:

#include "stdafx.h"#include <iostream>#include <Windows.h>int _tmain(int argc, _TCHAR* argv[]){    //检测服务器是否有命名管道可供连接    if (FALSE==WaitNamedPipe(_T("\\\\.\\pipe\\TrackerService"),NMPWAIT_WAIT_FOREVER))    {        std::cout<<"不能连接到服务器\n"<<std::endl;        return -1;    }    else    {        std::cout<<"连接服务器成功\n"<<std::endl;    }    //打开命名管道    HANDLE createHandle=CreateFile(_T("\\\\.\\pipe\\TrackerService"),GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);    if (createHandle==INVALID_HANDLE_VALUE)    {        std::cout<<"打开命名管道失败\n"<<std::endl;        return -1;    }    else    {        std::cout<<"打开命名管道成功\n"<<std::endl;    }    int lpNumberOfBytesRead=0;    char cBuffer[256];    //从服务器读取数据    if (ReadFile(createHandle,&cBuffer,sizeof(cBuffer),(LPDWORD)&lpNumberOfBytesRead,NULL))    {        std::cout<<cBuffer<<std::endl;    }    CloseHandle(createHandle);    return 0;}


原创粉丝点击