Kỹ Thuật Giao Tiếp Giữa Các Tiến Trình Trong C#

Giới thiệu

Có nhiều phương thức giao tiếp giữa các tiến trình (IPC), trong bài viết này chúng ta sẽ tổng hợp các phương thức phổ biến để tham khảo.

Ống Đặt Tên (Named Pipes)

Ống đặt tên là một phương thức giao tiếp hiệu quả giữa các tiến trình trên cùng một máy.

Code máy chủ:

Console.WriteLine("Máy chủ Ống Đặt Tên đang hoạt động...");
using (var serverPipe = new NamedPipeServerStream("my_pipe_channel", PipeDirection.InOut))
{
    Console.WriteLine("Chờ kết nối từ máy khách");
    await serverPipe.WaitForConnectionAsync();
    Console.WriteLine("Máy khách đã kết nối");

    //Đọc tin nhắn từ máy khách
    byte[] dataBuffer = new byte[2048];
    int bytesReceived = await serverPipe.ReadAsync(dataBuffer, 0, dataBuffer.Length);
    var clientMessage = Encoding.UTF8.GetString(dataBuffer, 0, bytesReceived);
    Console.WriteLine("Nhận được tin nhắn từ máy khách: " + clientMessage);

    //Gửi phản hồi
    string serverResponse = "Xin chào từ máy chủ";
    byte[] responseBuffer = Encoding.UTF8.GetBytes(serverResponse);
    await serverPipe.WriteAsync(responseBuffer, 0, responseBuffer.Length);
    Console.ReadLine();
}

Code máy khách:

Console.WriteLine("Máy khách Ống Đặt Tên đang hoạt động...");
using (var clientPipe = new NamedPipeClientStream(".", "my_pipe_channel", PipeDirection.InOut))
{
    Console.WriteLine("Đang kết nối đến máy chủ");
    await clientPipe.ConnectAsync();
    Console.WriteLine("Đã kết nối đến máy chủ");
    //Gửi tin nhắn
    string messageToServer = "Xin chào, tôi là máy khách";
    byte[] messageBuffer = Encoding.UTF8.GetBytes(messageToServer);
    await clientPipe.WriteAsync(messageBuffer, 0, messageBuffer.Length);
    //Đọc phản hồi
    byte[] responseBuffer = new byte[2048];
    int bytesRead = await clientPipe.ReadAsync(responseBuffer, 0, responseBuffer.Length);
    var serverResponse = Encoding.UTF8.GetString(responseBuffer, 0, bytesRead);
    Console.WriteLine("Nhận được phản hồi từ máy chủ: " + serverResponse);
    Console.ReadLine();
}

Ống Ẩn Danh (Anonymous Pipes)

Ống ẩn danh thường được sử dụng cho giao tiếp giữa tiến trình cha và con, trong khi ống đặt tên có thể được sử dụng cho bất kỳ tiến trình nào.

Ứng dụng chính: Giao tiếp giữa tiến trình cha-con, truyền dữ liệu đơn hướng đơn giản, giao tiếp tạm thời không cần chia sẻ qua nhiều tiến trình.

class AnonymousPipeServer
{
    static void Main(string[] args)
    {
        // Tạo ống ẩn danh
        using (var pipeServer = new AnonymousPipeServerStream(PipeDirection.Out, HandleInheritability.Inheritable))
        {
            Console.WriteLine("Tiến trình cha đang chạy...");

            // Khởi động tiến trình con
            var processInfo = new ProcessStartInfo("AnonymousPipeClient.exe");
            processInfo.Arguments = pipeServer.GetClientHandleAsString();
            processInfo.UseShellExecute = false;
            using (var childProcess = Process.Start(processInfo))
            {
                // Gửi tin nhắn đến tiến trình con
                using (var writer = new StreamWriter(pipeServer))
                {
                    writer.AutoFlush = true;
                    writer.WriteLine("Xin chào từ tiến trình cha!");
                }

                // Chờ tiến trình con kết thúc
                childProcess.WaitForExit();
            }
        }
    }
}

Máy khách

using System;
using System.IO;
using System.IO.Pipes;

class AnonymousPipeClient
{
    static void Main(string[] args)
    {
        // Lấy handle ống từ tiến trình cha
        string pipeHandle = args[0];
        using (var pipeClient = new AnonymousPipeClientStream(PipeDirection.In, pipeHandle))
        {
            Console.WriteLine("Tiến trình con đang chạy...");

            // Đọc tin nhắn từ tiến trình cha
            using (var reader = new StreamReader(pipeClient))
            {
                string message = reader.ReadLine();
                Console.WriteLine($"Nhận từ tiến trình cha: {message}");
            }
        }
    }
}

Tệp Định Dạng Bộ Nhớ (Memory-Mapped Files)

Tệp định dạng bộ nhớ cho phép nhiều tiến trình chia sẻ vùng bộ nhớ, phù hợp cho truyền tải dữ liệu lớn với hiệu suất cao.

