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 - 使用位运算提取颜色,合并颜色
通常我们可以使用16进制的格式表示RGB颜色,比如0x2f88c0.通过位操作运算,能很方便的将其中的R,G,B颜色各部分分别提取出来.反之,也可以将R,G,B颜色值组合成一个完整的颜色. 1,提取颜 ...
- UVA 10574 - Counting Rectangles(枚举+计数)
10574 - Counting Rectangles 题目链接 题意:给定一些点,求可以成几个边平行于坐标轴的矩形 思路:先把点按x排序,再按y排序.然后用O(n^2)的方法找出每条垂直x轴的边,保 ...
- Windows Azure入门教学系列 (四):使用Blob Storage
本文将会介绍如何使用Blob Storage.Blob Storage可以看做是云端的文件系统.与桌面操作系统上不同,我们是通过REST API来进行对文件的操作.有关REST API的详细信息,请参 ...
- inode结构体成员详解
概述:inode译成中文就是索引节点,它用来存放档案及目录的基本信息,包含时间.档名.使用者及群组等.inode分为内存中的inode和文件系统中的inode,为了避免混淆,我们称前者为VFS ino ...
- iOS开发技巧 -- 复用代码片段
如果你是一位开发人员在开发过程中会发现有些代码无论是在同一个工程中还是在不同工程中使用率会很高,有经验的人会直接封装在一个类里,或者写成一个宏定义或者把这些代码收集起来,下次直接使用,或者放到xcod ...
- POJ2782:Bin Packing
Description A set of n<tex2html_verbatim_mark> 1-dimensional items have to be packed in iden ...
- 通过程序预览Office文档
我承认,标题是夸大了,就是为了吸引注意力.这里只有Word文档和Excel文档的预览代码. Word://此部分来源:http://princed.mblogger.cn/posts/11885.as ...
- DotNetBar.Bar控制Y顺序控制方向
DotNetBar.Bar控件Y方向上的顺序控制 老帅 控件DevComponents.DotNetBar.Bar是能够有多种用途的.能够作为容器,也能够作为工具条,不管做什么,在Y方向上 ...
- vc中改变对话框的背景色
---- 笔者曾在<软件报>2000年第5期中讨论过如何改变控件的颜色,但还有相当一部分的读者来信提问:一个基于对话框的MFC AppWizard应用程序中,如何改变对话框的背景颜色呢?对 ...
- Javascript selection的兼容性写法介绍
本文为大家讲解下Javascript selection的兼容性写法,感兴趣的朋友可以参考下 function getSelectedText() { //this function code is ...