WebSocket学习总结

一 、websocket 已解决
     但是websocket延伸出来的网络编程还有好多知识点没有清理。主要的流程和实现方式已经大概了解清楚,下面从学习的进度思路来一点点复习。
 
     网络请求第一步,先给目标服务器和端口号进行三次握手,握手成功之后才会发送http请求。
               
  其中有一个概念叫套接字,那么什么是套接字呢?
 
  套接字
  
    套接字,是支持TCP/IP的网络通信的基本操作单元,可以看做是不同主机之间的进程进行双向通信的端点,简单的说就是通信的两方的一种约定,用套接字中的相关函数来完成通信过程。
   非常非常简单的举例说明下:Socket=Ip address+ TCP/UDP + port。
 
    区分不同应用程序进程间的网络通信和连接,主要有3个参数:通信的目的IP地址、使用的传输层协议(TCP或UDP)和使用的端口号。Socket原意是 “插座”。通过将这3个参数结合起来,与一个“插座”Socket绑定,应用层就可以和传输层通过套接字接口,区分来自不  同应用程序进程或网络连接的通信,实现数据传输的并发服务。
 
     Socket可以看成在两个程序进行通讯连接中的一个端点,是连接应用程序和网络驱动程序的桥梁,Socket在应用程序中创建,通过绑定与网络驱动建立关系。此后,应用程序送给Socket的数据,由Socket交给网络驱动程序向网络上发送出去。计算机从网络上收到与该Socket绑定IP地址和端口号相关的数据后,由网络驱动程序交给Socket,应用程序便可从该Socket中提取接收到的数据,网络应用程序就是这样通过Socket进行数据的发送与接收的。
 
   对于http程序员来说,套接字api隐藏了tcp和ip的所有细节。套接字API最开始是为Unix系统开发的,但是现在几乎所有的操作系统和语言中都有其变体的存在
 
WebSocket
 
  websocket协议用于完全双工的双向通讯(双工的意思是服务端给客户端发送消息的同时,客户端也能给服务端发送消息 ps:对讲机不是完全双工的),但仅限于支持websocket的客户端。
  个人理解:websocket只是tcp协议上层的一个应用层协议,和http协议是同一级别。这种完全双工的双向通讯早就可以实现了,只是浏览器不可以。而服务器端不同的语言有不同的API实现。
                   h5的websocket还是要先通过http协议通知服务器进行协议的升级。
 
                  
                 这是一个典型的websocket握手的报文,熟悉http报文的能看出来多出来两个东西,
                 
                 这是告诉服务器把协议升级为ws
               
                第一个是私钥,第二个是用户定义的字符串,用来区分同url下,不同的服务所需要的协议,第三个是告诉服务器用Websocket Draft(协议版本)目前版本是13
                秘钥的加密原理是:把客户端上报的key拼上一段GUID “258EAFA5-E914-47DA-95CA-C5AB0DC85B11″,拿这个字符串做SHA-1 hash计算,然后再把得到的结果通过base64加密,最后在返回给客户端。
  二、编程实现
  websocket的端点可以用任意类型的处理程序或模块来创建。我在《C#高级编程》里面用ashx实现了一遍,是一个基于浏览器的简单的聊天室。
 
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <script src="Scripts/jquery-1.8.2.min.js"></script>
    <title></title>
    <script>
        $(document).ready(function () {
            var name = prompt('what is you name?:');
            if ("WebSocket" in window) {
                ws = new WebSocket("ws://localhost/ws.ashx?name=" + name);
            }
                //firefox的websocket对象是MozWebSocket,和别的浏览器不一样,不知道现在改了没有
            else if ("MozWebSocket" in window) {
                ws = new MozWebSocket("ws://localhost/ws.ashx?name=" + name);
            }
            //请求升级之后,调用open
            ws.OPEN = function () {
                $('#messages').prepend('Conneccted</br>');
                $('#cmdSend').click(function () {
                    ws.send($('#extMessage').val());
                    $('#textMessage').val('');
                });
            };
            //收到服务器数据,调用onmessage
            ws.onmessage = function (e) {
                $('#chatMessages').prepend(e.data + '<br/>');
            };
            $('#cmdLeave').click(function () {
                ws.close();
            });
            //连接关闭
            ws.onclose = function () {
                $('#chatMessages').prepend('Close<br/>');

            };
            //出现错误
            ws.onerror = function (e) {
                $('#chatMessage').prepend('Oops something went wrong<br/>');

            };
        });

    </script>
</head>
<body>
    <input id="txtMessage" />
    <input id="cmdSend" type="button" value="Send" />
    <input id="cmdLeave" type="button" value="Leave" />
    <br />
    <div id="chatMessage"></div>
</body>
</html>

服务器上的实现要稍微复杂一些

ProcessRequest的主体

 public void ProcessRequest(HttpContext context)
        {

            if (context.IsWebSocketRequest)
            {
                var chatuser = new ChatUser();
                chatuser.UserNmae = context.Request["name"];

                ChatApp.Add(chatuser);

                context.AcceptWebSocketRequest(chatuser.HandleWebSocket);
            }
        }

 

   chatuser对象,保存用户信息并且实现给用户发消息的方法。
  public class ChatUser
    {
        public string UserNmae { get; set; }

        public WebSocketContext _context { get; set; }
        //调用此函数时,把WebSocketContext传递给ChatUser的_context属性,发广播的时候很方便。
        public async Task HandleWebSocket(WebSocketContext wsContext)
        {
            _context = wsContext;
            const int maxMessageSize = 1024;
            byte[] receiveBuffer = new byte[maxMessageSize];
            WebSocket socket = _context.WebSocket;
            while (socket.State == WebSocketState.Open)
            {
                WebSocketReceiveResult receiveResult =
                    await socket.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), CancellationToken.None);
                if (receiveResult.MessageType == WebSocketMessageType.Close)
                {
                    await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
                }
                else if (receiveResult.MessageType == WebSocketMessageType.Binary)
                {
                    await
                        socket.CloseAsync(WebSocketCloseStatus.InvalidMessageType, "can not accept binary frame",
                            CancellationToken.None);
                }
                else
                {
                    var receivedString = Encoding.UTF8.GetString(receiveBuffer, 0, receiveBuffer.Count());
                    var edhostring = string.Concat(UserNmae, "said", receivedString);
                    ArraySegment<byte> outputBBuffer = new ArraySegment<byte>(Encoding.UTF8.GetBytes(edhostring));
                    ChatApp.BroadcastMessage(edhostring);
                }
            }
        }

        public async Task SendMessage(string msg)
        {
            if (_context != null && _context.WebSocket.State == WebSocketState.Open)
            {
                var outputBuffer = new ArraySegment<byte>(Encoding.UTF8.GetBytes(msg));
                await
                    _context.WebSocket.SendAsync(outputBuffer, WebSocketMessageType.Text, true, CancellationToken.None);
            }
        }
    }

用户表对象,主要用来广播消息

  public static class ChatApp
    {


        public static IList<ChatUser> list = new List<ChatUser>();

        public static void Add(ChatUser user)
        {
            list.Add(user);
        }
        //遍历用户列表发消息
        public static void BroadcastMessage(string str)
        {
            foreach (var user in list)
            {
                user.SendMessage(str);
            }
        }


    }

三、关于代码中动态类型,静态类,静态字段,静态构造函数,下次再写。

  
 
posted @ 2016-03-30 16:36  天街小雨&  阅读(544)  评论(0编辑  收藏  举报