http://blog.csdn.net/qq_20282263/article/details/54310737

C# 实现WebSocket服务端

原创 2017年01月10日 09:22:50
  • 10096
.net4.5中实现了对websocket的支持
在这里我使用的是.net4.0。因此需要对原本的socket发送的数据根据websocket的协议进行解析和打包。
文章参考 http://www.cnblogs.com/dolphinX/p/3462898.html
  • 1
  • 2
  • 3
  • 4
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);
}
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
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; }
}
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
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
}
}

WebSocket服务端的更多相关文章

  1. C# WebSocket 服务端示例代码 + HTML5客户端示例代码

    WebSocket服务端 C#示例代码 using System; using System.Collections.Generic; using System.Linq; using System. ...

  2. nodejs搭建简单的websocket服务端

    创建websocket服务端使用了nodejs-websocket ,首先要安装nodejs-websocket,在项目的目录下: npm install nodejs-websocket 1.搭建w ...

  3. 用nodejs快速实现websocket服务端(带SSL证书生成)

    有不少公司将nodejs的socket.io作为websocket的解决方案,很遗憾的是socket.io是对websocket的封装,并不支持html5原始的websocket协议,微信小程序使用的 ...

  4. asp.net网站作为websocket服务端的应用该如何写

    最近被websocket的一个问题困扰了很久,有一个需求是在web网站中搭建websocket服务.客户端通过网页与服务器建立连接,然后服务器根据ip给客户端网页发送信息. 其实,这个需求并不难,只是 ...

  5. Netty 搭建 WebSocket 服务端

    一.编码器.解码器 ... ... @Autowired private HttpRequestHandler httpRequestHandler; @Autowired private TextW ...

  6. VB6+Winsock编写的websocket服务端

    早就写好了,看这方面资料比较少,索性贴出来.只是一个DEMO中的,没有做优化,代码比较草.由于没地方上传附件,所以只把一些主要的代码贴出来. 这只是服务端,不过客户端可以反推出来,其实了解了webso ...

  7. .NET 即时通信,WebSocket服务端实例

    即时通信常用手段 1.第三方平台 谷歌.腾讯 环信等多如牛毛,其中谷歌即时通信是免费的,但免费就是免费的并不好用.其他的一些第三方一般收费的,使用要则限流(1s/限制x条消息)要么则限制用户数. 但稳 ...

  8. .NET实现WebSocket服务端即时通信实例

    即时通信常用手段 1.第三方平台 谷歌.腾讯 环信等多如牛毛,其中谷歌即时通信是免费的,但免费就是免费的并不好用.其他的一些第三方一般收费的,使用要则限流(1s/限制x条消息)要么则限制用户数. 但稳 ...

  9. [Go] 实现websocket服务端

    直接使用官方子包可以实现websocket协议, golang.org/x/net/websocket 如果在这个目录没有这个包就去github下载net库,放进这个目录$GOPATH/src/gol ...

随机推荐

  1. rsync 同步多台服务器项目目录

    server1:192.168.10.1server2:192.168.10.2实现server2实时同步server1的数据! ###############server1############# ...

  2. vue按需加载组件-webpack require.ensure

    使用 vue-cli构建的项目,在 默认情况下 ,执行 npm run build 会将所有的js代码打包为一个整体, 打包位置是 dist/static/js/app.[contenthash].j ...

  3. Spring Boot . 3 -- Spring Boot Auto_configuration 是如何实现的?

    配置是Spring 框架的重要核心之一,所以Spring 应用能够正常的跑起来肯定是需要配置的,但是使用的Spring Boot 后很多配置没有做,那么AUTO-CONFIGURATION 到底是怎么 ...

  4. IP、CIDR、广播地址、子网掩码、MAC地址--这些是什么鬼

    继续学习趣谈网络协议中的内容,认识几个专有名词,IP.CIDR.广播地址.子网掩码.MAC地址,这些都是什么鬼? 一.IP IP地址是一个网卡在网络世界的通讯地址,相当于我们现实世界的门牌号码 (1) ...

  5. Quartz任务调度2

    注意: 不同的版本的jar包,具体的操作不太相同,但是思路是相同的:比如1.8.6jar包中,JobDetail是个类,直接通过构造方法与Job类关联.SimpleTrigger和CornTrigge ...

  6. 采用Qt快速绘制多条曲线(折线),跟随鼠标动态显示线上点的值(基于Qt的开源绘图控件QCustomPlot进行二次开发)

    QCustomPlot是一个开源的基于Qt的第三方绘图库,能够绘制漂亮的2D图形. QCustomPlot的官方网址:https://www.qcustomplot.com/ 从官网下载QCustom ...

  7. centos下安装redis(记录其中踩坑的过程)

    一.先下载到redis-3.0.4.tar.gz包(本文以3.0.4版本为例) 我将这个包放在/opt目录下,在/opt下并解压这个包 tar -zxvf redis-.tar.gz 然后进入redi ...

  8. Python之文件处理-递归删除特定文件

    Python之文件处理-递归删除特定文件 #!/usr/bin/env python # -*- coding:utf-8 -*- import os def delete_particular_fi ...

  9. 集训第四周(高效算法设计)L题 (背包贪心)

    Description   John Doe is a famous DJ and, therefore, has the problem of optimizing the placement of ...

  10. allegro中查看寄生参数

    在allegro中可以查看线的寄生参数,这个命令所在的位置在如下如位置: 版权声明:本文为博主原创文章,未经博主允许不得转载.