多线程命名管道

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

 

一、服务器端的实现。

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

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;
}


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 下面是一个简单的 Python 程序示例,用于在 Windows 上使用命名管道进行数据双向通信: ``` import os, msvcrt # 创建命名管道 pipe_name = "\\\\.\\pipe\\mypipe" os.mkfifo(pipe_name) # 父进程写入数据 with open(pipe_name, "w") as pipe: pipe.write("Hello, World!") # 子进程读取数据 with open(pipe_name, "r") as pipe: data = pipe.read() print(data) # 删除命名管道 os.remove(pipe_name) ``` 这段程序中,我们首先使用 os.mkfifo() 函数创建了一个名为 "mypipe" 的命名管道。之后,在父进程中使用 open() 函数打开管道进行写入操作,在子进程中使用 open() 函数打开管道进行读取操作。最后,使用 os.remove() 函数删除了命名管道。 注意: 由于 Windows 中命名管道的实现有所不同,所以在 Windows 上使用命名管道时需要使用 "\\\\.\\pipe\\" 前缀来指定管道的路径。 ### 回答2: 在Windows上,Python可以使用命名管道实现数据的双向通信。下面是一个使用Python建立命名管道进行双向通信的示例。 首先,我们需要导入所需的模块: ```python import os import win32pipe import win32file import win32event import pywintypes import threading ``` 然后,定义一个用于读取客户端发送数据的函数: ```python def read_from_pipe(pipe): while True: try: data = win32file.ReadFile(pipe, 4096) print("收到客户端发送的数据:", data[1].decode()) except pywintypes.error as e: if e.winerror == 232: # PIPE_BROKEN print("管道已关闭") break ``` 接下来,定义一个用于向客户端发送数据的函数: ```python def write_to_pipe(pipe): while True: try: message = input("请输入要发送给客户端的数据:") win32file.WriteFile(pipe, message.encode()) except pywintypes.error as e: if e.winerror == 232: # PIPE_BROKEN print("管道已关闭") break ``` 然后,在主函数中创建命名管道、启动读取和写入管道的线程: ```python def main(): pipe_name = r'\\.\pipe\my_pipe' pipe = win32pipe.CreateNamedPipe( pipe_name, win32pipe.PIPE_ACCESS_DUPLEX, win32pipe.PIPE_TYPE_MESSAGE | win32pipe.PIPE_WAIT, 1, # maximum number of instances 4096, # output buffer size 4096, # input buffer size 0, # default time out None # default security attributes ) win32pipe.ConnectNamedPipe(pipe, None) read_thread = threading.Thread(target=read_from_pipe, args=(pipe,)) write_thread = threading.Thread(target=write_to_pipe, args=(pipe,)) read_thread.start() write_thread.start() read_thread.join() write_thread.join() win32pipe.DisconnectNamedPipe(pipe) win32pipe.CloseHandle(pipe) if __name__ == "__main__": main() ``` 现在,我们可以运行该脚本,在终端上输入要发送给客户端的数据,然后在另一个终端上查看收到的数据。 请注意,在此示例中,管道的名称是“\\.\pipe\my_pipe”,可以根据需要修改。此外,由于管道是阻塞方式的,所以在读取和写入管道时,程序会一直等待,直到有数据到达或发送成功。 希望这个示例可以帮助你理解如何使用Python在Windows上进行命名管道的双向通信。 ### 回答3: Python在Windows系统中可以使用命名管道(Named Pipe)来实现双向数据通信。命名管道是一种特殊类型的文件,用于进程间或机器间通信。 下面是一个Python在Windows系统中使用命名管道实现双向数据通信的示例: 1. 创建命名管道: ``` import win32pipe import win32file pipe_name = r'\\.\pipe\my_pipe' pipe = win32pipe.CreateNamedPipe(pipe_name, win32pipe.PIPE_ACCESS_DUPLEX, win32pipe.PIPE_TYPE_MESSAGE | win32pipe.PIPE_READMODE_MESSAGE | win32pipe.PIPE_WAIT, win32pipe.PIPE_UNLIMITED_INSTANCES, 65536, 65536, 0, None) ``` 2. 连接命名管道: ``` win32pipe.ConnectNamedPipe(pipe, None) ``` 3. 从命名管道中读取数据: ``` data = win32file.ReadFile(pipe, 65536, None) ``` 4. 向命名管道中写入数据: ``` data_to_send = b'Message to send' win32file.WriteFile(pipe, data_to_send) ``` 5. 关闭命名管道: ``` win32file.CloseHandle(pipe) ``` 上述代码中,通过`CreateNamedPipe`函数创建了一个命名管道,使用`ConnectNamedPipe`函数连接管道。然后,可以使用`ReadFile`函数从管道中读取数据,使用`WriteFile`函数向管道中写入数据。最后,使用`CloseHandle`函数关闭管道。 需要注意的是,以上示例只是一个简单的示例,实际使用时可能需要考虑处理异常、多线程等情况。 希望以上内容对您有帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值