C# 实现WebSocket服务端实例

C# 实现WebSocket服务端实例WebSocket协议是一种双向通信协议,它建立在TCP之上,同http一样通过TCP来传输数据,但是它和http最大的不同有两点:1.WebSocket是一种双向通信协议,在建立连接后,WebSocket服务器和Browser/UA都能主动的向对方发送或接收数据,就像Socket一样,不同的是WebSocket是一种建立在Web基础上的一种简单模拟Socket的协议;2.WebSocket

WebSocket

WebSocket协议是一种双向通信协议,它建立在TCP之上,同http一样通过TCP来传输数据,但是它和http最大的不同有两点:

  1. WebSocket是一种双向通信协议,在建立连接后,WebSocket服务器和Browser/UA都能主动的向对方发送或接收数据,就像Socket一样,不同的是WebSocket是一种建立在Web基础上的一种简单模拟Socket的协议;
  2. WebSocket需要通过握手连接,类似于TCP它也需要客户端和服务器端进行握手连接,连接成功后才能相互通信。

当Web应用程序调用new WebSocket(url)接口时,Browser就开始了与地址为url的WebServer建立握手连接的过程。

客户端向服务器发送请求:

GET /chat HTTP/1.1  
Host: server.example.com  
Upgrade: websocket  
Connection: Upgrade  
Sec-WebSocket-Key:dGhlIHNhbXBsZSBub25jZQ==  
Origin: http://example.com  
Sec-WebSocket-Protocol: chat,superchat  
Sec-WebSocket-Version: 13  

服务器端返回内容:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: xsOSgr30aKL2GNZKNHKmeT1qYjA=

请求中的***【Sec-WebSocket-Key】是随机发送的。而服务器返回的【Sec-WebSocket-Accept】***是将【Sec-WebSocket-Key】加上一个固定字符串【258EAFA5-E914-47DA-95CA-C5AB0DC85B11】,并使用SHA-1加密后,再进行BASE-64编码生成的。

服务端简单实例

新建一个Web MVC项目:

.net4.5中实现了对websocket的支持,所以直接在一个项目中新建一个一般处理程序 Handler1

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.WebSockets;


namespace WebApplicationWebsocketHandler
{ 
   
    /// <summary>
    /// 离线消息
    /// </summary>
    public class MessageInfo
    { 
   
        public MessageInfo(DateTime _MsgTime, ArraySegment<byte> _MsgContent)
        { 
   
            MsgTime = _MsgTime;
            MsgContent = _MsgContent;
        }
        public DateTime MsgTime { 
    get; set; }
        public ArraySegment<byte> MsgContent { 
    get; set; }
    }




    /// <summary>
    /// Handler1 的摘要说明
    /// </summary>
    public class Handler1 : IHttpHandler
    { 
   
        private static Dictionary<string, WebSocket> CONNECT_POOL = new Dictionary<string, WebSocket>();//用户连接池
        private static Dictionary<string, List<MessageInfo>> MESSAGE_POOL = new Dictionary<string, List<MessageInfo>>();//离线消息池
        public void ProcessRequest(HttpContext context)
        { 
   
            //context.Response.ContentType = "text/plain";
            //context.Response.Write("Hello World");
            if (context.IsWebSocketRequest)
            { 
   
                context.AcceptWebSocketRequest(ProcessChat);
            }
        }

