我正在研究应用程序(托管(Web)和已安装的Windows客户端)之间的实时消息的体系结构设计。由于Windows客户端安装在客户端,我们无法控制防火墙,即打开任何端口。
所以我想到使用SignalR通过websocket或fallback技术使用http发送实例通知。我们的Windows客户端目前正在使用.Net 4.0 Framework。
我做了一些关于通过信号器确保消息传递的研究,人们建议用GUID确认消息,但不确定如何实现这个想法。此外,当客户端未连接时,我需要在RabbitMQ上排队消息,而onConnected只是从队列中发送所有消息。
namespace SignalRHub.Hubs
{
[Authorize]
public class ChatHub : Hub
{
public void Send(string who, string data)
{
string name = Context.User.Identity.Name;
List<string> groups = new List<string>();
groups.Add(name);
groups.Add(who);
Message message = new Message()
{
messageId = Guid.NewGuid(),
data = data
};
Clients.Groups(groups).addNewMessageToPage(name, JsonConvert.SerializeObject(message));
}
public void AcknowledgeServer(Guid messageId)
{
// Process the message acknowledge
var msgGuid = messageId;
}
public override Task OnConnected()
{
string name = Context.User.Identity.Name;
Groups.Add(Context.ConnectionId, name);
return base.OnConnected();
}
}
public class Message
{
public Guid messageId { get; set; }
public String data { get; set; }
}
}
请指教?
答案 0 :(得分:0)
我只需几步即可完成此操作。
根据设计,signalR无法保证客户端只会收到消息和只接收一次,因为即使您实现了这样的协议,您也可能会丢失确认消息并仍然会收到消息两次。
第一种方法:服务器为所有未处理的消息发送事件触发器addNewMessages。可以做的伎俩,但如果永远不会调用发送事件...
//just a sample repo : should be persisted, thread safe ...
public static class MessageRepository
{
public static List<Message> UnprocessedMessages = new List<Message>();
public static void AddMessage(Message msg)
{
UnprocessedMessages.Add(msg);
}
public static List<Message> GetMessagesByIssuer(string issuer)
{
return UnprocessedMessages.Where(m => m.Issuer.Equals(issuer)).ToList();
}
public static void Remove(Guid id)
{
if (UnprocessedMessages.Any(m => m.messageId.Equals(id)))
{
var message = UnprocessedMessages.FirstOrDefault(m => m.messageId.Equals(id));
UnprocessedMessages.Remove(message);
}
}
}
[Authorize]
public class ChatHub : Hub
{
public void Send(string who, string data)
{
string name = Context.User.Identity.Name;
List<string> groups = new List<string>();
groups.Add(name);
groups.Add(who);
Message message = new Message()
{
messageId = Guid.NewGuid(),
data = data,
Issuer = name,
Receiver = who,
CreationDate = DateTime.UtcNow
};
MessageRepository.AddMessage(message);
var unProcessedMessages = MessageRepository.GetMessagesByIssuer(name).OrderBy(m => m.CreationDate).ToList();
unProcessedMessages.ForEach(m =>
{
Clients.Groups(groups).addNewMessageToPage(name, JsonConvert.SerializeObject(m));
});
}
public void AcknowledgeServer(Guid messageId)
{
// Process the message acknowledge
var msgGuid = messageId;
MessageRepository.Remove(msgGuid);
}
public override Task OnConnected()
{
string name = Context.User.Identity.Name;
Groups.Add(Context.ConnectionId, name);
return base.OnConnected();
}
}
public class Message
{
public Guid messageId { get; set; }
public String data { get; set; }
public String Issuer { get; set; }
public String Receiver { get; set; }
public DateTime CreationDate { get; set; }
}
这是第二种方法(在我看来更好)。您有一个带有Timer的C#客户端单例,它会定期检查您的存储库,然后发送未处理的消息。您还应该删除过期的邮件。
public class PresenceMonitor
{
private Timer _timer;
// How often we plan to check if the connections in our store are valid
private readonly TimeSpan _presenceCheckInterval = TimeSpan.FromSeconds(10);
public PresenceMonitor()
{
}
public void StartMonitoring()
{
if (_timer == null)
{
_timer = new Timer(_ =>
{
try
{
Check();
}
catch (Exception ex)
{
// Don't throw on background threads, it'll kill the entire process
Trace.TraceError(ex.Message);
}
},
null,
TimeSpan.Zero,
_presenceCheckInterval);
}
}
private void Check()
{
var context = GlobalHost.ConnectionManager.GetHubContext<ChatHub>();
var messages = MessageRepository.GetAllMessages();
messages.ForEach(m =>
{
if (context != null)
{
List<string> groups = new List<string>();
groups.Add(m.Issuer);
groups.Add(m.Receiver);
context.Clients.Groups(groups).addNewMessageToPage(m.Issuer, JsonConvert.SerializeObject(m));
}
});
}
}
在startuc
课程中,初始化Monitor
。