using System; using System.Diagnostics; using System.IO.MemoryMappedFiles; using System.IO; namespace Sog.IO { //通过共享内存发送服务器指令,每个ID一个共享内存文件(就是一块内存) //服务器管理通过这个,比如 stop, reload, 服务器管理指令和gm指令的支持 //方法是游戏正常进程(SogLoader)启动会创建一块共享内存,其他模式启动的SogLoader通过写共享内存来实现进程间通信 //这种方法支持跨平台,比用信号量要通用 //进程间通信的指令使用字符串,方便扩展和其他工具集成使用 // int serverid // byte flag // int length // commandstring // 共享内存的读写不加锁,所以暂时只支持同时存在一条指令,后期可优化扩展 public class ShareMemoryCommand : IShareCommand { /// /// 应用程序id,用来生成唯一的共享内存映射文件的文件名 /// private uint m_appID; /// /// 共享文件大小,固定为10K,对进程管理命令来说足够了 /// private const long SHM_LENGHT = 10240; /// /// 文件 /// FileStream m_fileStream; private MemoryMappedFile m_shmFile; /// /// 共享内存映射文件的文件名 /// private string m_filename; /// /// 关闭共享文件,关闭文件 /// public void Close() { if (m_shmFile != null) { m_shmFile.Dispose(); m_shmFile = null; } if(m_fileStream != null) { m_fileStream.Dispose(); m_fileStream = null; } } /// /// 删除文件 /// public void DeleteFile() { //先关闭共享内存对象,否则不可能删除文件成功 Close(); if (m_appID == 0) { return; } if (m_filename == null) { return; } //删除文件有可能会不成功,别的进程占用,多试几次 for (int i=0; i<10; i++) { if (File.Exists(m_filename)) { try { File.Delete(m_filename); } catch(Exception ex) { Console.WriteLine(ex.Message); System.Threading.Thread.Sleep(500); } } else { break; } } } private string GetFilePathName(string filepath) { return filepath + "/sog_shf_" + ServerIDUtils.IDToString(m_appID); } public ShareMemoryCommand(uint appID) { m_appID = appID; m_filename = GetFilePathName("."); } public ShareMemoryCommand(string filepath,uint appID) { m_appID = appID; m_filename = GetFilePathName(filepath); } /// /// 创建一个固定大小的文件 /// private void CreateFile() { try { m_fileStream = File.Create(m_filename); m_fileStream.SetLength(SHM_LENGHT); //设置文件大小 m_fileStream.Dispose(); m_fileStream = null; } catch (Exception ex) { ex.Source = ""; } } /// /// 创建共享内存,为了兼容linux,只能通过这种方式,而且文件必须自己打开,缺省的CreateFromFile权限有问题,会attach不成功,说是文件被另外的进程打开,晕倒 /// 而且不支持有名字映射的共享内存方式,所以CreateNew,CreateOrOpen,OpenExisting之类的api全都不能用 /// public void Create() { CreateFile(); m_fileStream = File.Open(m_filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite); m_shmFile = MemoryMappedFile.CreateFromFile(m_fileStream, null, SHM_LENGHT , MemoryMappedFileAccess.ReadWrite , HandleInheritability.None, false); int procId = 0; using (Process curProcess = Process.GetCurrentProcess()) { procId = curProcess.Id; } //创建后清空内存 using (MemoryMappedViewStream stream = m_shmFile.CreateViewStream()) { byte[] idBytes = BitConverter.GetBytes(procId); stream.Write(idBytes, 0, 4); for (int i = 4; i < SHM_LENGHT; i++) { stream.WriteByte(0); } } } /// /// 打开已经存在的共享内存 /// /// public bool Attach() { try { if (File.Exists(m_filename) == false) { return false; } m_fileStream = File.Open(m_filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite); m_shmFile = MemoryMappedFile.CreateFromFile(m_fileStream, null, SHM_LENGHT, MemoryMappedFileAccess.ReadWrite, HandleInheritability.None, false); } catch(Exception ) { //应该是不存在 //Console.WriteLine("ShareMemoryCommand.AttachShareMemory failed ,message",ex.Message); return false; } return true; } public bool WriteCommand(string strCommand) { return DoWriteMessage(strCommand, false); } public bool WriteAck(string strCommand) { return DoWriteMessage(strCommand, true); } /// /// 写入命令,头包括一个byte,1表示req,2表示ack,4个byte的长度信息 /// /// /// private bool DoWriteMessage(string strMessage,bool isAckCommand) { //用完删除 using (MemoryMappedViewStream stream = m_shmFile.CreateViewStream()) { stream.Seek(4, System.IO.SeekOrigin.Begin); //flag + length byte[] havecommandByte = new byte[1]; stream.Read(havecommandByte, 0, 1); //上个命令没有读完 if(havecommandByte[0] == 1) { return false; } byte[] byteArray = System.Text.Encoding.UTF8.GetBytes(strMessage); byte[] lengthByte = BitConverter.GetBytes((int)byteArray.Length); stream.Seek(4, System.IO.SeekOrigin.Begin); if (isAckCommand) { stream.WriteByte(2); } else { stream.WriteByte(1); } stream.Write(lengthByte, 0, 4); stream.Write(byteArray, 0, byteArray.Length); stream.Flush(); return true; } } /// /// 读取命令 /// /// public string ReadCommand() { //用完删除 using (MemoryMappedViewStream stream = m_shmFile.CreateViewStream()) { stream.Seek(4, System.IO.SeekOrigin.Begin); //flag + length byte[] headInfo = new byte[5]; int iLength = stream.Read(headInfo, 0, 5); if (headInfo[0] != 1) { return null; } return DoReadMessage(headInfo, stream); } } public string ReadCommandAck() { //用完删除 using (MemoryMappedViewStream stream = m_shmFile.CreateViewStream()) { stream.Seek(4, System.IO.SeekOrigin.Begin); //flag + length byte[] headInfo = new byte[5]; int iLength = stream.Read(headInfo, 0, 5); if (headInfo[0] != 2) { return null; } return DoReadMessage(headInfo, stream); } } private string DoReadMessage(byte[] headInfo, MemoryMappedViewStream stream) { int commandLength = BitConverter.ToInt32(headInfo, 1); byte[] strCommandByte = new byte[commandLength]; stream.Read(strCommandByte, 0, commandLength); string strCommand = System.Text.Encoding.UTF8.GetString(strCommandByte); //清空内容 stream.Seek(4, System.IO.SeekOrigin.Begin); stream.WriteByte(0); for (int i = 0; i < 5 + commandLength; i++) { stream.WriteByte(0); } stream.Flush(); return strCommand; } public int ReadProcessId() { using (MemoryMappedViewStream stream = m_shmFile.CreateViewStream()) { byte[] procId = new byte[4]; stream.Read(procId, 0, 4); int iId = BitConverter.ToInt32(procId, 0); return iId; } } } }