        private async Task ProcessChat(AspNetWebSocketContext context)
        { 
   
            WebSocket socket = context.WebSocket;
            string user = context.QueryString["user"].ToString();

            try
            { 
   
                #region 用户添加连接池
                //第一次open时,添加到连接池中
                if (!CONNECT_POOL.ContainsKey(user))
                    CONNECT_POOL.Add(user, socket);//不存在,添加
                else
                    if (socket != CONNECT_POOL[user])//当前对象不一致,更新
                        CONNECT_POOL[user] = socket;
                #endregion

                #region 离线消息处理
                if (MESSAGE_POOL.ContainsKey(user))
                { 
   
                    List<MessageInfo> msgs = MESSAGE_POOL[user];
                    foreach (MessageInfo item in msgs)
                    { 
   
                        await socket.SendAsync(new ArraySegment<byte>(Encoding.UTF8.GetBytes(item.MsgTime + ":" + item.MsgContent)), WebSocketMessageType.Text, true, CancellationToken.None);
                    }
                    MESSAGE_POOL.Remove(user);//移除离线消息
                }
                #endregion

                string descUser = string.Empty;//目的用户
                while (true)
                { 
   
                    if (socket.State == WebSocketState.Open)
                    { 
   
                        ArraySegment<byte> buffer = new ArraySegment<byte>(new byte[2048]);
                        WebSocketReceiveResult result = await socket.ReceiveAsync(buffer, CancellationToken.None);

                        #region 消息处理(字符截取、消息转发)
                        try
                        { 
   
                            #region 关闭Socket处理,删除连接池
                            if (socket.State != WebSocketState.Open)//连接关闭
                            { 
   
                                if (CONNECT_POOL.ContainsKey(user)) CONNECT_POOL.Remove(user);//删除连接池
                                break;
                            }
                            #endregion

                            string userMsg = Encoding.UTF8.GetString(buffer.Array, 0, result.Count);//发送过来的消息
                            string[] msgList = userMsg.Split('|');
                            if (msgList.Length == 2)
                            { 
   
                                if (msgList[0].Trim().Length > 0)
                                    descUser = msgList[0].Trim();//记录消息目的用户
                                buffer = new ArraySegment<byte>(Encoding.UTF8.GetBytes(msgList[1]));

                                if (CONNECT_POOL.ContainsKey(descUser))//判断客户端是否在线
                                { 
   
                                    WebSocket destSocket = CONNECT_POOL[descUser];//目的客户端
                                    if (destSocket != null && destSocket.State == WebSocketState.Open)
                                        await destSocket.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None);
                                }
                                else
                                { 
   
                                    Task.Run(() =>
                                    { 
   
                                        if (!MESSAGE_POOL.ContainsKey(descUser))//将用户添加至离线消息池中
                                            MESSAGE_POOL.Add(descUser, new List<MessageInfo>());
                                        MESSAGE_POOL[descUser].Add(new MessageInfo(DateTime.Now, buffer));//添加离线消息
                                    });
                                }

                            }
                            else
                            { 
   
                                buffer = new ArraySegment<byte>(Encoding.UTF8.GetBytes(userMsg));

                                foreach (KeyValuePair<string, WebSocket> item in CONNECT_POOL)
                                { 
   
                                    await item.Value.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None);
                                }

                            }
                            
                        }
                        catch (Exception exs)
                        { 
   
                            //消息转发异常处理,本次消息忽略 继续监听接下来的消息
                        }
                        #endregion
                    }
                    else
                    { 
   
                        break;
                    }
                }//while end
            }
            catch (Exception ex)
            { 
   
                //整体异常处理
                if (CONNECT_POOL.ContainsKey(user)) CONNECT_POOL.Remove(user);
            }
        }


        public bool IsReusable
        { 
   
            get
            { 
   
                return false;
            }
        }
    }
}

客户端测试代码如下:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0"/>
    <title></title>
    <script src="http://code.jquery.com/jquery-1.4.1.min.js"></script>
    <script> var ws; $().ready(function () { 
      $('#conn').click(function () { 
      //ws = new WebSocket('ws://' + window.location.hostname + ':' + window.location.port + '/Handler1.ashx?user=' + $("#user").val()); ws = new WebSocket('ws://127.0.0.1:80/Handler1.ashx?user=' + $("#user").val()); //var host = 'ws://192.168.85.128:8085/api/WSChat?user='+$("#user").val(); //var host = "ws://192.168.85.128:8085/api/WSChat"; //webSocket = new WebSocket(host); $('#msg').append('<p>正在连接</p>'); ws.onopen = function () { 
      $('#msg').append('<p>已经连接</p>'); } ws.onmessage = function (evt) { 
      $('#msg').append('<p>' + evt.data + '</p>'); } ws.onerror = function (evt) { 
      $('#msg').append('<p>' + JSON.stringify(evt) + '</p>'); } ws.onclose = function () { 
      $('#msg').append('<p>已经关闭</p>'); } }); $('#close').click(function () { 
      ws.close(); }); $('#send').click(function () { 
      if (ws.readyState == WebSocket.OPEN) { 
      ws.send($("#to").val() + "|" + $('#content').val()); } else { 
      $('#tips').text('连接已经关闭'); } }); }); </script>
</head>
<body>
    <div>
        <input id="user" type="text" />
        <input id="conn" type="button" value="连接" />
        <input id="close" type="button" value="关闭"/><br />
        <span id="tips"></span>
        <input id="content" type="text" />
        <input id="send" type="button" value="发送"/><br />
        <input id="to" type="text" />目的用户
        <div id="msg">
        </div>
    </div>
</body>
</html>

测试需要iis配置支持WebSocket协议,win7暂不支持。。。

今天的文章C# 实现WebSocket服务端实例分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/13307.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注