我真的希望有人能帮我理解内存映射文件是如何工作的。我一直在网上做很多研究,但有时候寻求帮助就容易多了。如何使用内存映射文件在同一台计算机上的服务器和客户端之间进行通信?
我正在运行的服务器上创建文档创建系统。我想创建一个服务,它将按需将模板文件提供给客户端程序(这本身就是请求文件的实际客户端的服务)。
我想模板文件留在内存中,使得服务器没有用户每次请求一个文件的时候访问硬盘驱动器。
我无法使用管道连接,因为所涉及的文件太大。我可以使用套接字式连接,或者将文件分解成更小的部分,但这似乎不如内存映射文件更好,它似乎旨在解决这个确切问题,并且似乎是最快捷的方式在两个进程之间共享大文件。
我无法得到的内存映射文件的工作,直到我改变了文件名是@“全球\” +文件名(见代码)。这工作了一段时间,但后来它停止工作 - 但我不知道我改变了什么。我有这个想法here。我认为,无论“全球”预先确定与我的问题的解决方案有关,因为这似乎表明Windows内存以类似于文件系统格式化的方式分段,而不同的进程可能具有不同的访问级别。
下面是从服务的代码:
public partial class TemplateDistributorService : ServiceBase
{
protected ServiceHost host;
public static Dictionary memFiles;
public static Dictionary fileSizes;
public TemplateDistributorService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
memFiles = new Dictionary();
fileSizes = new Dictionary();
foreach (var path in args)
{
string fileName = @"Global\" + System.IO.Path.GetFileNameWithoutExtension(path);
var sz = new System.IO.FileInfo(path).Length;
//all files must be less than 2GB for easier programming. I don't anticipate
//this being a problem, and if it does become one, we can up this to the max system file size (4 GB)
if (sz <= System.Int32.MaxValue)
{
using (System.IO.MemoryMappedFiles.MemoryMappedFile mmf =
System.IO.MemoryMappedFiles.MemoryMappedFile.CreateNew(fileName,
sz, System.IO.MemoryMappedFiles.MemoryMappedFileAccess.ReadWrite))
{
fileSizes[fileName] = System.Convert.ToInt32(sz);
using (var stream = mmf.CreateViewStream())
{
using (var writer = new System.IO.BinaryWriter(stream))
{
byte[] fileBytes = System.IO.File.ReadAllBytes(path);
writer.Write(fileBytes);
}
}
memFiles[fileName] = mmf;
}
}
}
host = new ServiceHost(typeof(TemplateDistributor));
host.AddServiceEndpoint(typeof(TemplateDistributorInterface),
new NetNamedPipeBinding(),
"net.pipe://localhost/PipedTemplateAccess");
host.Open();
}
protected override void OnStop()
{
host.Close();
}
}
[ServiceContract]
public interface TemplateDistributorInterface
{
[OperationContract]
int AddFile(string path, string fileName);
[OperationContract]
int FileSize(string fileName);
}
public class TemplateDistributor : TemplateDistributorInterface
{
public int AddFile(string path, string fileName)
{
//prepending the filename with 'Global\' is really important. Not sure why, but you can't access the memory mapped file without it
//https://stackoverflow.com/questions/11301978/memory-mapped-file-and-win-service-cannot-find-file-created-by-server
//string fileName = @"Global\" + System.IO.Path.GetFileNameWithoutExtension(path);
if (TemplateDistributorService.memFiles.ContainsKey(fileName))
{
if (TemplateDistributorService.memFiles[fileName] != null)
{
return 0;
}
else
{
return -1;
}
}
var sz = new System.IO.FileInfo(path).Length;
if (sz > System.Int32.MaxValue)
{
return -1;
}
using (System.IO.MemoryMappedFiles.MemoryMappedFile mmf =
System.IO.MemoryMappedFiles.MemoryMappedFile.CreateNew(fileName, sz, System.IO.MemoryMappedFiles.MemoryMappedFileAccess.ReadWrite))
{
TemplateDistributorService.fileSizes[fileName] = System.Convert.ToInt32(sz);
using (var stream = mmf.CreateViewStream())
{
using (var writer = new System.IO.BinaryWriter(stream))
{
byte[] fileBytes = System.IO.File.ReadAllBytes(path);
writer.Write(fileBytes);
}
}
TemplateDistributorService.memFiles[fileName] = mmf;
}
return 1;
}
public int FileSize(string fileName)
{
if (TemplateDistributorService.fileSizes.ContainsKey(fileName))
{
return TemplateDistributorService.fileSizes[fileName];
}
else
{
return 0;
}
}
}
这里是客户端代码(抛出:“类型‘System.IO.FileNotFoundException’的未处理的异常发生在System.Core.dll”当我尝试打开内存映射文件):
[ServiceContract]
public interface TemplateDistributorInterface
{
[OperationContract]
int AddFile(string path, string fileName);
[OperationContract]
int FileSize(string fileName);
}
class Program
{
static void Main(string[] args)
{
TestDocService();
}
static void TestDocService()
{
var path = @"C:\Users\jack.geiger\Documents\BuschFTP.docx";
ChannelFactory streamPipeFactory =
new ChannelFactory(
new NetNamedPipeBinding(),
new EndpointAddress(
"net.pipe://localhost/PipedTemplateAccess"));
TemplateDistributorInterface streamPipeProxy = streamPipeFactory.CreateChannel();
string fileName = @"Global\Restricted\" + System.IO.Path.GetFileNameWithoutExtension(path);
var f = streamPipeProxy.AddFile(path, fileName);
f = streamPipeProxy.AddFile(path, fileName);
//prepending the filename with 'Global\' is really important. Not sure why, but you can't access the memory mapped file without it
//https://stackoverflow.com/questions/11301978/memory-mapped-file-and-win-service-cannot-find-file-created-by-server
//mmf is being created in service, but I can't find it here for some reason
using (System.IO.MemoryMappedFiles.MemoryMappedFile mmf =
System.IO.MemoryMappedFiles.MemoryMappedFile.OpenExisting(
fileName,
System.IO.MemoryMappedFiles.MemoryMappedFileRights.Read,
System.IO.HandleInheritability.None))
{
using (var stream = mmf.CreateViewStream(0, 0, System.IO.MemoryMappedFiles.MemoryMappedFileAccess.Read))
{
using (System.IO.BinaryReader binReader = new System.IO.BinaryReader(stream))
{
var fileSize = streamPipeProxy.FileSize(fileName);
var bts = binReader.ReadBytes(streamPipeProxy.FileSize(fileName));
using (System.IO.MemoryStream memStr = new System.IO.MemoryStream())
{
memStr.Read(bts, 0, bts.Length);
memStr.Seek(0, System.IO.SeekOrigin.Begin);
using (WordprocessingDocument template = WordprocessingDocument.Create(memStr, WordprocessingDocumentType.Document))
{
template.MainDocumentPart.Document.RemoveAllChildren();
}
}
}
}
}
}
+1
是你的服务器真正创造“全球\ somefile”没有错误? –
+0
是的,我已经调试过。此外,作为额外的健全性检查,我可以在客户端应用程序中检查“f”的值,并返回1 - 表示成功创建文件(除非在不重新启动服务器的情况下调试两次,当它返回0时 - 表示文件已存在) –
+0
采取'WinObj.exe' - 当你*创建*“内存映射文件”'Global \ SomeName'时,必须创建'\ BaseNamedObjects \ SomeName' * section *对象。并在* open *'Global \ BuschFTP.docx'之前 - 检查 - 是否有'\ BaseNamedObjects \ BuschFTP.docx' *节*存在? –