You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

328 lines
9.8 KiB

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
{
/// <summary>
/// 应用程序id,用来生成唯一的共享内存映射文件的文件名
/// </summary>
private uint m_appID;
/// <summary>
/// 共享文件大小,固定为10K,对进程管理命令来说足够了
/// </summary>
private const long SHM_LENGHT = 10240;
/// <summary>
/// 文件
/// </summary>
FileStream m_fileStream;
private MemoryMappedFile m_shmFile;
/// <summary>
/// 共享内存映射文件的文件名
/// </summary>
private string m_filename;
/// <summary>
/// 关闭共享文件,关闭文件
/// </summary>
public void Close()
{
if (m_shmFile != null)
{
m_shmFile.Dispose();
m_shmFile = null;
}
if(m_fileStream != null)
{
m_fileStream.Dispose();
m_fileStream = null;
}
}
/// <summary>
/// 删除文件
/// </summary>
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);
}
/// <summary>
/// 创建一个固定大小的文件
/// </summary>
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 = "";
}
}
/// <summary>
/// 创建共享内存,为了兼容linux,只能通过这种方式,而且文件必须自己打开,缺省的CreateFromFile权限有问题,会attach不成功,说是文件被另外的进程打开,晕倒
/// 而且不支持有名字映射的共享内存方式,所以CreateNew,CreateOrOpen,OpenExisting之类的api全都不能用
/// </summary>
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);
}
}
}
/// <summary>
/// 打开已经存在的共享内存
/// </summary>
/// <returns></returns>
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);
}
/// <summary>
/// 写入命令,头包括一个byte,1表示req,2表示ack,4个byte的长度信息
/// </summary>
/// <param name="strMessage"></param>
/// <returns></returns>
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;
}
}
/// <summary>
/// 读取命令
/// </summary>
/// <returns></returns>
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;
}
}
}
}