WebSocket聊天室demo
根据Socket异步聊天室修改成WebSocket聊天室
WebSocket特别的地方是 握手和消息内容的编码、解码(添加了ServerHelper协助处理)
ServerHelper:
- using System;
- using System.Collections;
- using System.Text;
- using System.Security.Cryptography;
- namespace SocketDemo
- {
- // Server助手 负责:1 握手 2 请求转换 3 响应转换
- class ServerHelper
- {
- /// <summary>
- /// 输出连接头信息
- /// </summary>
- public static string ResponseHeader(string requestHeader)
- {
- Hashtable table = new Hashtable();
- // 拆分成键值对,保存到哈希表
- string[] rows = requestHeader.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
- foreach (string row in rows)
- {
- int splitIndex = row.IndexOf(':');
- if (splitIndex > )
- {
- table.Add(row.Substring(, splitIndex).Trim(), row.Substring(splitIndex + ).Trim());
- }
- }
- StringBuilder header = new StringBuilder();
- header.Append("HTTP/1.1 101 Web Socket Protocol Handshake\r\n");
- header.AppendFormat("Upgrade: {0}\r\n", table.ContainsKey("Upgrade") ? table["Upgrade"].ToString() : string.Empty);
- header.AppendFormat("Connection: {0}\r\n", table.ContainsKey("Connection") ? table["Connection"].ToString() : string.Empty);
- header.AppendFormat("WebSocket-Origin: {0}\r\n", table.ContainsKey("Sec-WebSocket-Origin") ? table["Sec-WebSocket-Origin"].ToString() : string.Empty);
- header.AppendFormat("WebSocket-Location: {0}\r\n", table.ContainsKey("Host") ? table["Host"].ToString() : string.Empty);
- string key = table.ContainsKey("Sec-WebSocket-Key") ? table["Sec-WebSocket-Key"].ToString() : string.Empty;
- string magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
- header.AppendFormat("Sec-WebSocket-Accept: {0}\r\n", Convert.ToBase64String(SHA1.Create().ComputeHash(Encoding.ASCII.GetBytes(key + magic))));
- header.Append("\r\n");
- return header.ToString();
- }
- /// <summary>
- /// 解码请求内容
- /// </summary>
- public static string DecodeMsg(Byte[] buffer, int len)
- {
- if (buffer[] != 0x81
- || (buffer[] & 0x80) != 0x80
- || (buffer[] & 0x80) != 0x80)
- {
- return null;
- }
- Byte[] mask = new Byte[];
- int beginIndex = ;
- int payload_len = buffer[] & 0x7F;
- if (payload_len == 0x7E)
- {
- Array.Copy(buffer, , mask, , );
- payload_len = payload_len & 0x00000000;
- payload_len = payload_len | buffer[];
- payload_len = (payload_len << ) | buffer[];
- beginIndex = ;
- }
- else if (payload_len != 0x7F)
- {
- Array.Copy(buffer, , mask, , );
- beginIndex = ;
- }
- for (int i = ; i < payload_len; i++)
- {
- buffer[i + beginIndex] = (byte)(buffer[i + beginIndex] ^ mask[i % ]);
- }
- return Encoding.UTF8.GetString(buffer, beginIndex, payload_len);
- }
- /// <summary>
- /// 编码响应内容
- /// </summary>
- public static byte[] EncodeMsg(string content)
- {
- byte[] bts = null;
- byte[] temp = Encoding.UTF8.GetBytes(content);
- if (temp.Length < )
- {
- bts = new byte[temp.Length + ];
- bts[] = 0x81;
- bts[] = (byte)temp.Length;
- Array.Copy(temp, , bts, , temp.Length);
- }
- else if (temp.Length < 0xFFFF)
- {
- bts = new byte[temp.Length + ];
- bts[] = 0x81;
- bts[] = ;
- bts[] = (byte)(temp.Length & 0xFF);
- bts[] = (byte)(temp.Length >> & 0xFF);
- Array.Copy(temp, , bts, , temp.Length);
- }
- else
- {
- byte[] st = System.Text.Encoding.UTF8.GetBytes(string.Format("暂不处理超长内容").ToCharArray());
- }
- return bts;
- }
- }
- }
Server:
- using System;
- using System.Collections.Generic;
- using System.Text;
- using System.Net;
- using System.Net.Sockets;
- namespace SocketDemo
- {
- class ClientInfo
- {
- public Socket Socket { get; set; }
- public bool IsOpen { get; set; }
- public string Address { get; set; }
- }
- // 管理Client
- class ClientManager
- {
- static List<ClientInfo> clientList = new List<ClientInfo>();
- public static void Add(ClientInfo info)
- {
- if (!IsExist(info.Address))
- {
- clientList.Add(info);
- }
- }
- public static bool IsExist(string address)
- {
- return clientList.Exists(item => string.Compare(address, item.Address, true) == );
- }
- public static bool IsExist(string address, bool isOpen)
- {
- return clientList.Exists(item => string.Compare(address, item.Address, true) == && item.IsOpen == isOpen);
- }
- public static void Open(string address)
- {
- clientList.ForEach(item =>
- {
- if (string.Compare(address, item.Address, true) == )
- {
- item.IsOpen = true;
- }
- });
- }
- public static void Close(string address = null)
- {
- clientList.ForEach(item =>
- {
- if (address == null || string.Compare(address, item.Address, true) == )
- {
- item.IsOpen = false;
- item.Socket.Shutdown(SocketShutdown.Both);
- }
- });
- }
- // 发送消息到ClientList
- public static void SendMsgToClientList(string msg, string address = null)
- {
- clientList.ForEach(item =>
- {
- if (item.IsOpen && (address == null || item.Address != address))
- {
- SendMsgToClient(item.Socket, msg);
- }
- });
- }
- public static void SendMsgToClient(Socket client, string msg)
- {
- byte[] bt = ServerHelper.EncodeMsg(msg);
- client.BeginSend(bt, , bt.Length, SocketFlags.None, new AsyncCallback(SendTarget), client);
- }
- private static void SendTarget(IAsyncResult res)
- {
- //Socket client = (Socket)res.AsyncState;
- //int size = client.EndSend(res);
- }
- }
- // 接收消息
- class ReceiveHelper
- {
- public byte[] Bytes { get; set; }
- public void ReceiveTarget(IAsyncResult res)
- {
- Socket client = (Socket)res.AsyncState;
- int size = client.EndReceive(res);
- if (size > )
- {
- string address = client.RemoteEndPoint.ToString(); // 获取Client的IP和端口
- string stringdata = null;
- if (ClientManager.IsExist(address, false)) // 握手
- {
- stringdata = Encoding.UTF8.GetString(Bytes, , size);
- ClientManager.SendMsgToClient(client, ServerHelper.ResponseHeader(stringdata));
- ClientManager.Open(address);
- }
- else
- {
- stringdata = ServerHelper.DecodeMsg(Bytes, size);
- }
- if (stringdata.IndexOf("exit") > -)
- {
- ClientManager.SendMsgToClientList(address + "已从服务器断开", address);
- ClientManager.Close(address);
- Console.WriteLine(address + "已从服务器断开");
- Console.WriteLine(address + " " + DateTimeOffset.Now.ToString("G"));
- return;
- }
- else
- {
- Console.WriteLine(stringdata);
- Console.WriteLine(address + " " + DateTimeOffset.Now.ToString("G"));
- ClientManager.SendMsgToClientList(stringdata, address);
- }
- }
- // 继续等待
- client.BeginReceive(Bytes, , Bytes.Length, SocketFlags.None, new AsyncCallback(ReceiveTarget), client);
- }
- }
- // 监听请求
- class AcceptHelper
- {
- public byte[] Bytes { get; set; }
- public void AcceptTarget(IAsyncResult res)
- {
- Socket server = (Socket)res.AsyncState;
- Socket client = server.EndAccept(res);
- string address = client.RemoteEndPoint.ToString();
- ClientManager.Add(new ClientInfo() { Socket = client, Address = address, IsOpen = false });
- ReceiveHelper rs = new ReceiveHelper() { Bytes = this.Bytes };
- IAsyncResult recres = client.BeginReceive(rs.Bytes, , rs.Bytes.Length, SocketFlags.None, new AsyncCallback(rs.ReceiveTarget), client);
- // 继续监听
- server.BeginAccept(new AsyncCallback(AcceptTarget), server);
- }
- }
- class Program
- {
- static void Main(string[] args)
- {
- Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
- server.Bind(new IPEndPoint(IPAddress.Parse("127.0.0.1"), )); // 绑定IP+端口
- server.Listen(); // 开始监听
- Console.WriteLine("等待连接...");
- AcceptHelper ca = new AcceptHelper() { Bytes = new byte[] };
- IAsyncResult res = server.BeginAccept(new AsyncCallback(ca.AcceptTarget), server);
- string str = string.Empty;
- while (str != "exit")
- {
- str = Console.ReadLine();
- Console.WriteLine("ME: " + DateTimeOffset.Now.ToString("G"));
- ClientManager.SendMsgToClientList(str);
- }
- ClientManager.Close();
- server.Close();
- }
- }
- }
Client:
- <!DOCTYPE html>
- <script>
- var mySocket;
- function Star() {
- mySocket = new WebSocket("ws://127.0.0.1:200", "my-custom-protocol");
- mySocket.onopen = function Open() {
- Show("连接打开");
- };
- mySocket.onmessage = function (evt) {
- Show(evt.data);
- };
- mySocket.onclose = function Close() {
- Show("连接关闭");
- mySocket.close();
- };
- }
- function Send() {
- var content = document.getElementById("content").value;
- Show(content);
- mySocket.send(content);
- }
- function Show(msg) {
- var roomContent = document.getElementById("roomContent");
- roomContent.innerHTML = msg + "<br/>" + roomContent.innerHTML;
- }
- </script>
- <html>
- <head>
- <title></title>
- </head>
- <body>
- <div id="roomContent" style="width: 500px; height: 200px; overflow: hidden; border: 2px solid #686868;
- margin-bottom: 10px; padding: 10px 0px 0px 10px;">
- </div>
- <div>
- <textarea id="content" cols="" rows="" style="padding: 10px 0px 0px 10px;"></textarea>
- </div>
- <input type="button" value="Connection" onclick="Star()" />
- <input type="button" value="Send" onclick="Send()" />
- </body>
- </html>
总结:
以上demo仅仅是为了大家对WebSocket有一个了解、认识, 如果想要在项目中使用的话,还是推荐优先考虑一些现有的引擎,毕竟一些浏览器兼容性问题和服务端通信上的问题解决起来还是比较耗时的。
WebSocket聊天室demo的更多相关文章
- 用Java构建一个简单的WebSocket聊天室
前言 首先对于一个简单的聊天室,大家应该都有一定的概念了,这里我们省略用户模块的讲解,而是单纯的先说说聊天室的几个功能:自我对话.好友交流.群聊.离线消息等. 今天我们要做的demo就能帮我们做到这一 ...
- Netty入门(一)之webSocket聊天室
一:简介 Netty 是一个提供 asynchronous event-driven (异步事件驱动)的网络应用框架,是一个用以快速开发高性能.高可靠性协议的服务器和客户端. 换句话说,Netty 是 ...
- 使用.NET Core和Vue搭建WebSocket聊天室
博客地址是:https://qinyuanpei.github.io. WebSocket是HTML5标准中的一部分,从Socket这个字眼我们就可以知道,这是一种网络通信协议.WebSocket是 ...
- websocket聊天室
目录 websocket方法总结 群聊功能 基于websocket聊天室(版本一) websocket方法总结 # 后端 3个 class ChatConsumer(WebsocketConsumer ...
- php +html5 websocket 聊天室
针对内容比较长出错,修改后的解码函数 和 加码函数 原文请看上一篇 http://yixun.yxsss.com/yw3104.html function uncode($str,$key){ $ma ...
- koa2+webSocket 聊天室
做了一个简单的的聊天室,用来看看 koa和 websocket的使用还是挺好的,已经放到gitHub. https://github.com/zhaowanhua/koa2WebSocket
- 实现一个简单的WebSocket聊天室
WebSocket 简介 WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议. WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主 ...
- tornado websocket聊天室
1.app.py #!/usr/bin/env python # -*- coding:utf-8 -*- import uuid import json import tornado.ioloop ...
- 基于springboot的websocket聊天室
WebSocket入门 1.概述 1.1 Http #http简介 HTTP是一个应用层协议,无状态的,端口号为80.主要的版本有1.0/1.1/2.0. #http1.0/1.1/2.0 1.HTT ...
随机推荐
- Swift - 同步请求获取网络数据
使用NSURLConnection可以实现http通信.它提供了异步请求和同步请求两种通信方式. 注意:同步请求数据会造成主线程阻塞,必须请求结束后用户才能做其他的操作,所有通常在请求大数据或者网络不 ...
- Codeforces Round #112 (Div. 2)---A. Supercentral Point
Supercentral Point time limit per test 2 seconds memory limit per test 256 megabytes input standard ...
- swift 有些语法还是不支持。
<pre name="code" class="html">"func hasAnyMatches(list: Int[], condit ...
- Windows Azure入门教学系列 (六):使用Table Storage
本文是Windows Azure入门教学的第六篇文章. 本文将会介绍如何使用Table Storage.Table Storage提供给我们一个云端的表格结构.我们可以把他想象为XML文件或者是一个轻 ...
- android布局margin和padding差异!
事实上从使用的时候就能够差别开来. android:padding android:layout_margin padding是在本控件级别的,而margin是在layout级别的. 最好拿有背景的控 ...
- Swift - 多层无缝循环滚动背景(SpriteKit游戏开发)
在游戏开发中,比如跑酷游戏.我们需要实现背景的无限循环滚动,来营造运动的效果.除了单层的背景滚动,还有视差滚动. 视差滚动是指让多层背景以不同的速度移动,形成立体的效果,从而带来非常出色的视觉体验. ...
- 阿斯钢iojeg9uhw8uhy平
http://www.huihui.cn/share/8424421 http://www.huihui.cn/share/8424375 http://www.huihui.cn/share/842 ...
- mybatis 的简单使用
须要用到的包:(这里仅仅是当中一个版本号.其它的百度) mysql-connector-java-5.1.6-bin mybatis-3.2.2 先看项目文件夹: 配置文件mybatisconfig. ...
- ASP.NET 常用内置对象详解-----Response
利用提供的内置对象,可以实现页面之间的数据传递及实现一些特定的功能,如:缓冲输出,页面重定向等等. Response :响应,反应 Request:请求 Server:服务器 Application: ...
- visio ppt axure AI svg powerdesign xmind
visio ppt axure AI svg powerdesign xmind