C# Socket 实现WebSocket服务器端
-
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Net; namespace WebSocketServer
{
class Program
{
static void Main(string[] args)
{
WebSocket socket = new WebSocket();
socket.start(8064);
}
}
}using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net; namespace WebSocketServer
{
public class Session
{
private Socket _sockeclient;
private byte[] _buffer;
private string _ip;
private bool _isweb = false; public Socket SockeClient
{
set { _sockeclient = value; }
get { return _sockeclient; }
} public byte[] buffer
{
set { _buffer = value; }
get { return _buffer; }
} public string IP
{
set { _ip = value; }
get { return _ip; }
} public bool isWeb
{
set { _isweb = value; }
get { return _isweb; }
}
}
}using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Net.Sockets;
using System.Net;
using System.Text.RegularExpressions;
using System.Security.Cryptography; namespace WebSocketServer
{
public class WebSocket
{
private Dictionary<string, Session> SessionPool = new Dictionary<string, Session>();
private Dictionary<string, string> MsgPool = new Dictionary<string, string>(); #region 启动WebSocket服务
/// <summary>
/// 启动WebSocket服务
/// </summary>
public void start(int port)
{
Socket SockeServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
SockeServer.Bind(new IPEndPoint(IPAddress.Any, port));
SockeServer.Listen(20);
SockeServer.BeginAccept(new AsyncCallback(Accept), SockeServer);
Console.WriteLine("服务已启动");
Console.WriteLine("按任意键关闭服务");
Console.ReadLine();
}
#endregion #region 处理客户端连接请求
/// <summary>
/// 处理客户端连接请求
/// </summary>
/// <param name="result"></param>
private void Accept(IAsyncResult socket)
{
// 还原传入的原始套接字
Socket SockeServer = (Socket)socket.AsyncState;
// 在原始套接字上调用EndAccept方法,返回新的套接字
Socket SockeClient = SockeServer.EndAccept(socket);
byte[] buffer = new byte[4096];
try
{
//接收客户端的数据
SockeClient.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(Recieve), SockeClient);
//保存登录的客户端
Session session = new Session();
session.SockeClient = SockeClient;
session.IP = SockeClient.RemoteEndPoint.ToString();
session.buffer = buffer;
lock (SessionPool)
{
if (SessionPool.ContainsKey(session.IP))
{
this.SessionPool.Remove(session.IP);
}
this.SessionPool.Add(session.IP, session);
}
//准备接受下一个客户端
SockeServer.BeginAccept(new AsyncCallback(Accept), SockeServer);
Console.WriteLine(string.Format("Client {0} connected", SockeClient.RemoteEndPoint));
}
catch (Exception ex)
{
Console.WriteLine("Error : " + ex.ToString());
}
}
#endregion #region 处理接收的数据
/// <summary>
/// 处理接受的数据
/// </summary>
/// <param name="socket"></param>
private void Recieve(IAsyncResult socket)
{
Socket SockeClient = (Socket)socket.AsyncState;
string IP = SockeClient.RemoteEndPoint.ToString();
if (SockeClient == null || !SessionPool.ContainsKey(IP))
{
return;
}
try
{
int length = SockeClient.EndReceive(socket);
byte[] buffer = SessionPool[IP].buffer;
SockeClient.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(Recieve), SockeClient);
string msg = Encoding.UTF8.GetString(buffer, 0, length);
// websocket建立连接的时候,除了TCP连接的三次握手,websocket协议中客户端与服务器想建立连接需要一次额外的握手动作
if (msg.Contains("Sec-WebSocket-Key"))
{
SockeClient.Send(PackageHandShakeData(buffer, length));
SessionPool[IP].isWeb = true;
return;
}
if (SessionPool[IP].isWeb)
{
msg = AnalyzeClientData(buffer, length);
}
byte[] msgBuffer = PackageServerData(msg);
foreach (Session se in SessionPool.Values)
{
se.SockeClient.Send(msgBuffer, msgBuffer.Length, SocketFlags.None);
}
}
catch
{
SockeClient.Disconnect(true);
Console.WriteLine("客户端 {0} 断开连接", IP);
SessionPool.Remove(IP);
}
}
#endregion #region 客户端和服务端的响应
/*
* 客户端向服务器发送请求
*
* GET / HTTP/1.1
* Origin: http://localhost:1416
* Sec-WebSocket-Key: vDyPp55hT1PphRU5OAe2Wg==
* Connection: Upgrade
* Upgrade: Websocket
*Sec-WebSocket-Version: 13
* User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
* Host: localhost:8064
* DNT: 1
* Cache-Control: no-cache
* Cookie: DTRememberName=admin
*
* 服务器给出响应
*
* HTTP/1.1 101 Switching Protocols
* Upgrade: websocket
* Connection: Upgrade
* Sec-WebSocket-Accept: xsOSgr30aKL2GNZKNHKmeT1qYjA=
*
* 在请求中的“Sec-WebSocket-Key”是随机的,服务器端会用这些数据来构造出一个SHA-1的信息摘要。把“Sec-WebSocket-Key”加上一个魔幻字符串
* “258EAFA5-E914-47DA-95CA-C5AB0DC85B11”。使用 SHA-1 加密,之后进行 BASE-64编码,将结果做为 “Sec-WebSocket-Accept” 头的值,返回给客户端
*/
#endregion #region 打包请求连接数据
/// <summary>
/// 打包请求连接数据
/// </summary>
/// <param name="handShakeBytes"></param>
/// <param name="length"></param>
/// <returns></returns>
private byte[] PackageHandShakeData(byte[] handShakeBytes, int length)
{
string handShakeText = Encoding.UTF8.GetString(handShakeBytes, 0, length);
string key = string.Empty;
Regex reg = new Regex(@"Sec\-WebSocket\-Key:(.*?)\r\n");
Match m = reg.Match(handShakeText);
if (m.Value != "")
{
key = Regex.Replace(m.Value, @"Sec\-WebSocket\-Key:(.*?)\r\n", "$1").Trim();
}
byte[] secKeyBytes = SHA1.Create().ComputeHash(Encoding.ASCII.GetBytes(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"));
string secKey = Convert.ToBase64String(secKeyBytes);
var responseBuilder = new StringBuilder();
responseBuilder.Append("HTTP/1.1 101 Switching Protocols" + "\r\n");
responseBuilder.Append("Upgrade: websocket" + "\r\n");
responseBuilder.Append("Connection: Upgrade" + "\r\n");
responseBuilder.Append("Sec-WebSocket-Accept: " + secKey + "\r\n\r\n");
return Encoding.UTF8.GetBytes(responseBuilder.ToString());
}
#endregion #region 处理接收的数据
/// <summary>
/// 处理接收的数据
/// 参考 http://www.cnblogs.com/smark/archive/2012/11/26/2789812.html
/// </summary>
/// <param name="recBytes"></param>
/// <param name="length"></param>
/// <returns></returns>
private string AnalyzeClientData(byte[] recBytes, int length)
{
int start = 0;
// 如果有数据则至少包括3位
if (length < 2) return "";
// 判断是否为结束针
bool IsEof = (recBytes[start] >> 7) > 0;
// 暂不处理超过一帧的数据
if (!IsEof) return "";
start++;
// 是否包含掩码
bool hasMask = (recBytes[start] >> 7) > 0;
// 不包含掩码的暂不处理
if (!hasMask) return "";
// 获取数据长度
UInt64 mPackageLength = (UInt64)recBytes[start] & 0x7F;
start++;
// 存储4位掩码值
byte[] Masking_key = new byte[4];
// 存储数据
byte[] mDataPackage;
if (mPackageLength == 126)
{
// 等于126 随后的两个字节16位表示数据长度
mPackageLength = (UInt64)(recBytes[start] << 8 | recBytes[start + 1]);
start += 2;
}
if (mPackageLength == 127)
{
// 等于127 随后的八个字节64位表示数据长度
mPackageLength = (UInt64)(recBytes[start] << (8 * 7) | recBytes[start] << (8 * 6) | recBytes[start] << (8 * 5) | recBytes[start] << (8 * 4) | recBytes[start] << (8 * 3) | recBytes[start] << (8 * 2) | recBytes[start] << 8 | recBytes[start + 1]);
start += 8;
}
mDataPackage = new byte[mPackageLength];
for (UInt64 i = 0; i < mPackageLength; i++)
{
mDataPackage[i] = recBytes[i + (UInt64)start + 4];
}
Buffer.BlockCopy(recBytes, start, Masking_key, 0, 4);
for (UInt64 i = 0; i < mPackageLength; i++)
{
mDataPackage[i] = (byte)(mDataPackage[i] ^ Masking_key[i % 4]);
}
return Encoding.UTF8.GetString(mDataPackage);
}
#endregion #region 发送数据
/// <summary>
/// 把发送给客户端消息打包处理(拼接上谁什么时候发的什么消息)
/// </summary>
/// <returns>The data.</returns>
/// <param name="message">Message.</param>
private byte[] PackageServerData(string msg)
{
byte[] content = null;
byte[] temp = Encoding.UTF8.GetBytes(msg);
if (temp.Length < 126)
{
content = new byte[temp.Length + 2];
content[0] = 0x81;
content[1] = (byte)temp.Length;
Buffer.BlockCopy(temp, 0, content, 2, temp.Length);
}
else if (temp.Length < 0xFFFF)
{
content = new byte[temp.Length + 4];
content[0] = 0x81;
content[1] = 126;
content[2] = (byte)(temp.Length & 0xFF);
content[3] = (byte)(temp.Length >> 8 & 0xFF);
Buffer.BlockCopy(temp, 0, content, 4, temp.Length);
}
return content;
}
#endregion
}
}
C# Socket 实现WebSocket服务器端的更多相关文章
- Http、Socket、WebSocket之间联系与区别
WebSocket和Socket区别 可以把WebSocket想象成HTTP(应用层),HTTP和Socket什么关系,WebSocket和Socket就是什么关系. HTTP 协议有一个缺陷:通信只 ...
- TCP UDP socket http webSocket 之间的关系
---恢复内容开始--- OSI&TCP/IP模型 要弄清tcp udp socket http websocket之间的关系,首先要知道经典的OSI七层模型,与之对应的是TCP/IP的四层模 ...
- Socket与WebSocket以及http与https重新总结
Socket与WebSocket以及http与https重新总结 一.Socket 网络中的Socket是一个抽象的接口 ,而是为了方便使用TCP或UDP而抽象出来的一层 ,可以理解为网络中连接的两端 ...
- swoolefy PHP的异步、并行、高性能网络通信引擎内置了Http/WebSocket服务器端/客户端
近半年来努力付出,项目终于要正式结项了,团队4人经历了很多困难,加班加点,最终完成了!剩下的时间将总结一下在该项目中用到知识和遇到问题.今天就从swoole说起!项目中实现异步大文件传输的功能,在服务 ...
- PHP Socket实现websocket(一)基本函数介绍
WebSocket protocol 是HTML5一种新的协议.它实现了浏览器与服务器全双工通信(full-duplex). 一开始的握手需要借助HTTP请求完成. WebSocket是基于TCP来实 ...
- socket.io websocket
不能不知道的事: 在Http协议中,客户端向服务器端发送请求,服务器端收到请求再进行回应,整个过程中,服务器端是被动方,客户端是主动方: websoket是H5的一种基于TCP的新通信协议,它与Htt ...
- C#版 Winform界面 Socket编程 Server服务器端
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...
- Hosting socket.io WebSocket apps in IIS using iisnode
In this post I explain how to configure a socket.io node.js application to use of WebSockets when ho ...
- PHP Socket实现websocket(三)Stream函数
除了socket函数也可以使用stream函数来实现服务器与客户端. 参考PHP 的Stream实现服务器客户端模型: http://php.net/manual/en/book.stream.php ...
随机推荐
- Hibernate分页查询的两个方法
Hibernate中HQL查询语句有一个分页查询, session.setMaxResult(参数)和session.setFirstResult(参数) 如果只设置session.setMaxRes ...
- 强关联二维材料1T—TaS2晶体
我校物理系张远波教授课题组通过一种新的实验方法——可控电荷插层,实现了对强关联二维材料1T—TaS2晶体相变的全面研究.1月26日,相关研究论文Gate-tunable phase transitio ...
- 机械硬盘怎么看是否4k对齐
在XP.VISTA.win7系统下,点击“开始”,“运行”,输入“MSINFO32”,点击“确定”,出现如下显示的界面,依次点击“组件/存储/磁盘”,查看“分区起始偏移”的数值,如果不能被4096整除 ...
- xp安装vmware10时一直停在installing packages on the system
我估计的原因是: vm会在网上邻居(LAN或高速internet)下创建两个 vmware network adapter vmnet8 vmware network adapter vmnet1 ...
- 有关于mfc webbrowser插件的使用
最近写的东西中常常需要嵌入一些浏览器,微软提供了一个比较好的接口,可以在MFC写的程序中嵌入一个简易的浏览器,是以ActiveX插件的形式提供的接口,使用起来也比较的方便,这里我就简单记录下这个插件的 ...
- Paxos Made Simple
Paxos一致性算法——分布式系统中的经典算法,论文本身也有一段有趣的故事.一致性问题是分布式系统的根本问题之一,在论文中,作者一步步的加强最初一致性问题(2.1节提出的问题)的约束条件,最终导出了一 ...
- OSGi 系列(三)之 bundle 详解
OSGi 系列(三)之 bundle 详解 1. 什么是 bundle bundle 是以 jar 包形式存在的一个模块化物理单元,里面包含了代码,资源文件和元数据(metadata),并且 jar ...
- Java 8 Optional 类深度解析
Java 8 Optional 类深度解析 身为一名Java程序员,大家可能都有这样的经历:调用一个方法得到了返回值却不能直接将返回值作为参数去调用别的方法.我们首先要判断这个返回值是否为null,只 ...
- boosting_bagging
boosting(提升法) 对于训练集中的每个样本建立全职W(i),当某个样本被错误分类概率很高时,样本的权重加大: 在迭代过程中,每一个迭代器都是一个弱分类器,我们需要用某种策略将其组合,作为最终模 ...
- Mockplus教程:分分钟搞定APP首页原型设计
Mockplus是一款快速原型设计工具,支持包括APP原型在内的多种原型与线框图设计.除了快速,Mockplus广受欢迎更因为它极低的上手门槛.今天小编就为大家展示如何用Mockplus在3分钟内完成 ...