Máy ghi

using System.IO.MemoryMappedFiles;
//Tệp định dạng bộ nhớ cho phép nhiều tiến trình chia sẻ vùng bộ nhớ, phù hợp cho truyền tải dữ liệu lớn.
Console.WriteLine("Máy ghi Tệp Định Dạng Bộ Nhớ khởi động~");
//Tạo một tệp định dạng bộ nhớ
using (var memoryMappedFile = MemoryMappedFile.CreateOrOpen("shared_memory", 15000))
{
    //Tạo một bộ truy cập để xem
    using (var accessor = memoryMappedFile.CreateViewAccessor())
    {
        Console.WriteLine("Tệp định dạng bộ nhớ đã tạo, chờ máy khách kết nối");
        Console.ReadLine();
        Console.WriteLine("Máy khách đã kết nối, bắt đầu ghi dữ liệu");
        var message = "Xin chào từ máy ghi";
        //Ghi dữ liệu
        var dataArray = System.Text.Encoding.UTF8.GetBytes(message);
        accessor.WriteArray(0, dataArray, 0, dataArray.Length);
        Console.WriteLine("Hoàn tất ghi dữ liệu");
        Console.ReadLine();
    }
}

Máy đọc

using System.IO.MemoryMappedFiles;
using System.Text;

Console.WriteLine("Máy đọc Tệp Định Dạng Bộ Nhớ khởi động");
using (var memoryMappedFile = MemoryMappedFile.OpenExisting("shared_memory"))
{
    Console.WriteLine("Tệp định dạng bộ nhớ đã được mở");
    using (var accessor = memoryMappedFile.CreateViewAccessor())
    {
        var buffer = new byte[1500];
        accessor.ReadArray(0, buffer, 0, buffer.Length);
        string message = Encoding.UTF8.GetString(buffer);
        Console.WriteLine("Đọc được dữ liệu: " + message);
        Console.ReadLine();
    }
}

Kênh Ipc

Kênh Ipc là một phương thức giao tiếp giữa các tiến trình dựa trên .NET Remoting, có hiệu suất cao, phù hợp cho giao tiếp giữa các tiến trình trên cùng một máy. Tuy nhiên, chỉ áp dụng cho .NET Framework: Kênh Ipc và .NET Remoting không áp dụng cho .NET Core và .NET 5+. .NET Remoting không hỗ trợ cơ chế bảo mật hiện đại, chỉ nên sử dụng trong môi trường đáng tin cậy.

Định nghĩa thư viện chung

public class RemoteService : MarshalByRefObject
{
    public string GreetUser(string userName)
    {
        return $"Xin chào, {userName}!";
    }
}

Máy chủ

static void Main(string[] args)
{
    // Tạo và đăng ký Kênh Ipc
    IpcServerChannel communicationChannel = new IpcServerChannel("MyIpcChannel");
    ChannelServices.RegisterChannel(communicationChannel, false);

    // Đăng ký đối tượng từ xa
    RemotingConfiguration.RegisterWellKnownServiceType(
        typeof(RemoteService), // Loại đối tượng từ xa
        "RemoteService.rem",   // URI đối tượng từ xa
        WellKnownObjectMode.Singleton); // Chế độ singleton

    Console.WriteLine("Máy chủ đang chạy. Nhấn Enter để thoát...");
    Console.ReadLine();
}

Máy khách

static void Main(string[] args)
{
    // Tạo và đăng ký Kênh Ipc
    IpcClientChannel clientChannel = new IpcClientChannel();
    ChannelServices.RegisterChannel(clientChannel, false);

    // Lấy đối tượng từ xa
    RemoteService remoteService = (RemoteService)Activator.GetObject(
        typeof(RemoteService), // Loại đối tượng từ xa
        "ipc://MyIpcChannel/RemoteService.rem"); // URI đối tượng từ xa

    // Gọi phương thức từ xa
    string response = remoteService.GreetUser(" từ máy chủ");
    Console.WriteLine($"Nhận từ máy chủ: {response}");

    Console.WriteLine("Nhấn Enter để thoát...");
    Console.ReadLine();
}

WCF

Phù hợp cho các kịch bản giao tiếp phức tạp, hỗ trợ nhiều giao thức.

Socket

Phù hợp cho giao tiếp mạng cần tùy chỉnh cao.

gRPC-----

Kết hợp hiệu suất cao và giao tiếp RPC đa ngôn ngữ.

gRPC trong .net - hướng dẫn sử dụng

Kênh Http

Kênh Tcp

Gửi Tin Nhắn Win32 Api

Tín hiệu Mutex

Địa chỉ code: https://gitee.com/xiaoqingyao/ipcdemo.git

Tham khảo:

Giao tiếp đa tiến trình C#, hôm nay, nó đã đến - blog园

Thẻ: C# ipc Named Pipes Memory-Mapped Files WCF

Đăng vào ngày 17 tháng 6 lúc 06:36