基于webapi的websocket聊天室(二)

上一篇 - 基于webapi的websocket聊天室(一)


消息超传缓冲区的问题

在上一篇中我们定义了一个聊天室WebSocketChatRoom。但是每个游客只分配了400个字节的发言缓冲区,大概100字。
如果需要发送更多内容呢?难道直接增大缓冲区?
这是一个办法。但还有其他办法。

多次接受消息

可以多次调用ReceiveAsync来接受消息。每次接收消息放在不同数组中,最后合并。

//WebSocketChatRoom.cs

/// <summary>
/// 多次接受消息
/// </summary>
/// <param name="client"></param>
/// <returns></returns>
public async Task<(List<byte> bytes, WebSocketMessageType MessageType)> GetBytes(WebSocket client)
{
    IList<ArraySegment<byte>> byteSegments = new List<ArraySegment<byte>>();
    WebSocketReceiveResult result;
    do
    {
        ArraySegment<byte> buffer = new ArraySegment<byte>(new byte[400]);
        byteSegments.Add(buffer);
        result = await client.ReceiveAsync(buffer, CancellationToken.None);
    } while (result.EndOfMessage==false);
    // 将所有字节连接起来
    List<byte> allBytes = new List<byte>();
    for (int i = 0; i < byteSegments.Count; i++)
    {
        var segment = byteSegments[i];
        if (i== byteSegments.Count-1)
        {
            allBytes.AddRange(segment.Take(result.Count));
        }
        else
        {
            allBytes.AddRange(segment);
        }
    }
    return (allBytes,result.MessageType);
}

然后调整消息循环

//WebSocketChatRoom.cs

while(!client.CloseStatus.HasValue)
{
    //使用新的方法
    var bytesResult = await GetBytes(client);
    //广播游客发言
    if (bytesResult.MessageType == WebSocketMessageType.Text)
    {
        CascadeMeaasge(visitor, $"{visitor.Name}: " + UTF8Encoding.UTF8.GetString(bytesResult.bytes.ToArray()));
    }
}
  • 缺点
    由于每次接受消息都会创建新的对象来接受消息,会频繁GC。

增加默认缓存区

简单的解决这个问题,就是每个连接分配一个固定大小的默认缓存区,当这个缓存区不够的时候才临时创建新的缓存区。
改造起来也简单。
就是多增加一个参数。然后再while中第一次使用defaultBuffer,之后再创建新的buffer。

//WebSocketChatRoom.cs

/// <summary>
/// 多次接受消息
/// </summary>
/// <param name="client"></param>
/// <param name="defaultBuffer"></param>
/// <returns></returns>
public async Task<(ArraySegment<byte> bytes, WebSocketMessageType MessageType)> GetBytes(WebSocket client, byte[] defaultBuffer)
{
    IList<ArraySegment<byte>> byteSegments = new List<ArraySegment<byte>>();
    WebSocketReceiveResult result;
    int bufferSize = 400;
    do
    {
        if (byteSegments.Count==0)
        {
            byteSegments.Add(defaultBuffer);
        }
        else
        {
            ArraySegment<byte> buffer = new ArraySegment<byte>(new byte[bufferSize]);
            byteSegments.Add(buffer);
        }
        result = await client.ReceiveAsync(byteSegments.Last(), CancellationToken.None);
    } while (result.EndOfMessage==false);
    // 将所有字节连接起来
    if (byteSegments.Count==1)
    {
        //ArraySegment是结构体,值类型,不会导致GC
        return (new ArraySegment<byte>(defaultBuffer,0,result.Count), result.MessageType);
    }
    else
    {
        List<byte> allBytes = new List<byte>();
        for (int i = 0; i < byteSegments.Count; i++)
        {
            var segment = byteSegments[i];
            if (i == byteSegments.Count - 1)
            {
                allBytes.AddRange(segment.Take(result.Count));
            }
            else
            {
                allBytes.AddRange(segment);
            }
        }
        var resultSegment = new ArraySegment<byte>(allBytes.ToArray());
        return (resultSegment, result.MessageType);
    }
}

然后调用这个方法的时候把默认缓冲区传进去即可

//WebSocketChatRoom.cs

//消息缓冲区。每个连接分配400字节,100个汉字的内存
var defaultBuffer = new byte[400];
//读数据
//消息循环
while (!client.CloseStatus.HasValue)
{
    var bytesResult = await GetBytes(client, defaultBuffer);
    //广播游客发言
    if (bytesResult.MessageType == WebSocketMessageType.Text)
    {
        CascadeMeaasge(visitor, $"{visitor.Name}: " + UTF8Encoding.UTF8.GetString(bytesResult.bytes.Array,0,bytesResult.bytes.Count));
    }
}
posted @ 2024-05-12 10:45  ggtc  阅读(74)  评论(0编辑  收藏  举报
//右下角目录