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 ...
随机推荐
- JSP简单练习-使用JDOM创建xml文件
注意:在编写代码前,请确保该Web文件夹下的"WEB-INF/lib"下包括jdom.jar包! <%@ page language="java" con ...
- 浅谈Jquery的使用下篇
上一篇我们已经探讨了Jquery的有关的内容,简单的选择器以及一些Jquery的属性事件和方法等内容,让我们简单的学到了Jquery的比较基础的内容,下面我们就来探讨Jquery的一些其它的内容,比如 ...
- element.style覆盖了我的样式!!
原文:element.style覆盖了我的样式!! 有时候在写css时,显示效果会出现非常诡异的效果 不知道有没有遇到这种 css: #logo{ border: solid 1px blue; } ...
- boost::asio async_write也不能保证一次发完所有数据 二
只有看boost源码才能弄明白发生了什么.首先我是将vector里面写入了数据,然后用boost::asio::buffer将vector构造成了mutable_buffer_1对象. 参考该文档的重 ...
- css 水平居中的办法
<div style="width: 100%; text-align: center; margin: auto;"> <div style="dis ...
- 【zigbee】开启及清除NV_RESTORE信息的方法
1.NV_RESTORE宏的作用 问:coo和终端都已经组网成功 1.这时将coo断电,又一次上电,组网后终端的短地址是否不变? 2.这时终端断电,又一次上电,组网后终端的短地址是否不变? 3.这时C ...
- Tomcat详细用法学习(三)
本篇接上一篇<Tomcat详细用法学习(二)>,主要讲解服务器所要求的web应用的组织结构. 上一篇说到了如何使用服务器将自己的web应用映射成虚拟目录,以便于在浏览器中可以对自己开发的w ...
- [置顶] location.href你真的会用了?
*.location.href 用法: top.location.href=”url” 在顶层页面打开url(跳出框架) self.location.href=”url” ...
- js之checkbox的代码全选/全不选,使用id获取元素,而不是name
每当有多个选项的时候,都会有一种想法是:全选,全不选,如果子选项有被选,父选项也得被选. 注意:这里是根据id来获取元素的,但是不能直接用getElementById,因为那只能返回一个,而不是集合. ...
- 【安卓】eclipse中不可错过的几个秘密、!
1.PackageExplorer显示文件层次的默认方式是平行列出全部包,事实上也可显示成多级,并且效果比navigator好多了. PackageExplorer视图中,"右上角箭头→pa ...