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
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|