一、 最终完成效果
二、SignalR
SignalR是一个开源的ASP.NET库,用于实现实时网络应用程序。它允许服务器端代码能够主动向客户端发送实时数据,并允许客户端与服务器之间进行实时双向通信。
SignalR使用了多种技术来实现实时通信,包括WebSocket、Server-Sent Events(SSE)和Long Polling。SignalR可以用于各种应用场景,包括实时聊天、实时协作、实时推送通知以及实时数据更新等。它支持多种客户端平台,包括Web、桌面和移动设备。
聊天功能的实现主要使用WebSocket全双工通讯传输。
WebSocket
WebSocket是一种在客户端和服务器之间进行双向通信的协议。它允许服务器可以主动向客户端发送消息,而不需要客户端发送请求。相比传统的HTTP协议,WebSocket具有更低的延迟和更高的效率,WebSocket基于TCP协议,并通过握手过程来建立连接。一旦连接建立成功,双方就可以通过发送和接收消息来进行通信。WebSocket是一种持久连接,可以在同一个连接上发送多条消息,而不需要每次都重新建立连接。
详细了解可以前往微软官网:
三、服务器创建聊天通道
由于客户端每次连接使用SignalR生成的通道,SignalR都会创建一个对应的唯一ID(每次连接ID都会改变),该ID用于寻找聊天对象,所以我们可以创建一个字典类来进行一个映射。
private readonly static Dictionary<string, string> _connections = new();
1、用户连接
当用户连接的时候,我们将其自定义的用户ID和SignalR生成的用户ID存储到映射中。
[Authorize]//JWT权限
public override async Task OnConnectedAsync()
{
var userId = Context.GetHttpContext().Request.Query["userId"].ToString();//根据访问地址所带请求头获取到用户自定义ID
if (userId != null)
{
_connections[userId] = Context.ConnectionId;//将其用户ID和连接ID存储到映射中
}
await base.OnConnectedAsync();
}
2、用户断开
当用户断开连接时,我们将映射中的自定义的用户ID和SignalR生成的用户ID删除对应项。
[Authorize]
public override async Task OnDisconnectedAsync(Exception exception)
{
var userId = Context.GetHttpContext().Request.Query["userId"].ToString();
if (userId != null && _connections.ContainsKey(userId))
{
_connections.Remove(userId);//删除对应项
}
await base.OnDisconnectedAsync(exception);
}
3、给指定用户发送消息
接收收件人和接收人的用户ID可以用于在数据库中存放聊天记录。
[Authorize]
public async Task SendMsgToUser(string formUserId, string toUserId, string message, string tomessagename)
{
HubText hubText = new HubText();
hubText.Message = message;
hubText.Tomessagename = tomessagename;
if (_connections.ContainsKey(toUserId))//接收人连接在线
{
var connectionId = _connections[toUserId];
await Clients.Client(Context.ConnectionId).SendAsync("ReceiveMessage", hubText);//给自己发一份
await Clients.Client(connectionId).SendAsync("ReceiveMessage", hubText);//给接收人发一份
}
else//接收人连接不在线
{
await Clients.Client(Context.ConnectionId).SendAsync("ReceiveMessage", hubText);//给自己发一份
//这里可以选择保存到数据库中或者Redis或其他
}
}
四、客户端连接通道
1、创建连接
startConnection() {
var options = {
skipNegotiation: true,
transport: signalR.HttpTransportType.WebSockets
};
//this.JWTkey你的JWT令牌
options.accessTokenFactory = () => Cookies.get('gJwt');
//创建连接
this.connection = new signalR.HubConnectionBuilder()
.withUrl('https://localhost:7019/chatHub?access_token=' + Cookies.get('gJwt') + '&&userId=' + Cookies
.get('Id')) // 替换为你的SignalR服务URL,携带请求头发送
.withAutomaticReconnect() //断开自动连接
.build();
//注册
this.connection.on('ReceiveMessage', (res) => {
this.msgs.push(res)
this.$nextTick(() => {
this.$refs.contentDatas.scrollTo(0, this.$refs.element.offsetHeight)
});
});
//开始连接
this.connection.start();
},
2、发送消息
//发送消息按钮
sendMessage() {
const Hubss = Hubs()
this.connection.invoke('SendMsgToUser', Cookies.get('Id'), this.$route.params.item, this.chatfatext, Hubss.HubVaukes)
}
这个一对一聊天的主要功能就是这些了,这些代码只能完成简单的互相发送消息,其他的一些逻辑判断或者前端样式等等代码有点多,这里就不做介绍了。