在分布式系统中,服务器与客户端之间的通信是一个常见的需求。Mailslot是一种特殊的通信机制,允许多个客户端向服务器发送消息,而无需服务器确认。这种“fire-and-forget”的通信方式在某些场景下非常有用,例如日志记录、通知等。然而,.NET标准库并没有提供直接支持Mailslot操作的类,现有的一些封装也不够简洁和优雅。因此,本文将介绍一种自定义的Mailslot实现方式。
服务器端的实现涉及到创建一个Mailslot,并监听来自客户端的消息。以下是服务器端的代码示例:
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
public class MailslotServer : IDisposable
{
private SafeMailslotHandle _handle;
public MailslotServer(string name)
{
_handle = CreateMailslot($"\\\\.\\mailslot\\{name}", 0, 0, IntPtr.Zero);
if (_handle.IsInvalid)
throw new Win32Exception();
}
public string GetNextMessage()
{
int messageBytes;
int bytesRead;
int messages;
if (!GetMailslotInfo(_handle, IntPtr.Zero, out messageBytes, out messages, IntPtr.Zero))
throw new Win32Exception();
if (messageBytes == Mailslot.MailslotNoMessage)
return null;
var bBuffer = new byte[messageBytes];
if (!ReadFile(_handle, bBuffer, messageBytes, out bytesRead, IntPtr.Zero) || bytesRead == 0)
throw new Win32Exception();
return Encoding.Unicode.GetString(bBuffer);
}
public void Dispose()
{
if (_handle != null)
{
_handle.Close();
_handle = null;
}
}
}
服务器端首先创建一个Mailslot,然后通过调用GetNextMessage方法来获取客户端发送的消息。如果Mailslot中没有消息,则返回null。
客户端的实现相对简单,只需要向服务器端创建的Mailslot发送消息即可。以下是客户端的代码示例:
public class MailslotClient : IDisposable
{
private SafeMailslotHandle _handle;
private readonly string _name;
private readonly string _machine;
public MailslotClient(string name) : this(name, ".")
{
}
public MailslotClient(string name, string machine)
{
_name = name;
_machine = machine;
}
public void SendMessage(string msg)
{
if (_handle == null) CreateHandle();
int bytesWritten;
byte[] bMessage = Encoding.Unicode.GetBytes(msg);
bool succeeded = WriteFile(_handle, bMessage, bMessage.Length, out bytesWritten, IntPtr.Zero);
if (!succeeded || bMessage.Length != bytesWritten)
{
if (_handle != null) _handle.Close();
_handle = null;
throw new Win32Exception();
}
}
public void Dispose()
{
if (_handle != null)
{
_handle.Close();
_handle = null;
}
}
private void CreateHandle()
{
_handle = CreateFile($"\\\\{_machine}\\mailslot\\{_name}", FileDesiredAccess.GenericWrite, FileShareMode.FileShareRead, IntPtr.Zero, FileCreationDisposition.OpenExisting, 0, IntPtr.Zero);
if (_handle.IsInvalid)
throw new Win32Exception();
}
}