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服务器端的更多相关文章

  1. Http、Socket、WebSocket之间联系与区别

    WebSocket和Socket区别 可以把WebSocket想象成HTTP(应用层),HTTP和Socket什么关系,WebSocket和Socket就是什么关系. HTTP 协议有一个缺陷:通信只 ...

  2. TCP UDP socket http webSocket 之间的关系

    ---恢复内容开始--- OSI&TCP/IP模型 要弄清tcp udp socket http websocket之间的关系,首先要知道经典的OSI七层模型,与之对应的是TCP/IP的四层模 ...

  3. Socket与WebSocket以及http与https重新总结

    Socket与WebSocket以及http与https重新总结 一.Socket 网络中的Socket是一个抽象的接口 ,而是为了方便使用TCP或UDP而抽象出来的一层 ,可以理解为网络中连接的两端 ...

  4. swoolefy PHP的异步、并行、高性能网络通信引擎内置了Http/WebSocket服务器端/客户端

    近半年来努力付出,项目终于要正式结项了,团队4人经历了很多困难,加班加点,最终完成了!剩下的时间将总结一下在该项目中用到知识和遇到问题.今天就从swoole说起!项目中实现异步大文件传输的功能,在服务 ...

  5. PHP Socket实现websocket(一)基本函数介绍

    WebSocket protocol 是HTML5一种新的协议.它实现了浏览器与服务器全双工通信(full-duplex). 一开始的握手需要借助HTTP请求完成. WebSocket是基于TCP来实 ...

  6. socket.io websocket

    不能不知道的事: 在Http协议中,客户端向服务器端发送请求,服务器端收到请求再进行回应,整个过程中,服务器端是被动方,客户端是主动方: websoket是H5的一种基于TCP的新通信协议,它与Htt ...

  7. C#版 Winform界面 Socket编程 Server服务器端

    using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...

  8. 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 ...

  9. PHP Socket实现websocket(三)Stream函数

    除了socket函数也可以使用stream函数来实现服务器与客户端. 参考PHP 的Stream实现服务器客户端模型: http://php.net/manual/en/book.stream.php ...

随机推荐

  1. ALTER数据库

    alter table dbo.Sheet1$ alter column UserId int null

  2. LuoguP1032 字符变换(BFS)

    题目链接为:https://www.luogu.org/problemnew/show/P1032 思路:看到数据比较小,而且最多有6个规则,就可以用搜索去做了,我用的BFS,大体思路如下: 定义结构 ...

  3. 第七章 二叉搜索树(b3)BST:删除

  4. OJ_单词倒排

    题目描述:对字符串中的所有单词进行倒排. 说明: 1.每个单词是以26个大写或小写英文字母构成,可以用一个“-”中连接线连接单词两部分表示一个单词,但是仅限一个“-”,出现两个“--”则为非构成单词的 ...

  5. 模板练习(LUOGU)

    1:并查集 P3183食物链 #define man 300050 ; int find(int x){ if(fa[x]==x) return fa[x]; return fa[x]=find(fa ...

  6. DB2数据类型(抄袭)

    数字 数据类型        精度          最小值                                        最大值                       smal ...

  7. jquery ui中的dialog,官网上经典的例子

    jquery ui中的dialog,官网上经典的例子   jquery ui中dialog和easy ui中的dialog很像,但是最近用到的时候全然没有印象,一段时间不用就忘记了,这篇随笔介绍一下这 ...

  8. Android中px, ppi, dpi, dp, dip, sp概念解析

    Android中px, ppi, dpi, dp, dip, sp概念解析

  9. 2018.09.14 codeforces364D(随机化算法)

    传送门 根据国家集训队2014论文集中胡泽聪的随机化算法可以通过这道题. 对于每个数,它有12" role="presentation" style="posi ...

  10. centos6.5(64位)离线安装scalr

    1.下载scalr-server安装备包: 下载地址:http://pan.baidu.com/s/1eSA3dom scalr-server-5.1.0.oss-nightly.2015013004 ...