参考文章:Asp.net webApi 通过WebSocket推送消息给客户端,搭建一个即是服务端又是客户端的服务_c# webapi websocket-CSDN博客
WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。WebSocket API也被W3C定为标准。
WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
废话不多说了,直接手把手教你如何通信。
下面是一个简单的用户连接和断开操作实例代码,有别的业务需求可以根据自己情况修改一下代码
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Net.WebSockets;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
using System.Web.WebSockets;
using WYC.DAL.DAL;
namespace WYC.WebAPI.Controllers
{
/// <summary>
/// 聊天控制器
/// </summary>
[RoutePrefix("Chat")]
[AllowAnonymous] //不授权
public class ChatController : ApiController
{
/// <summary>
/// 记录客户端(根据用户ID一一对应)
/// </summary>
private static Dictionary<string, WebSocket> _listSockets = new Dictionary<string, WebSocket>();
/// <summary>
/// 业务逻辑层
/// </summary>
private readonly ChatDAL _dal = new ChatDAL();
/// <summary>
/// 连接
/// </summary>
/// <param name="userId">用户Id</param>
/// <returns></returns>
[Route("GetConnect"), HttpGet]
public async Task<HttpResponseMessage> GetConnect(string userId)
{
await _dal.UpdateUserOnlineState(userId, 1); //修改用户在线状态
//指定执行不带参数的方法(注意:ProcessRequest(AspNetWebSocketContext context)这样属于不带参数,当前这个方法因为多了一个userId参数所以属于带参数方法)
//HttpContext.Current.AcceptWebSocketRequest(ProcessRequest);
//指定执行额外带参数的方法(注意:ProcessRequest(AspNetWebSocketContext context)这样属于不带参数,当前这个方法因为多了一个userId参数所以属于带参数方法)
Func<AspNetWebSocketContext, Task> paramnter = async (context) =>
{
await ProcessRequest(context, userId);
};
HttpContext.Current.AcceptWebSocketRequest(paramnter);
return Request.CreateResponse(HttpStatusCode.SwitchingProtocols);
}
/// <summary>
/// 客户端连接成功后执行方法(接收信息和发送信息)
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public async Task ProcessRequest(AspNetWebSocketContext context, string userId)
{
var socket = context.WebSocket;
_sockets.Add(socket); //存放客户端连接的WebSocket
_listSockets.Add(userId, socket); //存放客户端连接的WebSocket
while (true) //进入一个无限循环,一直监听当前连接成功的客户端操作行为
{
var buffer = new ArraySegment<byte>(new byte[1024]);
//对web socket进行异步接收数据(相当于是等待客户端发送消息)
var receivedResult = await socket.ReceiveAsync(buffer, System.Threading.CancellationToken.None);
if (receivedResult.MessageType == WebSocketMessageType.Close) //客户端断开连接
{
await socket.CloseAsync(WebSocketCloseStatus.Empty, string.Empty, System.Threading.CancellationToken.None);//如果client发起close请求,对client进行ack
_listSockets.Remove(userId); //删除
break; //结束循环
}
if (socket.State == WebSocketState.Open) //客户端连接状态
{
//获取客户端发送消息内容
string recvMsg = Encoding.UTF8.GetString(buffer.Array, 0, receivedResult.Count);
}
}
}
}
}
注意这里有一个小小的重点:
一、HttpContext.Current.AcceptWebSocketRequest(ProcessRequest);
二、 Func<AspNetWebSocketContext, Task> paramnter = async (context) =>
{
await ProcessRequest(context, userId);
};
HttpContext.Current.AcceptWebSocketRequest(paramnter);
以上两个代码区别就是一个指定带参数的方法,一个指定不带参数的方法,上面的一是指定不带参数的方法:相当于指定执行ProcessRequest(AspNetWebSocketContext context)这样的方法,上面的二是指定带参数的方法:相当于指定执行ProcessRequest(AspNetWebSocketContext context, string userId)这样多了一个参数的方法。正常咱们在处理业务和数据库对接的话都会设计到带参数所以这里是个重点。
以下代码是个人成功案例,可以根据自己业务需求进行调整代码
一、控制器
注意:这里不需要下载任何什么SDK包,就是引用一下系统自带的包如下:
using System.Net.WebSockets;
using System.Web.WebSockets;
1、首先创建一个ChatController继承自ApiController,如下代码:
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Net.WebSockets;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
using System.Web.WebSockets;
using WYC.DAL.DAL;
namespace WYC.WebAPI.Controllers
{
/// <summary>
/// 聊天控制器
/// </summary>
[RoutePrefix("Chat")]
[AllowAnonymous] //不授权
public class ChatController : ApiController
{
/// <summary>
/// 记录客户端(根据用户ID一一对应)
/// </summary>
private static Dictionary<string, WebSocket> _listSockets = new Dictionary<string, WebSocket>();
/// <summary>
/// 业务逻辑层
/// </summary>
private readonly ChatDAL _dal = new ChatDAL();
/// <summary>
/// 连接
/// </summary>
/// <param name="userId">用户Id</param>
/// <returns></returns>
[Route("GetConnect"), HttpGet]
public async Task<HttpResponseMessage> GetConnect(string userId)
{
await _dal.UpdateUserOnlineState(userId, 1); //修改用户在线状态
//指定执行不带参数的方法(注意:ProcessRequest(AspNetWebSocketContext context)这样属于不带参数,当前这个方法因为多了一个userId参数所以属于带参数方法)
//HttpContext.Current.AcceptWebSocketRequest(ProcessRequest);
//指定执行额外带参数的方法(注意:ProcessRequest(AspNetWebSocketContext context)这样属于不带参数,当前这个方法因为多了一个userId参数所以属于带参数方法)
Func<AspNetWebSocketContext, Task> paramnter = async (context) =>
{
await ProcessRequest(context, userId);
};
HttpContext.Current.AcceptWebSocketRequest(paramnter);
return Request.CreateResponse(HttpStatusCode.SwitchingProtocols);
}
/// <summary>
/// 客户端连接成功后执行方法(接收信息和发送信息)
/// </summary>
/// <param name="context">WebSocket上下文类</param>
/// <param name="userId">当前连接用户Id</param>
/// <returns></returns>
public async Task ProcessRequest(AspNetWebSocketContext context, string userId)
{
var socket = context.WebSocket;
_listSockets.Add(userId, socket); //存放客户端连接的WebSocket
while (true) //进入一个无限循环,一直监听当前连接成功的客户端操作行为
{
var buffer = new ArraySegment<byte>(new byte[1024]);
//对web socket进行异步接收数据(相当于是等待客户端发送消息)
var receivedResult = await socket.ReceiveAsync(buffer, System.Threading.CancellationToken.None);
if (receivedResult.MessageType == WebSocketMessageType.Close) //客户端断开连接
{
await socket.CloseAsync(WebSocketCloseStatus.Empty, string.Empty, System.Threading.CancellationToken.None);//如果client发起close请求,对client进行ack
_listSockets.Remove(userId); //删除
await _dal.UpdateUserOnlineState(userId, 0); //修改用户离线状态
break; //结束循环
}
if (socket.State == WebSocketState.Open) //客户端连接状态
{
//获取客户端发送消息内容
string recvMsg = Encoding.UTF8.GetString(buffer.Array, 0, receivedResult.Count);
//根据 前端|特殊字符 切割 获取接收信息用户Id
string receiptUserId = recvMsg.Substring(0, recvMsg.IndexOf("|"));
recvMsg = recvMsg.Substring(recvMsg.IndexOf("|") + 1); //获取完整聊天信息
await SendMessageToClientAsync(userId, receiptUserId, recvMsg); //一对一聊天
}
}
}
/// <summary>
/// 发送消息给指定用户(一对一聊天)
/// </summary>
/// <param name="sendUserId">发送用户Id</param>
/// <param name="receiptUserId">接受用户Id</param>
/// <param name="message">发送消息</param>
/// <returns></returns>
public async Task SendMessageToClientAsync(string sendUserId, string receiptUserId, string message)
{
//判断当前接受用户是否在词典里 并且 获取WebSocket
if (_listSockets.TryGetValue(receiptUserId, out WebSocket client)) //存在 则实时推送消息给用户
{
if (client.State == WebSocketState.Open) //打开状态
{
byte[] buffer = Encoding.UTF8.GetBytes(message);
ArraySegment<byte> segment = new ArraySegment<byte>(buffer);
//发送对应的client用户消息
await client.SendAsync(segment, WebSocketMessageType.Text, true, System.Threading.CancellationToken.None);
}
}
//将聊天信息存放数据库
await _dal.AddUserChatMessage(sendUserId, receiptUserId, message); //添加一对一聊天记录
}
/// <summary>
/// 群发消息给用户(群发消息)
/// </summary>
/// <param name="message">消息</param>
/// <returns></returns>
public async Task SendMessageToAllAsync(string message)
{
//这里因为WebSocket发送数据需要时字节格式 所以将字符串转换成字节,实际前端接收到的消息是字符串
byte[] buffer = Encoding.UTF8.GetBytes(message);
ArraySegment<byte> segment = new ArraySegment<byte>(buffer);
//遍历所有在线用户并发送数据
foreach (var client in _listSockets.Values)
{
if (client.State == WebSocketState.Open) //打开状态
{
//发送对应的client用户消息
await client.SendAsync(segment, WebSocketMessageType.Text, true, System.Threading.CancellationToken.None);
}
}
}
}
}
二、前端代码
需要引用jquery.js包如下代码:
<script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-1.11.3.min.js"></script>
var socket;
//Socket连接
function Connection() {
try {
//192.168.1.7:1112把这个IP和端口替换成自己WebAPI项目IP和端口 其实这里跟Http协议一样的请求道理,无非就是把http://替换成了ws://
var host = "ws://192.168.1.7:1112/Chat/GetConnect?userId=" + userId;
socket = new WebSocket(host);
/*连接*/
socket.onopen = function (msg) {
console.log("连接成功");
};
ReceptionMsg(); //接收消息
} catch (e) {
alert(e);
}
}
//Socket发送消息
function SocketSendMsg(msg) {
if (socket.readyState == WebSocket.OPEN) {
//ltHyUserId:接收消息的用户Id msg:消息 这里使用特殊字符|隔开,后端需要加工处理
socket.send(ltHyUserId + "|" + msg);
} else {
alert('连接已关闭');
}
}
//Socket关闭连接
function SocketClose() {
socket.close(); //关闭
}
//Socket接收服务端消息
function ReceptionMsg() {
/*接收消息*/
socket.onmessage = function (evt) {
console.log(evt.data); //接收到服务端发送过来的消息
}
}
注意:
需要将IIS安装WebSocket协议,不管是服务器还是本机都需要,如下步骤:
1、打开控制面板
2、点击程序
3、点击启用和关闭Windows功能
4、勾选WebSocket协议
然后确认重启电脑就OK了。