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 ...
随机推荐
- Oracle 创建索引的基本规则总结
1. 选择索引字段的原则: 在WHERE子句中最频繁使用的字段 联接语句中的联接字段 选择高选择性的字段(如果很少的字段拥有相同值,即有很多独特值,则选择性很好) Oracle在UNIQUE和主键字 ...
- 数据层交换和高性能并发处理(开源ETL大数据治理工具--KETTLE使用及二次开发 )
ETL是什么?为什么要使用ETL?KETTLE是什么?为什么要学KETTLE? ETL是数据的抽取清洗转换加载的过程,是数据进入数据仓库进行大数据分析的载入过程,目前流行的数据进入仓库的 ...
- UVA 10718 Bit Mask 贪心+位运算
题意:给出一个数N,下限L上限U,在[L,U]里面找一个整数,使得N|M最大,且让M最小. 很明显用贪心,用位运算搞了半天,样例过了后还是WA,没考虑清楚... 然后网上翻到了一个人家位运算一句话解决 ...
- Android中Activity之间访问互传参数
public class MainActivity extends Activity { private static final int OTHER = 1; @Override protected ...
- Python中列表的常用操作
只整理重要常用的操作: append():尾部追加元素,参数只能为一个. extend():用列表扩展列表,参数为列表. insert():在指定位置插入元素,第一个参数为插入位置,第二个为参数为插入 ...
- JS - 图片放大器
下载地址:http://www.lanrentuku.com/js/tupian-1170.html
- haproxy redirect location和redirect prefix
<pre name="code" class="html">redirect location <loc> [code <code ...
- 5款帮助简化的HTML5 Audio开发的Javascript类库
HTML5的audio标签提供了我们方便控制声音的功能,可是使用原生的HTML5来开发声音或者音乐相关的项目仍旧很的麻烦.在今天这篇文章中,我们将介绍5款帮助你简化开发的javascript audi ...
- c++中编译器的作用
编译器的部分工作是寻找程序代码中的错误.编译器不能查出程序的意义是否正确. 但它能够查出程序形式上的错误.以下是编译器能查出的最普遍的一些错误: (1)语法错误.程序猿犯了c++语言中的语法错误. ( ...
- [读书笔记]设计原本[The Design of Design]
第1章 设计之命题 1.设计首先诞生于脑海里,再慢慢逐步成形(实现) 2.好的设计具有概念完整性:统一.经济.清晰.优雅.利落.漂亮... 第2章 工程师怎样进行设计思维——理性模型 1.有序模型的有 ...