first upload

This commit is contained in:
miuser
2019-04-23 13:02:29 +08:00
parent 02414de697
commit dc10f33175
171 changed files with 50745 additions and 2 deletions

View File

@@ -1,2 +1,60 @@
# UdpPlugWebsocket
这是一个独立的可执行Win32服务端程序用于桥接终端APP和物联网硬件
# UdpPlugWebSocket 简称UPWS服务端程序#
## 项目说明 ##
公司要做物联网硬件产品需要一个服务器组件桥接APP软件终端和物联网硬件。 目前比较火的MQTT物联网协议虽然技术先进但进入门槛较高用户也相对较为复杂。BG了一番没有找到合适的服务端开源代码就决定使用自己比较熟悉的.Net技术从头开发一个服务器组件。于是开了这个repository如果能帮到你我很高兴。 有问题欢迎与我微信联系: 微信号miuser00加好友请注明您的来意。
## 项目简介 ##
这是一个独立的可执行Win32服务端程序用于桥接终端APP和物联网硬件。 APP端为Websocet接口的静态html页面硬件端为基于Arduino开发的电路与服务器通讯使用UDP协议通讯为方便您的使用本Respository亦提供了一个C#编写的简易UDP测试程序用来模拟硬件设备)。 APP通过Webscoket接口发送一个自定义的字符串报文给服务端服务端根据报文中的ID把报文转发给相同ID的UDP硬件设备或UDP测试程序
![](.\document\preview.png)
启动方法在本地windows环境下运行.\UdpPlugWebsocket\bin\Debug\UdpPlugWebsocket.exe即可按照config.xml中描述的端口启动服务程序。默认UDP端口为7101Websocket端口为9000。此时外部的UDP连接和Websocket连接都会在UI中显示。其中UDP在终端设备页面中显示Websocket连接在控制页面中显示通讯状况在交换面板页面中显示。您可以通过配置->显示调试系信息 打开或关闭UI的通讯Log。
## 数据包格式 UDP与Websocket相同##
![](.\document\packageintro.png)
**Endpoint是外部连接的唯一索引。**
**Endpoint字符串的格式为"XXX.XXX.XXX.XXX:XXXX" 前四组三位数为IP地址最后一组四位数为远程端口号**
##UDP通讯测试##
运行.\UdpExample\client\bin\Debug\client.exe 在程序控制台中输入如下数据包
004832A08000000000200000000000000001234testtest05 即可在终端设备页面看到本机的连接。
![](.\document\clienttest.png)
## Websocket通讯测试 ##
任意支持websocket的浏览器中运行.\UPW_Browser\index.html?ID=0000000002&MM=0000000000000000与UDP测试程序建立连接参数区分大小写
![](.\document\webtest.png)
## 目录结构 ##
.\UdpPlugWebsocket UPWS服务程序主服务程序
.\UdpPlugWebsocket\UdpExample UDP测试程序
.\UPW_Browser Websocket测试程序
.\bin_UPWS 编译后的Win32项目二进制文
... 其余目录为参考代码目录,可删除
##编译环境##
Visual Studio 2015,C#,Win10
## 使用到的开源库 ##
Websocket-Sharp
https://github.com/sta/websocket-sharp
Coldairarrow.Util.Sockets
https://github.com/Coldairarrow/Sockets
## 关键词 ##
C#,Websocket,UDP,物联网,异步编程,接口,lamda表达式,线程池,Action,Invoke,Arduino,ESP8266
## 版权声明 ##
如果您愿意使用 UdpPlugWebSocket 组件,请遵循 MIT 许可所述内容.

View File

@@ -0,0 +1,37 @@
<html>
<head>
<script>
var socket;
if ("WebSocket" in window) {
var ws = new WebSocket("ws://127.0.0.1:9000");
socket = ws;
ws.onopen = function() {
console.log('连接成功');
};
ws.onmessage = function(evt) {
var received_msg = evt.data;
document.getElementById("showMes").value+=evt.data+"\n";
};
ws.onclose = function() {
alert("断开了连接");
};
} else {
alert("浏览器不支持WebSocket");
}
function login(){
var message=document.getElementById("name").value+":"+document.getElementById("mes").value;
socket.send(message);
}
</script>
</head>
<body>
<textarea rows="3" cols="30" id="showMes" style="width:300px;height:500px;"></textarea>
<br/>
<label>名称</label>
<input type="text" id="name"/>
<br/>
<label>消息</label>
<input type="text" id="mes"/>
<button onclick="login();">发送</button>
</body>
</html>

File diff suppressed because one or more lines are too long

2
UPW_Browser/main/jquery-1.8.3.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
!function(a,b){"function"==typeof define&&define.amd?define([],b):"undefined"!=typeof module&&module.exports?module.exports=b():a.ReconnectingWebSocket=b()}(this,function(){function a(b,c,d){function l(a,b){var c=document.createEvent("CustomEvent");return c.initCustomEvent(a,!1,!1,b),c}var e={debug:!1,automaticOpen:!0,reconnectInterval:1e3,maxReconnectInterval:3e4,reconnectDecay:1.5,timeoutInterval:2e3};d||(d={});for(var f in e)this[f]="undefined"!=typeof d[f]?d[f]:e[f];this.url=b,this.reconnectAttempts=0,this.readyState=WebSocket.CONNECTING,this.protocol=null;var h,g=this,i=!1,j=!1,k=document.createElement("div");k.addEventListener("open",function(a){g.onopen(a)}),k.addEventListener("close",function(a){g.onclose(a)}),k.addEventListener("connecting",function(a){g.onconnecting(a)}),k.addEventListener("message",function(a){g.onmessage(a)}),k.addEventListener("error",function(a){g.onerror(a)}),this.addEventListener=k.addEventListener.bind(k),this.removeEventListener=k.removeEventListener.bind(k),this.dispatchEvent=k.dispatchEvent.bind(k),this.open=function(b){h=new WebSocket(g.url,c||[]),b||k.dispatchEvent(l("connecting")),(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","attempt-connect",g.url);var d=h,e=setTimeout(function(){(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","connection-timeout",g.url),j=!0,d.close(),j=!1},g.timeoutInterval);h.onopen=function(){clearTimeout(e),(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onopen",g.url),g.protocol=h.protocol,g.readyState=WebSocket.OPEN,g.reconnectAttempts=0;var d=l("open");d.isReconnect=b,b=!1,k.dispatchEvent(d)},h.onclose=function(c){if(clearTimeout(e),h=null,i)g.readyState=WebSocket.CLOSED,k.dispatchEvent(l("close"));else{g.readyState=WebSocket.CONNECTING;var d=l("connecting");d.code=c.code,d.reason=c.reason,d.wasClean=c.wasClean,k.dispatchEvent(d),b||j||((g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onclose",g.url),k.dispatchEvent(l("close")));var e=g.reconnectInterval*Math.pow(g.reconnectDecay,g.reconnectAttempts);setTimeout(function(){g.reconnectAttempts++,g.open(!0)},e>g.maxReconnectInterval?g.maxReconnectInterval:e)}},h.onmessage=function(b){(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onmessage",g.url,b.data);var c=l("message");c.data=b.data,k.dispatchEvent(c)},h.onerror=function(b){(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onerror",g.url,b),k.dispatchEvent(l("error"))}},1==this.automaticOpen&&this.open(!1),this.send=function(b){if(h)return(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","send",g.url,b),h.send(b);throw"INVALID_STATE_ERR : Pausing to reconnect websocket"},this.close=function(a,b){"undefined"==typeof a&&(a=1e3),i=!0,h&&h.close(a,b)},this.refresh=function(){h&&h.close()}}return a.prototype.onopen=function(){},a.prototype.onclose=function(){},a.prototype.onconnecting=function(){},a.prototype.onmessage=function(){},a.prototype.onerror=function(){},a.debugAll=!1,a.CONNECTING=WebSocket.CONNECTING,a.OPEN=WebSocket.OPEN,a.CLOSING=WebSocket.CLOSING,a.CLOSED=WebSocket.CLOSED,a});

54
UdpExample/UdpExample.sln Normal file
View File

@@ -0,0 +1,54 @@

Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "server", "server\server.csproj", "{BEC0344F-9AE7-4792-8A5D-C60A39D98732}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "client", "client\client.csproj", "{6593C9FB-053E-4292-B267-00FFD99C0278}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "udp", "udp\udp.csproj", "{F5BDA737-1EBE-4864-877C-36C5F3295F1E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|Mixed Platforms = Debug|Mixed Platforms
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|Mixed Platforms = Release|Mixed Platforms
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{BEC0344F-9AE7-4792-8A5D-C60A39D98732}.Debug|Any CPU.ActiveCfg = Debug|x86
{BEC0344F-9AE7-4792-8A5D-C60A39D98732}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
{BEC0344F-9AE7-4792-8A5D-C60A39D98732}.Debug|Mixed Platforms.Build.0 = Debug|x86
{BEC0344F-9AE7-4792-8A5D-C60A39D98732}.Debug|x86.ActiveCfg = Debug|x86
{BEC0344F-9AE7-4792-8A5D-C60A39D98732}.Debug|x86.Build.0 = Debug|x86
{BEC0344F-9AE7-4792-8A5D-C60A39D98732}.Release|Any CPU.ActiveCfg = Release|x86
{BEC0344F-9AE7-4792-8A5D-C60A39D98732}.Release|Mixed Platforms.ActiveCfg = Release|x86
{BEC0344F-9AE7-4792-8A5D-C60A39D98732}.Release|Mixed Platforms.Build.0 = Release|x86
{BEC0344F-9AE7-4792-8A5D-C60A39D98732}.Release|x86.ActiveCfg = Release|x86
{BEC0344F-9AE7-4792-8A5D-C60A39D98732}.Release|x86.Build.0 = Release|x86
{6593C9FB-053E-4292-B267-00FFD99C0278}.Debug|Any CPU.ActiveCfg = Debug|x86
{6593C9FB-053E-4292-B267-00FFD99C0278}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
{6593C9FB-053E-4292-B267-00FFD99C0278}.Debug|Mixed Platforms.Build.0 = Debug|x86
{6593C9FB-053E-4292-B267-00FFD99C0278}.Debug|x86.ActiveCfg = Debug|x86
{6593C9FB-053E-4292-B267-00FFD99C0278}.Debug|x86.Build.0 = Debug|x86
{6593C9FB-053E-4292-B267-00FFD99C0278}.Release|Any CPU.ActiveCfg = Release|x86
{6593C9FB-053E-4292-B267-00FFD99C0278}.Release|Mixed Platforms.ActiveCfg = Release|x86
{6593C9FB-053E-4292-B267-00FFD99C0278}.Release|Mixed Platforms.Build.0 = Release|x86
{6593C9FB-053E-4292-B267-00FFD99C0278}.Release|x86.ActiveCfg = Release|x86
{6593C9FB-053E-4292-B267-00FFD99C0278}.Release|x86.Build.0 = Release|x86
{F5BDA737-1EBE-4864-877C-36C5F3295F1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F5BDA737-1EBE-4864-877C-36C5F3295F1E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F5BDA737-1EBE-4864-877C-36C5F3295F1E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{F5BDA737-1EBE-4864-877C-36C5F3295F1E}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{F5BDA737-1EBE-4864-877C-36C5F3295F1E}.Debug|x86.ActiveCfg = Debug|Any CPU
{F5BDA737-1EBE-4864-877C-36C5F3295F1E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F5BDA737-1EBE-4864-877C-36C5F3295F1E}.Release|Any CPU.Build.0 = Release|Any CPU
{F5BDA737-1EBE-4864-877C-36C5F3295F1E}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{F5BDA737-1EBE-4864-877C-36C5F3295F1E}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{F5BDA737-1EBE-4864-877C-36C5F3295F1E}.Release|x86.ActiveCfg = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using udp;
namespace client
{
class Program
{
private const string _serverIp = "127.0.0.1";
private const int _serverPort = 7101;
static void Main(string[] args)
{
Client client = new Client();
client.ep = new IPEndPoint(IPAddress.Parse(_serverIp), _serverPort);
client.Listening();
client.Received += new UdpEventHandler(client_Received);
while (true)
{
string tmp = Console.ReadLine();
byte[] bt = Encoding.Default.GetBytes(tmp);
System.Threading.Thread t = new System.Threading.Thread(() =>
{
client.Send(bt, client.ep);
});
t.Start();
}
}
static void client_Received(object sender, UdpEventArgs e)
{
IPEndPoint ep = e.Remote as IPEndPoint;
string tmpReceived = Encoding.Default.GetString(e.Received);
Console.WriteLine(ep.Address.ToString() + ":" + ep.Port + "--> " + tmpReceived);
}
}
public class Client : Udp
{
public EndPoint ep;
public Client()
{
}
}
}

View File

@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// 有关程序集的常规信息通过以下
// 特性集控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("client")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Microsoft")]
[assembly: AssemblyProduct("client")]
[assembly: AssemblyCopyright("Copyright © Microsoft 2015")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// 将 ComVisible 设置为 false 使此程序集中的类型
// 对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型,
// 则将该类型上的 ComVisible 特性设置为 true。
[assembly: ComVisible(false)]
// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
[assembly: Guid("9550478a-9201-4126-8828-a465a0fda94d")]
// 程序集的版本信息由下面四个值组成:
//
// 主版本
// 次版本
// 内部版本号
// 修订号
//
// 可以指定所有这些值,也可以使用“内部版本号”和“修订号”的默认值,
// 方法是按如下所示使用“*”:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{6593C9FB-053E-4292-B267-00FFD99C0278}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>client</RootNamespace>
<AssemblyName>client</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkProfile>Client</TargetFrameworkProfile>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="udp">
<HintPath>..\udp\bin\Debug\udp.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using udp;
namespace server
{
class Program
{
static Server server = new Server();
static void Main(string[] args)
{
server.Port = 8000;
server.Listening();
if (server.IsListening)
{
server.Received += new UdpEventHandler(server_Received);
}
Console.ReadKey();
}
static void server_Received(object sender, UdpEventArgs e)
{
IPEndPoint ep = e.Remote as IPEndPoint;
string tmpReceived = Encoding.Default.GetString(e.Received);
Console.WriteLine(ep.Address.ToString() + ":" + ep.Port + "--> " + tmpReceived);
///自动回复
server.Send(Encoding.Default.GetBytes("服务器已收到数据:'" + tmpReceived + "',来自:" + ep.Address.ToString() + ":" + ep.Port + ""), ep);
}
}
public class Server : Udp
{
private EndPoint ep;
public Server()
{
}
}
}

View File

@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// 有关程序集的常规信息通过以下
// 特性集控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("server")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Microsoft")]
[assembly: AssemblyProduct("server")]
[assembly: AssemblyCopyright("Copyright © Microsoft 2015")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// 将 ComVisible 设置为 false 使此程序集中的类型
// 对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型,
// 则将该类型上的 ComVisible 特性设置为 true。
[assembly: ComVisible(false)]
// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
[assembly: Guid("186ec332-5da3-4abe-8970-ad8cafb2c209")]
// 程序集的版本信息由下面四个值组成:
//
// 主版本
// 次版本
// 内部版本号
// 修订号
//
// 可以指定所有这些值,也可以使用“内部版本号”和“修订号”的默认值,
// 方法是按如下所示使用“*”:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{BEC0344F-9AE7-4792-8A5D-C60A39D98732}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>server</RootNamespace>
<AssemblyName>server</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkProfile>Client</TargetFrameworkProfile>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="udp">
<HintPath>..\udp\bin\Debug\udp.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

18
UdpExample/udp/IUdp.cs Normal file
View File

@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
namespace udp
{
public interface IUdp : IDisposable
{
event UdpEventHandler Received;
void Send(byte[] bt, EndPoint ep);
Socket UdpSocket { get; }
}
}

View File

@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// 有关程序集的常规信息通过以下
// 特性集控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("udp")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Microsoft")]
[assembly: AssemblyProduct("udp")]
[assembly: AssemblyCopyright("Copyright © Microsoft 2015")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// 将 ComVisible 设置为 false 使此程序集中的类型
// 对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型,
// 则将该类型上的 ComVisible 特性设置为 true。
[assembly: ComVisible(false)]
// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
[assembly: Guid("1202b3ab-a590-4663-bc20-74a8c1dfebc2")]
// 程序集的版本信息由下面四个值组成:
//
// 主版本
// 次版本
// 内部版本号
// 修订号
//
// 可以指定所有这些值,也可以使用“内部版本号”和“修订号”的默认值,
// 方法是按如下所示使用“*”:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

146
UdpExample/udp/Udp.cs Normal file
View File

@@ -0,0 +1,146 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
namespace udp
{
public delegate void UdpEventHandler(object sender, UdpEventArgs e);
public abstract class Udp : IUdp
{
public event UdpEventHandler Received;
private int _port;
private string _ip;
public bool IsListening { get; private set; }
private Socket _sck;
public Socket UdpSocket
{
get { return _sck; }
}
public string Ip
{
get { return _ip; }
set { _ip = value; }
}
public int Port
{
get { return _port; }
set
{
if (value < 0)
value = 0;
if (value > 65536)
value = 65536;
_port = value;
}
}
public Udp()
{
_sck = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
_sck.ReceiveBufferSize = UInt16.MaxValue * 8;
//log
System.Diagnostics.Trace.Listeners.Clear();
System.Diagnostics.Trace.AutoFlush = true;
System.Diagnostics.Trace.Listeners.Add(new System.Diagnostics.TextWriterTraceListener("log.txt"));
}
public void Listening()
{
IPAddress ip = IPAddress.Any;
try
{
if (this._ip != null)
if (!IPAddress.TryParse(this._ip, out ip))
throw new ArgumentException("IP地址错误", "Ip");
_sck.Bind(new IPEndPoint(ip, this._port));
UdpState state = new UdpState();
state.Socket = _sck;
state.Remote = new IPEndPoint(IPAddress.Any, 0);
_sck.BeginReceiveFrom(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, ref state.Remote, new AsyncCallback(EndReceiveFrom), state);
IsListening = true;
}
catch (ArgumentException ex)
{
IsListening = false;
System.Diagnostics.Trace.WriteLine(DateTime.Now.ToString() + "\t" + ex.Message);
throw ex;
}
catch (Exception ex)
{
IsListening = false;
System.Diagnostics.Trace.WriteLine(DateTime.Now.ToString() + "\t" + ex.Message);
throw ex;
}
}
private void EndReceiveFrom(IAsyncResult ir)
{
if (IsListening)
{
UdpState state = ir.AsyncState as UdpState;
try
{
if (ir.IsCompleted)
{
int length = state.Socket.EndReceiveFrom(ir, ref state.Remote);
byte[] btReceived = new byte[length];
Buffer.BlockCopy(state.Buffer, 0, btReceived, 0, length);
OnReceived(new UdpEventArgs(btReceived, state.Remote));
}
}
catch (Exception ex)
{
System.Diagnostics.Trace.WriteLine(DateTime.Now.ToString() + "\t" + ex.Message+ex.Source);
}
finally
{
state.Socket.BeginReceiveFrom(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, ref state.Remote, new AsyncCallback(EndReceiveFrom), state);
}
}
}
private void OnReceived(UdpEventArgs e)
{
if (this.Received != null)
{
Received(this, e);
}
}
public void Send(byte[] bt, EndPoint ep)
{
if (_sck == null) return;
try
{
this._sck.SendTo(bt, ep);
}
catch (SocketException ex)
{
System.Diagnostics.Trace.WriteLine(DateTime.Now.ToString() + "\t" + ex.Message);
throw ex;
}
}
public void Dispose()
{
if (_sck == null) return;
using (_sck) ;
//this.IsListening = false;
//this._sck.Blocking = false;
//this._sck.Shutdown(SocketShutdown.Both);
//this._sck.Close();
//this._sck = null;
}
}
}

View File

@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
namespace udp
{
public class UdpEventArgs : EventArgs
{
private EndPoint _remote;
private byte[] _rec;
public byte[] Received
{
get { return _rec; }
}
public EndPoint Remote
{
get { return _remote; }
}
public UdpEventArgs(byte[] data, EndPoint remote)
{
this._remote = remote;
this._rec = data;
}
}
}

View File

@@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
namespace udp
{
internal class UdpState
{
public byte[] Buffer;
public EndPoint Remote;
public Socket Socket;
public UdpState()
{
Buffer = new byte[65536];
}
}
}

58
UdpExample/udp/udp.csproj Normal file
View File

@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{F5BDA737-1EBE-4864-877C-36C5F3295F1E}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>udp</RootNamespace>
<AssemblyName>udp</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile>Client</TargetFrameworkProfile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="IUdp.cs" />
<Compile Include="Udp.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="UdpEventArgs.cs" />
<Compile Include="UdpState.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

22
UdpPlugWebsocket.sln Normal file
View File

@@ -0,0 +1,22 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UdpPlugWebsocket", "UdpPlugWebsocket\UdpPlugWebsocket.csproj", "{54CF701B-B076-4FC3-8A01-7434111A1C5B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{54CF701B-B076-4FC3-8A01-7434111A1C5B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{54CF701B-B076-4FC3-8A01-7434111A1C5B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{54CF701B-B076-4FC3-8A01-7434111A1C5B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{54CF701B-B076-4FC3-8A01-7434111A1C5B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
</startup>
</configuration>

191
UdpPlugWebsocket/Browser.Designer.cs generated Normal file
View File

@@ -0,0 +1,191 @@
namespace UdpPlugWebsocket
{
partial class Browser
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.groupBox1 = new System.Windows.Forms.GroupBox();
this.dgv_browsers = new System.Windows.Forms.DataGridView();
this.panel1 = new System.Windows.Forms.Panel();
this.txt_Send = new System.Windows.Forms.TextBox();
this.btn_Send = new System.Windows.Forms.Button();
this.splitter1 = new System.Windows.Forms.Splitter();
this.groupBox2 = new System.Windows.Forms.GroupBox();
this.richTextBox1 = new System.Windows.Forms.RichTextBox();
this.dataGridView1 = new System.Windows.Forms.DataGridView();
this.groupBox1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.dgv_browsers)).BeginInit();
this.panel1.SuspendLayout();
this.groupBox2.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit();
this.SuspendLayout();
//
// groupBox1
//
this.groupBox1.Controls.Add(this.dgv_browsers);
this.groupBox1.Dock = System.Windows.Forms.DockStyle.Fill;
this.groupBox1.Font = new System.Drawing.Font("微软雅黑", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.groupBox1.Location = new System.Drawing.Point(0, 0);
this.groupBox1.Name = "groupBox1";
this.groupBox1.Size = new System.Drawing.Size(830, 600);
this.groupBox1.TabIndex = 2;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "浏览客户端:";
//
// dgv_browsers
//
this.dgv_browsers.AllowUserToAddRows = false;
this.dgv_browsers.BackgroundColor = System.Drawing.Color.WhiteSmoke;
this.dgv_browsers.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.dgv_browsers.Dock = System.Windows.Forms.DockStyle.Fill;
this.dgv_browsers.GridColor = System.Drawing.Color.Snow;
this.dgv_browsers.Location = new System.Drawing.Point(3, 25);
this.dgv_browsers.MultiSelect = false;
this.dgv_browsers.Name = "dgv_browsers";
this.dgv_browsers.ReadOnly = true;
this.dgv_browsers.RowHeadersVisible = false;
this.dgv_browsers.RowHeadersWidth = 4;
this.dgv_browsers.RowTemplate.Height = 23;
this.dgv_browsers.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect;
this.dgv_browsers.Size = new System.Drawing.Size(824, 572);
this.dgv_browsers.TabIndex = 0;
this.dgv_browsers.SelectionChanged += new System.EventHandler(this.dgv_browsers_SelectionChanged);
//
// panel1
//
this.panel1.Controls.Add(this.txt_Send);
this.panel1.Controls.Add(this.btn_Send);
this.panel1.Dock = System.Windows.Forms.DockStyle.Bottom;
this.panel1.Location = new System.Drawing.Point(0, 600);
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size(830, 25);
this.panel1.TabIndex = 5;
//
// txt_Send
//
this.txt_Send.Dock = System.Windows.Forms.DockStyle.Fill;
this.txt_Send.Font = new System.Drawing.Font("宋体", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.txt_Send.Location = new System.Drawing.Point(0, 0);
this.txt_Send.Name = "txt_Send";
this.txt_Send.Size = new System.Drawing.Size(744, 26);
this.txt_Send.TabIndex = 0;
//
// btn_Send
//
this.btn_Send.Dock = System.Windows.Forms.DockStyle.Right;
this.btn_Send.Location = new System.Drawing.Point(744, 0);
this.btn_Send.Name = "btn_Send";
this.btn_Send.Size = new System.Drawing.Size(86, 25);
this.btn_Send.TabIndex = 1;
this.btn_Send.Text = "发送";
this.btn_Send.UseVisualStyleBackColor = true;
this.btn_Send.Click += new System.EventHandler(this.btn_Send_Click);
//
// splitter1
//
this.splitter1.Dock = System.Windows.Forms.DockStyle.Bottom;
this.splitter1.Location = new System.Drawing.Point(0, 368);
this.splitter1.Name = "splitter1";
this.splitter1.Size = new System.Drawing.Size(830, 10);
this.splitter1.TabIndex = 9;
this.splitter1.TabStop = false;
//
// groupBox2
//
this.groupBox2.Controls.Add(this.richTextBox1);
this.groupBox2.Controls.Add(this.dataGridView1);
this.groupBox2.Dock = System.Windows.Forms.DockStyle.Bottom;
this.groupBox2.Font = new System.Drawing.Font("微软雅黑", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.groupBox2.Location = new System.Drawing.Point(0, 378);
this.groupBox2.Name = "groupBox2";
this.groupBox2.Size = new System.Drawing.Size(830, 222);
this.groupBox2.TabIndex = 8;
this.groupBox2.TabStop = false;
this.groupBox2.Text = "接收:";
//
// richTextBox1
//
this.richTextBox1.Dock = System.Windows.Forms.DockStyle.Fill;
this.richTextBox1.Location = new System.Drawing.Point(3, 25);
this.richTextBox1.Name = "richTextBox1";
this.richTextBox1.Size = new System.Drawing.Size(824, 194);
this.richTextBox1.TabIndex = 5;
this.richTextBox1.Text = "";
//
// dataGridView1
//
this.dataGridView1.AllowUserToAddRows = false;
this.dataGridView1.BackgroundColor = System.Drawing.Color.WhiteSmoke;
this.dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.dataGridView1.Dock = System.Windows.Forms.DockStyle.Fill;
this.dataGridView1.GridColor = System.Drawing.Color.Snow;
this.dataGridView1.Location = new System.Drawing.Point(3, 25);
this.dataGridView1.MultiSelect = false;
this.dataGridView1.Name = "dataGridView1";
this.dataGridView1.ReadOnly = true;
this.dataGridView1.RowHeadersVisible = false;
this.dataGridView1.RowHeadersWidth = 4;
this.dataGridView1.RowTemplate.Height = 23;
this.dataGridView1.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect;
this.dataGridView1.Size = new System.Drawing.Size(824, 194);
this.dataGridView1.TabIndex = 0;
//
// Browser
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(830, 625);
this.Controls.Add(this.splitter1);
this.Controls.Add(this.groupBox2);
this.Controls.Add(this.groupBox1);
this.Controls.Add(this.panel1);
this.Name = "Browser";
this.Text = "Browser";
this.Load += new System.EventHandler(this.Browser_Load);
this.groupBox1.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.dgv_browsers)).EndInit();
this.panel1.ResumeLayout(false);
this.panel1.PerformLayout();
this.groupBox2.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.GroupBox groupBox1;
private System.Windows.Forms.DataGridView dgv_browsers;
private System.Windows.Forms.Panel panel1;
private System.Windows.Forms.TextBox txt_Send;
private System.Windows.Forms.Button btn_Send;
private System.Windows.Forms.Splitter splitter1;
private System.Windows.Forms.GroupBox groupBox2;
private System.Windows.Forms.RichTextBox richTextBox1;
private System.Windows.Forms.DataGridView dataGridView1;
}
}

222
UdpPlugWebsocket/Browser.cs Normal file
View File

@@ -0,0 +1,222 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using WebSocketSharp.Server;
using WebSocketSharp;
using System.IO;
namespace UdpPlugWebsocket
{
public partial class Browser : Form
{
string s_output;
//本Websocket服务对象
public WebSocketServer server;
//人工选中的连接对象
public static IWebSocketSession curr;
private static Browser _instance;
public Browser()
{
InitializeComponent();
server = new WebSocketServer("ws://0.0.0.0:"+SetupForm.cfg.BrowserPort);
server.AddWebSocketService<Laputa>("/");
server.Start();
}
private void Browser_Load(object sender, EventArgs e)
{
}
/// <summary>
/// 发送字节
/// </summary>
public void Send(string EndpointString,byte[] bytes)
{
try
{
IWebSocketSession session=server.WebSocketServices["/"].Sessions.Sessions.Where(x => { return x.Context.UserEndPoint.ToString() == EndpointString; }).FirstOrDefault();
server.WebSocketServices["/"].Sessions.SendToAsync(System.Text.Encoding.Default.GetString(bytes), session.ID, null);
}
catch(Exception ex)
{
HandleError?.Invoke(ex.StackTrace);
}
}
public void RefreshDataGrid(WebSocketSessionManager wsm)
{
DataTable dt = GetConnectionTable(wsm);
{
this.Invoke(new Action(() =>
{
lock (dt.Rows.SyncRoot)
{
dgv_browsers.DataSource = dt.Copy();
}
dgv_browsers.Columns[0].Width = 400;
dgv_browsers.Columns[1].Width = 200;
}));
}
}
private DataTable GetConnectionTable(WebSocketSessionManager wsm)
{
DataTable dt = new DataTable();
dt.Columns.Add("ID");
dt.Columns.Add("EndPoint");
dt.Columns.Add("State");
lock (wsm.Sessions)
{
foreach (IWebSocketSession session in wsm.Sessions.ToArray())
{
DataRow dr = dt.NewRow();
dr[0] = session.ID;
dr[1] = session.Context.UserEndPoint;
dr[2] = session.State;
dt.Rows.Add(dr);
}
}
return dt;
}
public static Browser Instance
{
get
{
if (_instance == null)
_instance = new Browser();
return _instance;
}
private set { _instance = value; }
}
public void LogMessage(string message)
{
HandleMessage?.Invoke(message);
}
public void LogError(string message)
{
HandleError?.Invoke(message);
}
#region
/// <summary>
/// 消息处理程序
/// </summary>
public Action<string> HandleMessage { get; set; }
/// <summary>
/// 异常处理程序
/// </summary>
public Action<string> HandleError { get; set; }
#endregion
private void btn_Send_Click(object sender, EventArgs e)
{
try
{
server.WebSocketServices["/"].Sessions.SendToAsync(txt_Send.Text, curr.ID, null);
}catch (Exception ex)
{
HandleError?.Invoke(ex.StackTrace);
}
}
private void dgv_browsers_SelectionChanged(object sender, EventArgs e)
{
if (dgv_browsers.SelectedRows.Count > 0)
{
string tag = dgv_browsers.SelectedRows[0].Cells["EndPoint"].Value.ToString();
curr = server.WebSocketServices["/"].Sessions.Sessions.Where(x =>
{
var Id = (string)x.Context.UserEndPoint.ToString();
return Id == tag;
}).FirstOrDefault();
}
else
{
curr = null;
}
}
public void SetOutput(string text)
{
//决定是否屏显
if (SetupForm.cfg.EnableScreenLog == false) return;
text = DateTime.Now.ToLongDateString() + " " + DateTime.Now.ToLongTimeString() + " |" + text;
this.Invoke(new Action(() =>
{
s_output = s_output + text.Replace("\0", "") + "\r";
if ((s_output.Length) > 5000)
{
s_output = s_output.Substring(s_output.Length - 5000, 5000);
}
//滚到最后
this.richTextBox1.Text = s_output;
this.richTextBox1.Select(richTextBox1.TextLength, 0);
//this.richTextBox1.Focus();
this.richTextBox1.ScrollToCaret();
}));
}
#region
/// <summary>
/// 当新客户端连接后执行
/// </summary>
public Action<WebSocketSharp.Net.WebSockets.WebSocketContext> HandleNewWebSocketClientConnected { get; set; }
/// <summary>
/// 客户端连接接受新的消息后调用
/// </summary>
public Action<byte[], WebSocketSharp.Net.WebSockets.WebSocketContext> HandleWebSocketRecMsg { get; set; }
/// <summary>
/// 客户端连接发送消息后回调
/// </summary>
public Action<byte[], WebSocketSharp.Net.WebSockets.WebSocketContext> HandleWebSocketSendMsg { get; set; }
/// <summary>
/// 客户端连接关闭后回调
/// </summary>
public Action<WebSocketSharp.Net.WebSockets.WebSocketContext> HandleWebSocketClientClose { get; set; }
#endregion
}
public class Laputa : WebSocketBehavior
{
protected override void OnOpen()
{
Browser.Instance. LogMessage( Context.UserEndPoint.ToString() + " was accepted" + " Websocket ID is " + ID);
Browser.Instance.RefreshDataGrid(Sessions);
Browser.Instance.HandleNewWebSocketClientConnected?.Invoke(Context);
base.OnOpen();
}
protected override void OnClose(CloseEventArgs e)
{
Browser.Instance.LogMessage("Websocket "+this.ID + " was closed");
Browser.Instance.RefreshDataGrid(Sessions);
Browser.Instance.HandleWebSocketClientClose?.Invoke(Context);
base.OnClose(e);
}
protected override void OnMessage(MessageEventArgs e)
{
string s_data = e.Data;
Logger log = new Logger();
//Browser.Instance.LogMessage(Context.UserEndPoint.ToString() + " is Saying: " + e.Data);
Browser.Instance.HandleWebSocketRecMsg?.Invoke(System.Text.Encoding.Default.GetBytes(e.Data), Context);
//Console.Write(e.Data);
Browser.Instance.SetOutput(Context.UserEndPoint.ToString()+" received " +e.Data);
}
}
}

View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<ClassDiagram />

View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<ClassDiagram MajorVersion="1" MinorVersion="1">
<Class Name="Miuser.NUDP.Sockets.SocketServer">
<Position X="0.5" Y="0.5" Width="2.75" />
<Members>
<Property Name="_clientList" Hidden="true" />
<Property Name="_ip" Hidden="true" />
<Property Name="_isListen" Hidden="true" />
<Property Name="_port" Hidden="true" />
<Method Name="EndReceiveFrom" Hidden="true" />
<Method Name="HandleConnClientClose" Hidden="true" />
<Field Name="Instance" Hidden="true" />
<Property Name="IsListening" Hidden="true" />
<Method Name="RemoveConnection" Hidden="true" />
<Method Name="RemoveInactiveConnection" Hidden="true" />
<Method Name="RemoveInactiveConnections" Hidden="true" />
<Property Name="RWLock_ClientList" Hidden="true" />
<Method Name="SocketServer" Hidden="true" />
</Members>
<NestedTypes>
<Class Name="Miuser.NUDP.Sockets.SocketServer.UdpState" Collapsed="true">
<TypeIdentifier>
<NewMemberFileName>Device\SocketServer.cs</NewMemberFileName>
</TypeIdentifier>
</Class>
</NestedTypes>
<TypeIdentifier>
<HashCode>gBQAIQAAAAIEUAggQEBgABAAAkAAAQAAQAiAQAABAAg=</HashCode>
<FileName>Device\SocketServer.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="Miuser.NUDP.Sockets.SocketConnection">
<Position X="3.5" Y="0.5" Width="2.5" />
<Members>
<Field Name="_server" Hidden="true" />
<Method Name="InitTimer" Hidden="true" />
<Field Name="LIFELIMIT" Hidden="true" />
<Method Name="SocketConnection" Hidden="true" />
<Field Name="timer" Hidden="true" />
<Method Name="TimerUp" Hidden="true" />
</Members>
<TypeIdentifier>
<HashCode>wAQEAAAAAAAgQAAAUAAAAAAAAAAAAgAAAAoAAAAACAQ=</HashCode>
<FileName>Device\SocketConnection.cs</FileName>
</TypeIdentifier>
</Class>
<Font Name="Microsoft YaHei UI" Size="9" />
</ClassDiagram>

View File

@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{5DF7AFDF-6B88-42AD-B855-4EB9BF0E2495}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Coldairarrow.Util.Sockets</RootNamespace>
<AssemblyName>Coldairarrow.Util.Sockets</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Debug\Coldairarrow.Util.Sockets.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SocketClient.cs" />
<Compile Include="SocketConnection.cs" />
<Compile Include="SocketServer.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("01.Coldairarrow.Util.Sockets")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("01.Coldairarrow.Util.Sockets")]
[assembly: AssemblyCopyright("Copyright © 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// 将 ComVisible 设置为 false 会使此程序集中的类型
//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
//请将此类型的 ComVisible 特性设置为 true。
[assembly: ComVisible(false)]
// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
[assembly: Guid("5df7afdf-6b88-42ad-b855-4eb9bf0e2495")]
// 程序集的版本信息由下列四个值组成:
//
// 主版本
// 次版本
// 生成号
// 修订号
//
// 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号
//通过使用 "*",如下所示:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -0,0 +1,241 @@
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace Coldairarrow.Util.Sockets
{
/// <summary>
/// Socket客户端
/// </summary>
public class SocketClient
{
#region
/// <summary>
/// 构造函数,连接服务器IP地址默认为本机127.0.0.1
/// </summary>
/// <param name="port">监听的端口</param>
public SocketClient(int port)
{
_ip = "127.0.0.1";
_port = port;
}
/// <summary>
/// 构造函数
/// </summary>
/// <param name="ip">监听的IP地址</param>
/// <param name="port">监听的端口</param>
public SocketClient(string ip, int port)
{
_ip = ip;
_port = port;
}
#endregion
#region
private Socket _socket = null;
private string _ip = "";
private int _port = 0;
private bool _isRec=true;
private bool IsSocketConnected()
{
bool part1 = _socket.Poll(1000, SelectMode.SelectRead);
bool part2 = (_socket.Available == 0);
if (part1 && part2)
return false;
else
return true;
}
/// <summary>
/// 开始接受客户端消息
/// </summary>
public void StartRecMsg()
{
try
{
byte[] container = new byte[1024 * 1024 * 2];
_socket.BeginReceive(container, 0, container.Length, SocketFlags.None, asyncResult =>
{
try
{
int length = _socket.EndReceive(asyncResult);
//马上进行下一轮接受,增加吞吐量
if (length > 0 && _isRec && IsSocketConnected())
StartRecMsg();
if (length > 0)
{
byte[] recBytes = new byte[length];
Array.Copy(container, 0, recBytes, 0, length);
//处理消息
HandleRecMsg?.BeginInvoke(recBytes, this,null,null);
}
else
Close();
}
catch (Exception ex)
{
HandleException?.BeginInvoke(ex,null,null);
Close();
}
}, null);
}
catch (Exception ex)
{
HandleException?.BeginInvoke(ex,null,null);
Close();
}
}
#endregion
#region
/// <summary>
/// 开始服务,连接服务端
/// </summary>
public void StartClient()
{
try
{
//实例化 套接字 ip4寻址协议流式传输TCP协议
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//创建 ip对象
IPAddress address = IPAddress.Parse(_ip);
//创建网络节点对象 包含 ip和port
IPEndPoint endpoint = new IPEndPoint(address, _port);
//将 监听套接字 绑定到 对应的IP和端口
_socket.BeginConnect(endpoint, asyncResult =>
{
try
{
_socket.EndConnect(asyncResult);
//开始接受服务器消息
StartRecMsg();
HandleClientStarted?.BeginInvoke(this,null,null);
}
catch (Exception ex)
{
HandleException?.BeginInvoke(ex,null,null);
}
}, null);
}
catch (Exception ex)
{
HandleException?.BeginInvoke(ex,null,null);
}
}
/// <summary>
/// 发送数据
/// </summary>
/// <param name="bytes">数据字节</param>
public void Send(byte[] bytes)
{
try
{
_socket.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, asyncResult =>
{
try
{
int length = _socket.EndSend(asyncResult);
HandleSendMsg?.BeginInvoke(bytes, this,null,null);
}
catch (Exception ex)
{
HandleException?.BeginInvoke(ex,null,null);
}
}, null);
}
catch (Exception ex)
{
HandleException?.BeginInvoke(ex,null,null);
}
}
/// <summary>
/// 发送字符串默认使用UTF-8编码
/// </summary>
/// <param name="msgStr">字符串</param>
public void Send(string msgStr)
{
Send(Encoding.UTF8.GetBytes(msgStr));
}
/// <summary>
/// 发送字符串(使用自定义编码)
/// </summary>
/// <param name="msgStr">字符串消息</param>
/// <param name="encoding">使用的编码</param>
public void Send(string msgStr, Encoding encoding)
{
Send(encoding.GetBytes(msgStr));
}
/// <summary>
/// 传入自定义属性
/// </summary>
public object Property { get; set; }
/// <summary>
/// 关闭与服务器的连接
/// </summary>
public void Close()
{
try
{
_isRec = false;
_socket.Disconnect(false);
HandleClientClose?.BeginInvoke(this,null,null);
}
catch (Exception ex)
{
HandleException?.BeginInvoke(ex,null,null);
}
finally
{
_socket.Dispose();
GC.Collect();
}
}
#endregion
#region
/// <summary>
/// 客户端连接建立后回调
/// </summary>
public Action<SocketClient> HandleClientStarted { get; set; }
/// <summary>
/// 处理接受消息的委托
/// </summary>
public Action<byte[], SocketClient> HandleRecMsg { get; set; }
/// <summary>
/// 客户端连接发送消息后回调
/// </summary>
public Action<byte[], SocketClient> HandleSendMsg { get; set; }
/// <summary>
/// 客户端连接关闭后回调
/// </summary>
public Action<SocketClient> HandleClientClose { get; set; }
/// <summary>
/// 异常处理程序
/// </summary>
public Action<Exception> HandleException { get; set; }
#endregion
}
}

View File

@@ -0,0 +1,195 @@
using System;
using System.Net.Sockets;
using System.Text;
namespace Coldairarrow.Util.Sockets
{
/// <summary>
/// Socket连接,双向通信
/// </summary>
public class SocketConnection
{
#region
/// <summary>
/// 构造函数
/// </summary>
/// <param name="socket">维护的Socket对象</param>
/// <param name="server">维护此连接的服务对象</param>
public SocketConnection(Socket socket,SocketServer server)
{
_socket = socket;
_server = server;
}
#endregion
#region
private readonly Socket _socket;
private bool _isRec=true;
private SocketServer _server = null;
private bool IsSocketConnected()
{
bool part1 = _socket.Poll(1000, SelectMode.SelectRead);
bool part2 = (_socket.Available == 0);
if (part1 && part2)
return false;
else
return true;
}
#endregion
#region
/// <summary>
/// 开始接受客户端消息
/// </summary>
public void StartRecMsg()
{
try
{
byte[] container = new byte[1024 * 1024 * 4];
_socket.BeginReceive(container, 0, container.Length, SocketFlags.None, asyncResult =>
{
try
{
int length = _socket.EndReceive(asyncResult);
//马上进行下一轮接受,增加吞吐量
if (length > 0 && _isRec && IsSocketConnected())
StartRecMsg();
if (length > 0)
{
byte[] recBytes = new byte[length];
Array.Copy(container, 0, recBytes, 0, length);
try
{
//处理消息
HandleRecMsg?.BeginInvoke(recBytes, this, _server, null, null);
}
catch (Exception ex)
{
HandleException?.Invoke(ex);
}
}
else
Close();
}
catch (Exception ex)
{
HandleException?.BeginInvoke(ex,null,null);
Close();
}
}, null);
}
catch (Exception ex)
{
HandleException?.BeginInvoke(ex,null,null);
Close();
}
}
/// <summary>
/// 发送数据
/// </summary>
/// <param name="bytes">数据字节</param>
public void Send(byte[] bytes)
{
try
{
_socket.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, asyncResult =>
{
try
{
int length = _socket.EndSend(asyncResult);
HandleSendMsg?.BeginInvoke(bytes, this, _server,null,null);
}
catch (Exception ex)
{
HandleException?.BeginInvoke(ex,null,null);
}
}, null);
}
catch (Exception ex)
{
HandleException?.BeginInvoke(ex,null,null);
}
}
/// <summary>
/// 发送字符串默认使用UTF-8编码
/// </summary>
/// <param name="msgStr">字符串</param>
public void Send(string msgStr)
{
Send(Encoding.UTF8.GetBytes(msgStr));
}
/// <summary>
/// 发送字符串(使用自定义编码)
/// </summary>
/// <param name="msgStr">字符串消息</param>
/// <param name="encoding">使用的编码</param>
public void Send(string msgStr,Encoding encoding)
{
Send(encoding.GetBytes(msgStr));
}
/// <summary>
/// 传入自定义属性
/// </summary>
public object Property { get; set; }
/// <summary>
/// 关闭当前连接
/// </summary>
public void Close()
{
try
{
_isRec = false;
_socket.Disconnect(false);
_server.RemoveConnection(this);
HandleClientClose?.BeginInvoke(this, _server,null,null);
}
catch (Exception ex)
{
HandleException?.BeginInvoke(ex,null,null);
}
finally
{
_socket.Dispose();
GC.Collect();
}
}
#endregion
#region
/// <summary>
/// 客户端连接接受新的消息后调用
/// </summary>
public Action<byte[], SocketConnection, SocketServer> HandleRecMsg { get; set; }
/// <summary>
/// 客户端连接发送消息后回调
/// </summary>
public Action<byte[], SocketConnection, SocketServer> HandleSendMsg { get; set; }
/// <summary>
/// 客户端连接关闭后回调
/// </summary>
public Action<SocketConnection, SocketServer> HandleClientClose { get; set; }
/// <summary>
/// 异常处理程序
/// </summary>
public Action<Exception> HandleException { get; set; }
#endregion
}
}

View File

@@ -0,0 +1,275 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace Coldairarrow.Util.Sockets
{
/// <summary>
/// Socket服务端
/// </summary>
public class SocketServer
{
#region
/// <summary>
/// 构造函数
/// </summary>
/// <param name="ip">监听的IP地址</param>
/// <param name="port">监听的端口</param>
public SocketServer(string ip, int port)
{
_ip = ip;
_port = port;
}
/// <summary>
/// 构造函数,监听IP地址默认为本机0.0.0.0
/// </summary>
/// <param name="port">监听的端口</param>
public SocketServer(int port)
{
_ip = "0.0.0.0";
_port = port;
}
#endregion
#region
private Socket _socket { get; set; } = null;
private string _ip { get; set; } = "";
private int _port { get; set; } = 0;
private bool _isListen { get; set; } = true;
private void StartListen()
{
try
{
_socket.BeginAccept(asyncResult =>
{
try
{
Socket newSocket = _socket.EndAccept(asyncResult);
//马上进行下一轮监听,增加吞吐量
if (_isListen)
StartListen();
SocketConnection newConnection = new SocketConnection(newSocket, this)
{
HandleRecMsg = HandleRecMsg == null ? null : new Action<byte[], SocketConnection, SocketServer>(HandleRecMsg),
HandleClientClose = HandleClientClose == null ? null : new Action<SocketConnection, SocketServer>(HandleClientClose),
HandleSendMsg = HandleSendMsg == null ? null : new Action<byte[], SocketConnection, SocketServer>(HandleSendMsg),
HandleException = HandleException == null ? null : new Action<Exception>(HandleException)
};
newConnection.StartRecMsg();
AddConnection(newConnection);
HandleNewClientConnected?.BeginInvoke(this, newConnection, null, null);
}
catch (Exception ex)
{
HandleException?.BeginInvoke(ex, null, null);
}
}, null);
}
catch (Exception ex)
{
HandleException?.BeginInvoke(ex, null, null);
}
}
private LinkedList<SocketConnection> _clientList { get; } = new LinkedList<SocketConnection>();
#endregion
#region
/// <summary>
/// 开始服务,监听客户端
/// </summary>
public void StartServer()
{
try
{
//实例化套接字ip4寻址协议流式传输TCP协议
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//创建ip对象
IPAddress address = IPAddress.Parse(_ip);
//创建网络节点对象包含ip和port
IPEndPoint endpoint = new IPEndPoint(address, _port);
//将 监听套接字绑定到 对应的IP和端口
_socket.Bind(endpoint);
//设置监听队列长度为Int32最大值(同时能够处理连接请求数量)
_socket.Listen(int.MaxValue);
//开始监听客户端
StartListen();
HandleServerStarted?.BeginInvoke(this, null, null);
}
catch (Exception ex)
{
HandleException?.BeginInvoke(ex, null, null);
}
}
/// <summary>
/// 维护客户端列表的读写锁
/// </summary>
public ReaderWriterLockSlim RWLock_ClientList { get; } = new ReaderWriterLockSlim();
/// <summary>
/// 关闭指定客户端连接
/// </summary>
/// <param name="theConnection">指定的客户端连接</param>
public void CloseConnection(SocketConnection theConnection)
{
theConnection.Close();
}
/// <summary>
/// 添加客户端连接
/// </summary>
/// <param name="theConnection">需要添加的客户端连接</param>
public void AddConnection(SocketConnection theConnection)
{
RWLock_ClientList.EnterWriteLock();
try
{
_clientList.AddLast(theConnection);
}
finally
{
RWLock_ClientList.ExitWriteLock();
}
}
/// <summary>
/// 删除指定的客户端连接
/// </summary>
/// <param name="theConnection">指定的客户端连接</param>
public void RemoveConnection(SocketConnection theConnection)
{
RWLock_ClientList.EnterWriteLock();
try
{
_clientList.Remove(theConnection);
}
finally
{
RWLock_ClientList.ExitWriteLock();
}
}
/// <summary>
/// 通过条件获取客户端连接列表
/// </summary>
/// <param name="predicate">筛选条件</param>
/// <returns></returns>
public IEnumerable<SocketConnection> GetConnectionList(Func<SocketConnection, bool> predicate)
{
RWLock_ClientList.EnterReadLock();
try
{
return _clientList.Where(predicate);
}
finally
{
RWLock_ClientList.ExitReadLock();
}
}
/// <summary>
/// 获取所有客户端连接列表
/// </summary>
/// <returns></returns>
public IEnumerable<SocketConnection> GetConnectionList()
{
return _clientList;
}
/// <summary>
/// 寻找特定条件的客户端连接
/// </summary>
/// <param name="predicate">筛选条件</param>
/// <returns></returns>
public SocketConnection GetTheConnection(Func<SocketConnection, bool> predicate)
{
RWLock_ClientList.EnterReadLock();
try
{
return _clientList.Where(predicate).FirstOrDefault();
}
finally
{
RWLock_ClientList.ExitReadLock();
}
}
/// <summary>
/// 获取客户端连接数
/// </summary>
/// <returns></returns>
public int GetConnectionCount()
{
RWLock_ClientList.EnterReadLock();
try
{
return _clientList.Count;
}
finally
{
RWLock_ClientList.ExitReadLock();
}
}
#endregion
#region
/// <summary>
/// 异常处理程序
/// </summary>
public Action<Exception> HandleException { get; set; }
#endregion
#region
/// <summary>
/// 服务启动后执行
/// </summary>
public Action<SocketServer> HandleServerStarted { get; set; }
/// <summary>
/// 当新客户端连接后执行
/// </summary>
public Action<SocketServer, SocketConnection> HandleNewClientConnected { get; set; }
/// <summary>
/// 服务端关闭客户端后执行
/// </summary>
public Action<SocketServer, SocketConnection> HandleCloseClient { get; set; }
#endregion
#region
/// <summary>
/// 客户端连接接受新的消息后调用
/// </summary>
public Action<byte[], SocketConnection, SocketServer> HandleRecMsg { get; set; }
/// <summary>
/// 客户端连接发送消息后回调
/// </summary>
public Action<byte[], SocketConnection, SocketServer> HandleSendMsg { get; set; }
/// <summary>
/// 客户端连接关闭后回调
/// </summary>
public Action<SocketConnection, SocketServer> HandleClientClose { get; set; }
#endregion
}
}

View File

@@ -0,0 +1,3 @@
https://github.com/Coldairarrow/Sockets
ԭ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ΪTCP/IPЭ<50><EFBFBD><E9A3AC><EFBFBD>޸<EFBFBD>ΪUDPЭ<50><D0AD>

192
UdpPlugWebsocket/Device/Device.Designer.cs generated Normal file
View File

@@ -0,0 +1,192 @@
namespace UdpPlugWebsocket
{
partial class Device
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.groupBox1 = new System.Windows.Forms.GroupBox();
this.dgv_devices = new System.Windows.Forms.DataGridView();
this.panel1 = new System.Windows.Forms.Panel();
this.txt_Send = new System.Windows.Forms.TextBox();
this.btn_Send = new System.Windows.Forms.Button();
this.splitter1 = new System.Windows.Forms.Splitter();
this.groupBox2 = new System.Windows.Forms.GroupBox();
this.richTextBox1 = new System.Windows.Forms.RichTextBox();
this.dataGridView1 = new System.Windows.Forms.DataGridView();
this.groupBox1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.dgv_devices)).BeginInit();
this.panel1.SuspendLayout();
this.groupBox2.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit();
this.SuspendLayout();
//
// groupBox1
//
this.groupBox1.Controls.Add(this.dgv_devices);
this.groupBox1.Dock = System.Windows.Forms.DockStyle.Fill;
this.groupBox1.Font = new System.Drawing.Font("微软雅黑", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.groupBox1.Location = new System.Drawing.Point(0, 0);
this.groupBox1.Name = "groupBox1";
this.groupBox1.Size = new System.Drawing.Size(872, 614);
this.groupBox1.TabIndex = 3;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "终端设备:";
this.groupBox1.Enter += new System.EventHandler(this.groupBox1_Enter);
//
// dgv_devices
//
this.dgv_devices.AllowUserToAddRows = false;
this.dgv_devices.BackgroundColor = System.Drawing.Color.WhiteSmoke;
this.dgv_devices.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.dgv_devices.Dock = System.Windows.Forms.DockStyle.Fill;
this.dgv_devices.GridColor = System.Drawing.Color.Snow;
this.dgv_devices.Location = new System.Drawing.Point(3, 25);
this.dgv_devices.MultiSelect = false;
this.dgv_devices.Name = "dgv_devices";
this.dgv_devices.ReadOnly = true;
this.dgv_devices.RowHeadersVisible = false;
this.dgv_devices.RowHeadersWidth = 4;
this.dgv_devices.RowTemplate.Height = 23;
this.dgv_devices.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect;
this.dgv_devices.Size = new System.Drawing.Size(866, 586);
this.dgv_devices.TabIndex = 0;
this.dgv_devices.SelectionChanged += new System.EventHandler(this.dgv_devices_SelectionChanged);
//
// panel1
//
this.panel1.Controls.Add(this.txt_Send);
this.panel1.Controls.Add(this.btn_Send);
this.panel1.Dock = System.Windows.Forms.DockStyle.Bottom;
this.panel1.Location = new System.Drawing.Point(0, 614);
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size(872, 25);
this.panel1.TabIndex = 4;
//
// txt_Send
//
this.txt_Send.Dock = System.Windows.Forms.DockStyle.Fill;
this.txt_Send.Font = new System.Drawing.Font("宋体", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.txt_Send.Location = new System.Drawing.Point(0, 0);
this.txt_Send.Name = "txt_Send";
this.txt_Send.Size = new System.Drawing.Size(786, 26);
this.txt_Send.TabIndex = 0;
//
// btn_Send
//
this.btn_Send.Dock = System.Windows.Forms.DockStyle.Right;
this.btn_Send.Location = new System.Drawing.Point(786, 0);
this.btn_Send.Name = "btn_Send";
this.btn_Send.Size = new System.Drawing.Size(86, 25);
this.btn_Send.TabIndex = 1;
this.btn_Send.Text = "发送";
this.btn_Send.UseVisualStyleBackColor = true;
this.btn_Send.Click += new System.EventHandler(this.btn_Send_Click);
//
// splitter1
//
this.splitter1.Dock = System.Windows.Forms.DockStyle.Bottom;
this.splitter1.Location = new System.Drawing.Point(0, 382);
this.splitter1.Name = "splitter1";
this.splitter1.Size = new System.Drawing.Size(872, 10);
this.splitter1.TabIndex = 9;
this.splitter1.TabStop = false;
//
// groupBox2
//
this.groupBox2.Controls.Add(this.richTextBox1);
this.groupBox2.Controls.Add(this.dataGridView1);
this.groupBox2.Dock = System.Windows.Forms.DockStyle.Bottom;
this.groupBox2.Font = new System.Drawing.Font("微软雅黑", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.groupBox2.Location = new System.Drawing.Point(0, 392);
this.groupBox2.Name = "groupBox2";
this.groupBox2.Size = new System.Drawing.Size(872, 222);
this.groupBox2.TabIndex = 8;
this.groupBox2.TabStop = false;
this.groupBox2.Text = "接收:";
//
// richTextBox1
//
this.richTextBox1.Dock = System.Windows.Forms.DockStyle.Fill;
this.richTextBox1.Location = new System.Drawing.Point(3, 25);
this.richTextBox1.Name = "richTextBox1";
this.richTextBox1.Size = new System.Drawing.Size(866, 194);
this.richTextBox1.TabIndex = 5;
this.richTextBox1.Text = "";
//
// dataGridView1
//
this.dataGridView1.AllowUserToAddRows = false;
this.dataGridView1.BackgroundColor = System.Drawing.Color.WhiteSmoke;
this.dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.dataGridView1.Dock = System.Windows.Forms.DockStyle.Fill;
this.dataGridView1.GridColor = System.Drawing.Color.Snow;
this.dataGridView1.Location = new System.Drawing.Point(3, 25);
this.dataGridView1.MultiSelect = false;
this.dataGridView1.Name = "dataGridView1";
this.dataGridView1.ReadOnly = true;
this.dataGridView1.RowHeadersVisible = false;
this.dataGridView1.RowHeadersWidth = 4;
this.dataGridView1.RowTemplate.Height = 23;
this.dataGridView1.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect;
this.dataGridView1.Size = new System.Drawing.Size(866, 194);
this.dataGridView1.TabIndex = 0;
//
// Device
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(872, 639);
this.Controls.Add(this.splitter1);
this.Controls.Add(this.groupBox2);
this.Controls.Add(this.groupBox1);
this.Controls.Add(this.panel1);
this.Name = "Device";
this.Text = "Device";
this.Load += new System.EventHandler(this.Device_Load);
this.groupBox1.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.dgv_devices)).EndInit();
this.panel1.ResumeLayout(false);
this.panel1.PerformLayout();
this.groupBox2.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.GroupBox groupBox1;
private System.Windows.Forms.DataGridView dgv_devices;
private System.Windows.Forms.Panel panel1;
private System.Windows.Forms.TextBox txt_Send;
private System.Windows.Forms.Button btn_Send;
private System.Windows.Forms.Splitter splitter1;
private System.Windows.Forms.GroupBox groupBox2;
private System.Windows.Forms.RichTextBox richTextBox1;
private System.Windows.Forms.DataGridView dataGridView1;
}
}

View File

@@ -0,0 +1,188 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Windows.Forms;
using Miuser.NUDP.Sockets;
namespace UdpPlugWebsocket
{
public partial class Device : Form
{
string s_output;
//本UDP服务对象
public SocketServer server;
//人工选中的连接对象
public SocketConnection curr;
public Device()
{
InitializeComponent();
server = new SocketServer(SetupForm.cfg.UDPPort);
//处理从客户端收到的消息
server.HandleRecMsg = new Action<byte[], SocketConnection, SocketServer>((bytes, client, theServer) =>
{
string msg = Encoding.UTF8.GetString(bytes);
SetOutput(client.Tag+$" 收到消息:{msg}");
});
//处理服务器启动后事件
server.HandleServerStarted = new Action<SocketServer>(theServer =>
{
LogMessage("UDP Server 服务已启动************");
});
//处理新的客户端连接后的事件
server.HandleNewClientConnected = new Action<SocketServer, SocketConnection>((theServer, theCon) =>
{
LogMessage(theCon.Tag+ $@" UDP客户端接入当前连接数{theServer.GetConnectionCount()}");
RefreshDataGrid(theServer);
});
//处理客户端连接关闭后的事件
server.HandleClientClose = new Action<SocketConnection, SocketServer>((theCon, theServer) =>
{
LogMessage(theCon.Tag + $@" UDP客户端关闭当前连接数为{theServer.GetConnectionCount()}");
RefreshDataGrid(theServer);
});
//处理异常
server.HandleException = new Action<Exception>(ex =>
{
LogError(ex.Message+" "+ex.StackTrace);
});
server.StartServer();
}
private void Device_Load(object sender, EventArgs e)
{
}
private void RefreshDataGrid(SocketServer theServer)
{
DataTable dt = GetConnectionTable(theServer);
lock (dt)
{
this.Invoke(new Action(() =>
{
dgv_devices.DataSource = dt;
dgv_devices.Columns[0].Width = 200;
}));
}
}
private DataTable GetConnectionTable(SocketServer server)
{
DataTable dt = new DataTable();
dt.Columns.Add("Tag");
dt.Columns.Add("IP");
dt.Columns.Add("UDP");
dt.Columns.Add("Lifetime");
IEnumerable<SocketConnection> connections = server.GetConnectionList();
lock (connections)
{
foreach (SocketConnection conn in server.GetConnectionList().ToArray())
{
DataRow dr = dt.NewRow();
dr[0] = conn.Tag;
dr[1] = conn._endpoint.Address;
dr[2] = conn._endpoint.Port;
dr[3] = conn._lifetime;
dt.Rows.Add(dr);
}
}
return dt;
}
private static Device _instance;
public static Device Instance
{
get
{
if (_instance == null)
_instance = new Device();
return _instance;
}
private set { _instance = value; }
}
private void btn_Send_Click(object sender, EventArgs e)
{
curr?.Send(txt_Send.Text);
}
private void dgv_devices_SelectionChanged(object sender, EventArgs e)
{
if (dgv_devices.SelectedRows.Count > 0)
{
string tag = dgv_devices.SelectedRows[0].Cells["Tag"].Value.ToString();
curr = server.GetTheConnection(x =>
{
var Id = (string)x.Tag;
return Id == tag;
});
}else
{
curr = null;
}
}
public void LogMessage(string message)
{
HandleMessage?.Invoke(message);
}
public void LogError(string message)
{
HandleError?.Invoke(message);
}
#region
/// <summary>
/// 消息处理程序
/// </summary>
public Action<string> HandleMessage { get; set; }
/// <summary>
/// 异常处理程序
/// </summary>
public Action<string> HandleError { get; set; }
#endregion
public void SetOutput(string text)
{
//决定是否屏显
if (SetupForm.cfg.EnableScreenLog==false) return;
text = DateTime.Now.ToLongDateString() + " " + DateTime.Now.ToLongTimeString() + " |" + text;
this.Invoke(new Action(() =>
{
s_output = s_output + text.Replace("\0", "") + "\r";
if ((s_output.Length) > 5000)
{
s_output = s_output.Substring(s_output.Length - 5000, 5000);
}
//滚到最后
this.richTextBox1.Text = s_output;
this.richTextBox1.Select(richTextBox1.TextLength, 0);
//this.richTextBox1.Focus();
this.richTextBox1.ScrollToCaret();
}));
}
private void groupBox1_Enter(object sender, EventArgs e)
{
}
}
}

View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@@ -0,0 +1,241 @@
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace Coldairarrow.Util.Sockets
{
/// <summary>
/// Socket客户端
/// </summary>
public class SocketClient
{
#region
/// <summary>
/// 构造函数,连接服务器IP地址默认为本机127.0.0.1
/// </summary>
/// <param name="port">监听的端口</param>
public SocketClient(int port)
{
_ip = "127.0.0.1";
_port = port;
}
/// <summary>
/// 构造函数
/// </summary>
/// <param name="ip">监听的IP地址</param>
/// <param name="port">监听的端口</param>
public SocketClient(string ip, int port)
{
_ip = ip;
_port = port;
}
#endregion
#region
private Socket _socket = null;
private string _ip = "";
private int _port = 0;
private bool _isRec=true;
private bool IsSocketConnected()
{
bool part1 = _socket.Poll(1000, SelectMode.SelectRead);
bool part2 = (_socket.Available == 0);
if (part1 && part2)
return false;
else
return true;
}
/// <summary>
/// 开始接受客户端消息
/// </summary>
public void StartRecMsg()
{
try
{
byte[] container = new byte[1024 * 1024 * 2];
_socket.BeginReceive(container, 0, container.Length, SocketFlags.None, asyncResult =>
{
try
{
int length = _socket.EndReceive(asyncResult);
//马上进行下一轮接受,增加吞吐量
if (length > 0 && _isRec && IsSocketConnected())
StartRecMsg();
if (length > 0)
{
byte[] recBytes = new byte[length];
Array.Copy(container, 0, recBytes, 0, length);
//处理消息
HandleRecMsg?.BeginInvoke(recBytes, this,null,null);
}
else
Close();
}
catch (Exception ex)
{
HandleException?.BeginInvoke(ex,null,null);
Close();
}
}, null);
}
catch (Exception ex)
{
HandleException?.BeginInvoke(ex,null,null);
Close();
}
}
#endregion
#region
/// <summary>
/// 开始服务,连接服务端
/// </summary>
public void StartClient()
{
try
{
//实例化 套接字 ip4寻址协议流式传输TCP协议
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//创建 ip对象
IPAddress address = IPAddress.Parse(_ip);
//创建网络节点对象 包含 ip和port
IPEndPoint endpoint = new IPEndPoint(address, _port);
//将 监听套接字 绑定到 对应的IP和端口
_socket.BeginConnect(endpoint, asyncResult =>
{
try
{
_socket.EndConnect(asyncResult);
//开始接受服务器消息
StartRecMsg();
HandleClientStarted?.BeginInvoke(this,null,null);
}
catch (Exception ex)
{
HandleException?.BeginInvoke(ex,null,null);
}
}, null);
}
catch (Exception ex)
{
HandleException?.BeginInvoke(ex,null,null);
}
}
/// <summary>
/// 发送数据
/// </summary>
/// <param name="bytes">数据字节</param>
public void Send(byte[] bytes)
{
try
{
_socket.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, asyncResult =>
{
try
{
int length = _socket.EndSend(asyncResult);
HandleSendMsg?.BeginInvoke(bytes, this,null,null);
}
catch (Exception ex)
{
HandleException?.BeginInvoke(ex,null,null);
}
}, null);
}
catch (Exception ex)
{
HandleException?.BeginInvoke(ex,null,null);
}
}
/// <summary>
/// 发送字符串默认使用UTF-8编码
/// </summary>
/// <param name="msgStr">字符串</param>
public void Send(string msgStr)
{
Send(Encoding.UTF8.GetBytes(msgStr));
}
/// <summary>
/// 发送字符串(使用自定义编码)
/// </summary>
/// <param name="msgStr">字符串消息</param>
/// <param name="encoding">使用的编码</param>
public void Send(string msgStr, Encoding encoding)
{
Send(encoding.GetBytes(msgStr));
}
/// <summary>
/// 传入自定义属性
/// </summary>
public object Property { get; set; }
/// <summary>
/// 关闭与服务器的连接
/// </summary>
public void Close()
{
try
{
_isRec = false;
_socket.Disconnect(false);
HandleClientClose?.BeginInvoke(this,null,null);
}
catch (Exception ex)
{
HandleException?.BeginInvoke(ex,null,null);
}
finally
{
_socket.Dispose();
GC.Collect();
}
}
#endregion
#region
/// <summary>
/// 客户端连接建立后回调
/// </summary>
public Action<SocketClient> HandleClientStarted { get; set; }
/// <summary>
/// 处理接受消息的委托
/// </summary>
public Action<byte[], SocketClient> HandleRecMsg { get; set; }
/// <summary>
/// 客户端连接发送消息后回调
/// </summary>
public Action<byte[], SocketClient> HandleSendMsg { get; set; }
/// <summary>
/// 客户端连接关闭后回调
/// </summary>
public Action<SocketClient> HandleClientClose { get; set; }
/// <summary>
/// 异常处理程序
/// </summary>
public Action<Exception> HandleException { get; set; }
#endregion
}
}

View File

@@ -0,0 +1,153 @@
using System;
using System.Net.Sockets;
using System.Text;
using System.Linq;
using System.Net;
using UdpPlugWebsocket;
namespace Miuser.NUDP.Sockets
{
/// <summary>
/// Socket连接,双向通信
/// </summary>
public class SocketConnection
{
/// <summary>
/// 构造函数
/// </summary>
/// <param name="socket">维护的Socket对象</param>
/// <param name="server">维护此连接的服务对象</param>
public SocketConnection(IPEndPoint endpoint,SocketServer server)
{
_endpoint = endpoint;
_server = server;
InitTimer();
IsActive = true;
}
#region
//UDP连接生存时间
private static int LIFELIMIT =SetupForm.cfg.UDPTTL;
//定义Timer类
private System.Timers.Timer timer;
#endregion
#region
/// <summary>
/// 初始化Timer控件
/// </summary>
private void InitTimer()
{
//设置定时间隔(毫秒为单位)
int interval = 1000;
timer = new System.Timers.Timer(interval);
//设置执行一次false还是一直执行(true)
timer.AutoReset = true;
//设置是否执行System.Timers.Timer.Elapsed事件
timer.Enabled = true;
//绑定Elapsed事件
timer.Elapsed += new System.Timers.ElapsedEventHandler(TimerUp);
}
/// <summary>
/// Timer类执行定时到点事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void TimerUp(object sender, System.Timers.ElapsedEventArgs e)
{
_lifetime -= (int)((System.Timers.Timer)(sender)).Interval;
if (_lifetime <= 0)
{
IsActive = false;
HandleClientClose?.Invoke(this, _server);
timer.Dispose();
}
}
#endregion
#region
//连接字符
public readonly IPEndPoint _endpoint;
//服务端
public SocketServer _server = null;
//剩余生存时间
public int _lifetime = LIFELIMIT;
#endregion
#region
/// <summary>
/// 发送数据
/// </summary>
/// <param name="bytes">数据字节</param>
public void Send(byte[] bytes)
{
if (_server == null) return;
try
{
_server._socket.SendTo(bytes, _endpoint);
HandleSendMsg?.Invoke(bytes,this, _server);
}
catch (SocketException ex)
{
HandleException?.BeginInvoke(ex, null, null);
}
}
/// <summary>
/// 发送字符串默认使用UTF-8编码
/// </summary>
/// <param name="msgStr">字符串</param>
public void Send(string msgStr)
{
Send(Encoding.UTF8.GetBytes(msgStr));
}
/// <summary>
/// 发送字符串(使用自定义编码)
/// </summary>
/// <param name="msgStr">字符串消息</param>
/// <param name="encoding">使用的编码</param>
public void Send(string msgStr,Encoding encoding)
{
Send(encoding.GetBytes(msgStr));
}
/// <summary>
/// 传入自定义属性
/// </summary>
public object Tag { get; set; }
/// <summary>
/// 连接是否仍然活着
/// </summary>
public bool IsActive { get; set; }
/// <summary>
/// 重新激活连接,连接生命加满
/// </summary>
public void Reactive()
{
_lifetime = LIFELIMIT;
}
#endregion
#region
/// <summary>
/// 客户端连接发送消息后回调
/// </summary>
public Action<byte[], SocketConnection, SocketServer> HandleSendMsg { get; set; }
/// <summary>
/// 异常处理程序
/// </summary>
public Action<Exception> HandleException { get; set; }
/// <summary>
/// 客户端连接关闭后回调
/// </summary>
public Action<SocketConnection, SocketServer> HandleClientClose { get; set; }
#endregion
}
}

View File

@@ -0,0 +1,369 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Data;
namespace Miuser.NUDP.Sockets
{
/// <summary>
/// Socket服务端
/// </summary>
public class SocketServer
{
/// <summary>
/// 构造函数
/// </summary>
/// <param name="ip">监听的IP地址</param>
/// <param name="port">监听的端口</param>
public SocketServer(string ip, int port)
{
_ip = ip;
_port = port;
}
/// <summary>
/// 构造函数,监听IP地址默认为本机0.0.0.0
/// </summary>
/// <param name="port">监听的端口</param>
public SocketServer(int port)
{
_ip = "0.0.0.0";
_port = port;
//处理客户端连接关闭后的事件
Instance = this;
}
#region
private string _ip { get; set; } = "";
private int _port { get; set; } = 0;
private bool _isListen { get; set; } = true;
private LinkedList<SocketConnection> _clientList { get; } = new LinkedList<SocketConnection>();
internal class UdpState
{
public byte[] Buffer;
public EndPoint Remote;
public Socket Socket;
public UdpState()
{
Buffer = new byte[65536];
}
}
private bool IsListening { get; set; }
#endregion
#region
/// <summary>
/// 指向本类的静态对象,对象为最近生成的类实例
/// </summary>
/// <param name="port">监听的端口</param>
public static SocketServer Instance;
public Socket _socket { get; set; } = null;
#endregion
#region
/// <summary>
/// 删除指定的客户端连接
/// </summary>
/// <param name="theConnection">指定的客户端连接</param>
private void RemoveConnection(SocketConnection theConnection)
{
RWLock_ClientList.EnterWriteLock();
try
{
_clientList.Remove(theConnection);
HandleClientClose?.Invoke(theConnection, this);
}
finally
{
RWLock_ClientList.ExitWriteLock();
}
}
/// <summary>
/// 删除不活动的客户端连接
/// </summary>
/// <param name="theConnection">指定的客户端连接</param>
private void RemoveInactiveConnections()
{
List<SocketConnection> conns = new List<SocketConnection>();
foreach (SocketConnection connect in _clientList.ToArray())
{
if (connect.IsActive == false)
{
conns.Add(connect);
}
}
foreach (SocketConnection conn in conns.ToArray())
{
RemoveInactiveConnection(conn);
}
}
/// <summary>
/// 检测该客户端,如果不活动则删除连接
/// </summary>
/// <param name="theConnection">指定的客户端连接</param>
private void RemoveInactiveConnection(SocketConnection connect)
{
if (connect.IsActive == false)
{
RemoveConnection(connect);
}
}
/// <summary>
/// 当某连接不活动超时激活该函数
/// </summary>
private void HandleConnClientClose(SocketConnection conn, SocketServer server)
{
RemoveConnection(conn);
}
/// <summary>
/// 维护客户端列表的读写锁
/// </summary>
private ReaderWriterLockSlim RWLock_ClientList { get; } = new ReaderWriterLockSlim();
private void EndReceiveFrom(IAsyncResult ir)
{
if (IsListening)
{
UdpState state = ir.AsyncState as UdpState;
try
{
if (ir.IsCompleted)
{
int length = state.Socket.EndReceiveFrom(ir, ref state.Remote);
byte[] btReceived = new byte[length];
Buffer.BlockCopy(state.Buffer, 0, btReceived, 0, length);
//查询是否UDP连接已经存在
SocketConnection connection = GetTheConnection(x =>
{
var Id = (string)x.Tag;
return Id == state.Remote.ToString();
});
//如果不存在则新建一个UDP连接对象
if (connection == null)
{
connection = new SocketConnection(state.Remote as IPEndPoint, this)
{
HandleSendMsg = HandleSendMsg == null ? null : new Action<byte[], SocketConnection, SocketServer>(HandleSendMsg),
HandleClientClose = new Action<SocketConnection, SocketServer>(HandleConnClientClose),
HandleException = HandleException == null ? null : new Action<Exception>(HandleException)
};
//connection.HandleClientClose += HandleClientClose == null ? null : new Action<SocketConnection, SocketServer>(HandleClientClose);
connection.Tag = state.Remote.ToString();
AddConnection(connection);
}
connection.Reactive();
HandleRecMsg?.Invoke(btReceived, connection, this);
}
}
catch (Exception ex)
{
//System.Diagnostics.Trace.WriteLine(DateTime.Now.ToString() + "\t" + ex.Message + ex.Source);
HandleException?.Invoke(ex);
}
finally
{
state.Socket.BeginReceiveFrom(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, ref state.Remote, new AsyncCallback(EndReceiveFrom), state);
}
}
}
/// <summary>
/// 关闭指定客户端连接
/// </summary>
/// <param name="theConnection">指定的客户端连接</param>
#endregion
#region
/// <summary>
/// 开始服务,监听客户端
/// </summary>
public void StartServer()
{
try
{
//实例化套接字ip4寻址协议流式传输TCP协议
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
//开始侦听
Listening();
HandleServerStarted?.BeginInvoke(this, null, null);
}
catch (Exception ex)
{
HandleException?.BeginInvoke(ex, null, null);
}
}
///<summary>
///按照IP地址和端口发送字符串
///</summary>
public void Send(string EndpointString, byte[] bytes)
{
try
{
SocketConnection conn = GetTheConnection(x =>
{
var Id = (string)x.Tag;
return Id == EndpointString;
});
conn.Send(bytes);
}
catch (Exception ex)
{
HandleException?.BeginInvoke(ex, null, null);
}
}
///<summary>
///开始异步监听端口
///</summary>
public void Listening()
{
IPAddress ip = IPAddress.Any;
try
{
if (this._ip != null)
if (!IPAddress.TryParse(this._ip, out ip))
throw new ArgumentException("IP地址错误", "Ip");
_socket.Bind(new IPEndPoint(ip, this._port));
UdpState state = new UdpState();
state.Socket = _socket;
state.Remote = new IPEndPoint(IPAddress.Any, 0);
_socket.BeginReceiveFrom(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, ref state.Remote, new AsyncCallback(EndReceiveFrom), state);
IsListening = true;
}
catch (Exception ex)
{
IsListening = false;
HandleException?.BeginInvoke(ex, null, null);
}
}
/// <summary>
/// 异步处理收到的数据包
/// </summary>
public void CloseConnection(SocketConnection theConnection)
{
RemoveConnection(theConnection);
//调用外部回调函数通知连接被关闭
}
/// <summary>
/// 添加客户端连接
/// </summary>
/// <param name="theConnection">需要添加的客户端连接</param>
public void AddConnection(SocketConnection theConnection)
{
RWLock_ClientList.EnterWriteLock();
try
{
_clientList.AddLast(theConnection);
}
finally
{
RWLock_ClientList.ExitWriteLock();
//调用外部回调函数通知新连接建立
HandleNewClientConnected(this, theConnection);
}
}
/// <summary>
/// 通过条件获取客户端连接列表
/// </summary>
/// <param name="predicate">筛选条件</param>
/// <returns></returns>
public IEnumerable<SocketConnection> GetConnectionList(Func<SocketConnection, bool> predicate)
{
RemoveInactiveConnections();
IEnumerable<SocketConnection> ret;
lock (_clientList)
{
ret = _clientList.Where(predicate);
}
return ret;
}
/// <summary>
/// 获取所有客户端连接列表
/// </summary>
/// <returns></returns>
public IEnumerable<SocketConnection> GetConnectionList()
{
RemoveInactiveConnections();
return _clientList;
}
/// <summary>
/// 寻找特定条件的客户端连接
/// </summary>
/// <param name="predicate">筛选条件</param>
/// <returns></returns>
public SocketConnection GetTheConnection(Func<SocketConnection, bool> predicate)
{
SocketConnection conn;
lock (_clientList)
{
conn = _clientList.Where(predicate).FirstOrDefault();
}
if (conn == null) return null;
RemoveInactiveConnection(conn);
if (conn.IsActive) return conn;
return null;
}
/// <summary>
/// 获取客户端连接数
/// </summary>
/// <returns></returns>
public int GetConnectionCount()
{
int ret;
lock (_clientList)
{
ret = _clientList.Count;
}
return ret;
}
#endregion
#region
/// <summary>
/// 服务启动后执行
/// </summary>
public Action<SocketServer> HandleServerStarted { get; set; }
#endregion
#region
/// <summary>
/// 当新客户端连接后执行
/// </summary>
public Action<SocketServer, SocketConnection> HandleNewClientConnected { get; set; }
/// <summary>
/// 客户端连接接受新的消息后调用
/// </summary>
public Action<byte[], SocketConnection, SocketServer> HandleRecMsg { get; set; }
/// <summary>
/// 客户端连接发送消息后回调
/// </summary>
public Action<byte[], SocketConnection, SocketServer> HandleSendMsg { get; set; }
/// <summary>
/// 客户端连接关闭后回调
/// </summary>
public Action<SocketConnection, SocketServer> HandleClientClose { get; set; }
#endregion
#region
/// <summary>
/// 异常处理程序
/// </summary>
public Action<Exception> HandleException { get; set; }
#endregion
}
}

View File

@@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace Miuser.NUDP.Sockets
{
#region
/// <summary>
/// 异常处理程序
/// </summary>
public class SocketExceptionEventArgs : EventArgs { public Exception Exception; public EndPoint Remote; }
/// <summary>
/// 服务启动后执行
/// </summary>
public class ServerStartedEventArgs : EventArgs { public EndPoint Server; }
/// <summary>
/// 服务端关闭客户端后执行
/// </summary>
public class ServerClosedEventArgs : EventArgs { public EndPoint Server; }
/// <summary>
/// 当新客户端连接后执行
/// </summary>
public class NewClientEventArgs : EventArgs { public EndPoint Remote; }
/// <summary>
/// 客户端连接关闭后回调
/// </summary>
public class ClientClosedEventArgs : EventArgs { public EndPoint Remote; }
/// <summary>
/// 客户端连接接受新的消息后调用
/// </summary>
public class MsgReceivedEventArgs : EventArgs { public byte[] Received; public EndPoint Remote; }
/// <summary>
/// 客户端连接发送消息后回调
/// </summary>
public class MsgSendEventArgs : EventArgs { public byte[] Received; public EndPoint Remote; }
#endregion
}

305
UdpPlugWebsocket/Form1.Designer.cs generated Normal file
View File

@@ -0,0 +1,305 @@
namespace UdpPlugWebsocket
{
partial class Form1
{
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
/// <param name="disposing">如果应释放托管资源,为 true否则为 false。</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows
/// <summary>
/// 设计器支持所需的方法 - 不要修改
/// 使用代码编辑器修改此方法的内容。
/// </summary>
private void InitializeComponent()
{
this.menuStrip1 = new System.Windows.Forms.MenuStrip();
this.ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.ToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem();
this.ToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem();
this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.pan_Left = new System.Windows.Forms.Panel();
this.btn_Panel = new System.Windows.Forms.Button();
this.btn_Browser = new System.Windows.Forms.Button();
this.btn_Device = new System.Windows.Forms.Button();
this.statusStrip1 = new System.Windows.Forms.StatusStrip();
this.slab_bottom = new System.Windows.Forms.ToolStripStatusLabel();
this.pan_Fill = new System.Windows.Forms.Panel();
this.splitter1 = new System.Windows.Forms.Splitter();
this.tabControl1 = new System.Windows.Forms.TabControl();
this.tab_Message = new System.Windows.Forms.TabPage();
this.tab_Error = new System.Windows.Forms.TabPage();
this.pan_Top = new System.Windows.Forms.Panel();
this.menuStrip1.SuspendLayout();
this.pan_Left.SuspendLayout();
this.statusStrip1.SuspendLayout();
this.pan_Fill.SuspendLayout();
this.tabControl1.SuspendLayout();
this.SuspendLayout();
//
// menuStrip1
//
this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.ToolStripMenuItem,
this.ToolStripMenuItem1,
this.ToolStripMenuItem});
this.menuStrip1.Location = new System.Drawing.Point(0, 0);
this.menuStrip1.Name = "menuStrip1";
this.menuStrip1.Size = new System.Drawing.Size(1121, 29);
this.menuStrip1.TabIndex = 23;
this.menuStrip1.Text = "mnu_Main";
//
// 配置ToolStripMenuItem
//
this.ToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.ToolStripMenuItem});
this.ToolStripMenuItem.Name = "配置ToolStripMenuItem";
this.ToolStripMenuItem.Size = new System.Drawing.Size(12, 20);
//
// 系统配置ToolStripMenuItem
//
this.ToolStripMenuItem.Name = "系统配置ToolStripMenuItem";
this.ToolStripMenuItem.Size = new System.Drawing.Size(124, 22);
this.ToolStripMenuItem.Text = "系统配置";
//
// 配置ToolStripMenuItem1
//
this.ToolStripMenuItem1.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.ToolStripMenuItem1,
this.exitToolStripMenuItem,
this.ToolStripMenuItem});
this.ToolStripMenuItem1.Font = new System.Drawing.Font("新宋体", 15.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.ToolStripMenuItem1.Name = "配置ToolStripMenuItem1";
this.ToolStripMenuItem1.Size = new System.Drawing.Size(66, 25);
this.ToolStripMenuItem1.Text = "配置";
//
// 系统配置ToolStripMenuItem1
//
this.ToolStripMenuItem1.Name = "系统配置ToolStripMenuItem1";
this.ToolStripMenuItem1.Size = new System.Drawing.Size(158, 22);
this.ToolStripMenuItem1.Text = "系统配置";
this.ToolStripMenuItem1.Click += new System.EventHandler(this.ToolStripMenuItem1_Click);
//
// exitToolStripMenuItem
//
this.exitToolStripMenuItem.Name = "exitToolStripMenuItem";
this.exitToolStripMenuItem.Size = new System.Drawing.Size(158, 22);
this.exitToolStripMenuItem.Text = "退出";
this.exitToolStripMenuItem.Click += new System.EventHandler(this.exitToolStripMenuItem_Click);
//
// 显示调试信息ToolStripMenuItem
//
this.ToolStripMenuItem.Name = "显示调试信息ToolStripMenuItem";
this.ToolStripMenuItem.Size = new System.Drawing.Size(158, 22);
this.ToolStripMenuItem.Text = "显示调试信息";
this.ToolStripMenuItem.Click += new System.EventHandler(this.ToolStripMenuItem_Click);
//
// 硬件ToolStripMenuItem
//
this.ToolStripMenuItem.Font = new System.Drawing.Font("新宋体", 15.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.ToolStripMenuItem.Name = "硬件ToolStripMenuItem";
this.ToolStripMenuItem.Size = new System.Drawing.Size(66, 25);
this.ToolStripMenuItem.Text = "帮助";
this.ToolStripMenuItem.Click += new System.EventHandler(this.ToolStripMenuItem_Click);
//
// pan_Left
//
this.pan_Left.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.pan_Left.Controls.Add(this.btn_Panel);
this.pan_Left.Controls.Add(this.btn_Browser);
this.pan_Left.Controls.Add(this.btn_Device);
this.pan_Left.Dock = System.Windows.Forms.DockStyle.Left;
this.pan_Left.Location = new System.Drawing.Point(0, 29);
this.pan_Left.Name = "pan_Left";
this.pan_Left.Size = new System.Drawing.Size(263, 613);
this.pan_Left.TabIndex = 24;
//
// btn_Panel
//
this.btn_Panel.BackColor = System.Drawing.Color.White;
this.btn_Panel.Dock = System.Windows.Forms.DockStyle.Top;
this.btn_Panel.Font = new System.Drawing.Font("微软雅黑", 15.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.btn_Panel.Location = new System.Drawing.Point(0, 100);
this.btn_Panel.Name = "btn_Panel";
this.btn_Panel.Size = new System.Drawing.Size(261, 50);
this.btn_Panel.TabIndex = 8;
this.btn_Panel.Text = "交换面板";
this.btn_Panel.UseVisualStyleBackColor = false;
this.btn_Panel.Click += new System.EventHandler(this.btn_Panel_Click);
//
// btn_Browser
//
this.btn_Browser.BackColor = System.Drawing.Color.White;
this.btn_Browser.Dock = System.Windows.Forms.DockStyle.Top;
this.btn_Browser.Font = new System.Drawing.Font("微软雅黑", 15.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.btn_Browser.Location = new System.Drawing.Point(0, 50);
this.btn_Browser.Name = "btn_Browser";
this.btn_Browser.Size = new System.Drawing.Size(261, 50);
this.btn_Browser.TabIndex = 6;
this.btn_Browser.Text = "控制页面";
this.btn_Browser.UseVisualStyleBackColor = false;
this.btn_Browser.Click += new System.EventHandler(this.btn_Browser_Click);
//
// btn_Device
//
this.btn_Device.BackColor = System.Drawing.Color.White;
this.btn_Device.Dock = System.Windows.Forms.DockStyle.Top;
this.btn_Device.Font = new System.Drawing.Font("微软雅黑", 15.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.btn_Device.Location = new System.Drawing.Point(0, 0);
this.btn_Device.Name = "btn_Device";
this.btn_Device.Size = new System.Drawing.Size(261, 50);
this.btn_Device.TabIndex = 5;
this.btn_Device.Text = "终端设备";
this.btn_Device.UseVisualStyleBackColor = false;
this.btn_Device.Click += new System.EventHandler(this.btn_Device_Click);
//
// statusStrip1
//
this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.slab_bottom});
this.statusStrip1.Location = new System.Drawing.Point(0, 642);
this.statusStrip1.Name = "statusStrip1";
this.statusStrip1.Size = new System.Drawing.Size(1121, 22);
this.statusStrip1.TabIndex = 60;
this.statusStrip1.Text = "状态:";
//
// slab_bottom
//
this.slab_bottom.AutoSize = false;
this.slab_bottom.BackColor = System.Drawing.SystemColors.Control;
this.slab_bottom.Name = "slab_bottom";
this.slab_bottom.Size = new System.Drawing.Size(300, 17);
this.slab_bottom.Text = "状态:";
this.slab_bottom.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
// pan_Fill
//
this.pan_Fill.BackColor = System.Drawing.Color.White;
this.pan_Fill.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.pan_Fill.Controls.Add(this.splitter1);
this.pan_Fill.Controls.Add(this.tabControl1);
this.pan_Fill.Controls.Add(this.pan_Top);
this.pan_Fill.Dock = System.Windows.Forms.DockStyle.Fill;
this.pan_Fill.Location = new System.Drawing.Point(263, 29);
this.pan_Fill.Name = "pan_Fill";
this.pan_Fill.Size = new System.Drawing.Size(858, 613);
this.pan_Fill.TabIndex = 61;
//
// splitter1
//
this.splitter1.BackColor = System.Drawing.SystemColors.ActiveBorder;
this.splitter1.Dock = System.Windows.Forms.DockStyle.Top;
this.splitter1.Location = new System.Drawing.Point(0, 507);
this.splitter1.Name = "splitter1";
this.splitter1.Size = new System.Drawing.Size(856, 2);
this.splitter1.TabIndex = 8;
this.splitter1.TabStop = false;
//
// tabControl1
//
this.tabControl1.Alignment = System.Windows.Forms.TabAlignment.Bottom;
this.tabControl1.Controls.Add(this.tab_Message);
this.tabControl1.Controls.Add(this.tab_Error);
this.tabControl1.Dock = System.Windows.Forms.DockStyle.Fill;
this.tabControl1.Location = new System.Drawing.Point(0, 507);
this.tabControl1.Name = "tabControl1";
this.tabControl1.SelectedIndex = 0;
this.tabControl1.Size = new System.Drawing.Size(856, 104);
this.tabControl1.TabIndex = 7;
//
// tab_Message
//
this.tab_Message.Location = new System.Drawing.Point(4, 4);
this.tab_Message.Name = "tab_Message";
this.tab_Message.Padding = new System.Windows.Forms.Padding(3);
this.tab_Message.Size = new System.Drawing.Size(848, 78);
this.tab_Message.TabIndex = 1;
this.tab_Message.Text = "Message";
this.tab_Message.UseVisualStyleBackColor = true;
//
// tab_Error
//
this.tab_Error.Location = new System.Drawing.Point(4, 4);
this.tab_Error.Name = "tab_Error";
this.tab_Error.Size = new System.Drawing.Size(848, 80);
this.tab_Error.TabIndex = 2;
this.tab_Error.Text = "Error";
this.tab_Error.UseVisualStyleBackColor = true;
//
// pan_Top
//
this.pan_Top.BackColor = System.Drawing.Color.White;
this.pan_Top.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.pan_Top.Dock = System.Windows.Forms.DockStyle.Top;
this.pan_Top.Location = new System.Drawing.Point(0, 0);
this.pan_Top.Name = "pan_Top";
this.pan_Top.Size = new System.Drawing.Size(856, 507);
this.pan_Top.TabIndex = 4;
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(1121, 664);
this.Controls.Add(this.pan_Fill);
this.Controls.Add(this.pan_Left);
this.Controls.Add(this.menuStrip1);
this.Controls.Add(this.statusStrip1);
this.Name = "Form1";
this.Text = "UdpPlugWebsocket 0.1";
this.Load += new System.EventHandler(this.Form1_Load);
this.menuStrip1.ResumeLayout(false);
this.menuStrip1.PerformLayout();
this.pan_Left.ResumeLayout(false);
this.statusStrip1.ResumeLayout(false);
this.statusStrip1.PerformLayout();
this.pan_Fill.ResumeLayout(false);
this.tabControl1.ResumeLayout(false);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.MenuStrip menuStrip1;
private System.Windows.Forms.ToolStripMenuItem ToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem ToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem ToolStripMenuItem1;
private System.Windows.Forms.ToolStripMenuItem ToolStripMenuItem1;
private System.Windows.Forms.ToolStripMenuItem exitToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem ToolStripMenuItem;
private System.Windows.Forms.Panel pan_Left;
private System.Windows.Forms.Button btn_Panel;
private System.Windows.Forms.Button btn_Browser;
private System.Windows.Forms.Button btn_Device;
private System.Windows.Forms.StatusStrip statusStrip1;
private System.Windows.Forms.ToolStripStatusLabel slab_bottom;
private System.Windows.Forms.Panel pan_Fill;
private System.Windows.Forms.Splitter splitter1;
private System.Windows.Forms.TabControl tabControl1;
private System.Windows.Forms.TabPage tab_Error;
private System.Windows.Forms.TabPage tab_Message;
public System.Windows.Forms.Panel pan_Top;
private System.Windows.Forms.ToolStripMenuItem ToolStripMenuItem;
}
}

176
UdpPlugWebsocket/Form1.cs Normal file
View File

@@ -0,0 +1,176 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace UdpPlugWebsocket
{
public partial class Form1 : Form
{
//所有窗体的容器
List<Form> forms = new List<Form>();
//初始化配置窗体
SetupForm setup = new SetupForm();
//初始化所有窗体对象,每个窗体对应一个功能模组
private void Init_Forms()
{
try
{
//添加终端设备窗体
forms.Add(Device.Instance);
//添加浏览器窗体
forms.Add(Browser.Instance);
//添加交换面板窗体
forms.Add(Panel.Instance);
//配置窗体为子窗体,并添加到主窗体上
foreach (Form frm in forms)
{
frm.TopLevel = false;
frm.FormBorderStyle = FormBorderStyle.None;
frm.Dock = DockStyle.Fill;
pan_Top.Controls.Add(frm);
}
tab_Message.Controls.Add(MessageForm.Instance);
MessageForm.Instance.Show();
tab_Error.Controls.Add(ErrorForm.Instance);
ErrorForm.Instance.Show();
//连接模块与系统终端窗口
Device.Instance.HandleMessage += new Action<string>(MessageForm.Log);
Device.Instance.HandleError += new Action<string>(ErrorForm.Log);
Browser.Instance.HandleMessage += new Action<string>(MessageForm.Log);
Browser.Instance.HandleError += new Action<string>(ErrorForm.Log);
//连接Pannel数据输入端 Device->Panel
Device.Instance.server.HandleRecMsg += new Action<byte[], Miuser.NUDP.Sockets.SocketConnection, Miuser.NUDP.Sockets.SocketServer>((bytes, conn, server) =>
{
Panel.Instance.sw.SendFromUDP(bytes, conn.Tag.ToString());
});
//连接Pannel数据输入端 Browser->Panel
Browser.Instance.HandleWebSocketRecMsg += new Action<byte[], WebSocketSharp.Net.WebSockets.WebSocketContext>((bytes, context) =>
{
Panel.Instance.sw.SendFromWebSocket(bytes, context.UserEndPoint.ToString());
});
//连接Pannel数据输出端 Panel->Device
Panel.Instance.sw.HandleUDPSendMsg += new Action<byte[], string>((bytes, endpointString) =>
{
Device.Instance.server.Send(endpointString, bytes);
});
//连接Panel数据输出端 Panel->Browser
Panel.Instance.sw.HandleWebSocketSendMsg += new Action<byte[], string>((bytes, endpointString)=>
{
Browser.Instance.Send(endpointString, bytes);
});
//刷新菜单状态
ToolStripMenuItem.Checked = SetupForm.cfg.EnableScreenLog;
}
catch (Exception e)
{
MessageBox.Show(e.StackTrace,"系统模块初始化错误");
}
}
//使所有子窗体不可见
private void Invislble_Forms()
{
foreach (Form frm in forms)
{
frm.Visible = false;
}
}
//根据类名获得窗体对象
private Form Get_Form(string className)
{
foreach (Form frm in forms)
{
if (frm.GetType().Name == className)
{
return frm;
}
}
return null;
}
private void ShowModule(string moduleName)
{
try
{
if (Get_Form(moduleName) != null) Get_Form(moduleName).Visible = true;
}
catch (Exception e)
{
MessageBox.Show(e.StackTrace, "系统模块初始化错误");
}
}
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Init_Forms();
ShowModule("Device");
}
private void btn_Device_Click(object sender, EventArgs e)
{
Invislble_Forms();
ShowModule("Device");
}
private void btn_Browser_Click(object sender, EventArgs e)
{
{
Invislble_Forms();
ShowModule("Browser");
}
}
private void btn_Panel_Click(object sender, EventArgs e)
{
Invislble_Forms();
ShowModule("Panel");
}
private void ToolStripMenuItem1_Click(object sender, EventArgs e)
{
setup.ShowDialog();
}
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
System.Environment.Exit(0);
}
private void ToolStripMenuItem_Click(object sender, EventArgs e)
{
MessageBox.Show("UPW 0.1 \n Tks Song");
}
private void btn_LogScreen_Click(object sender, EventArgs e)
{
}
private void ToolStripMenuItem_Click(object sender, EventArgs e)
{
SetupForm.cfg.EnableScreenLog = !SetupForm.cfg.EnableScreenLog;
ToolStripMenuItem.Checked = SetupForm.cfg.EnableScreenLog;
}
}
}

129
UdpPlugWebsocket/Form1.resx Normal file
View File

@@ -0,0 +1,129 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="menuStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<metadata name="statusStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>259, 17</value>
</metadata>
<metadata name="$this.TrayHeight" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>55</value>
</metadata>
</root>

View File

@@ -0,0 +1,60 @@
namespace UdpPlugWebsocket
{
partial class ErrorForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.richTextBox1 = new System.Windows.Forms.RichTextBox();
this.SuspendLayout();
//
// richTextBox1
//
this.richTextBox1.Dock = System.Windows.Forms.DockStyle.Fill;
this.richTextBox1.Location = new System.Drawing.Point(0, 0);
this.richTextBox1.Name = "richTextBox1";
this.richTextBox1.Size = new System.Drawing.Size(463, 400);
this.richTextBox1.TabIndex = 0;
this.richTextBox1.Text = "";
//
// Message
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(463, 400);
this.Controls.Add(this.richTextBox1);
this.Name = "Message";
this.Text = "Message";
this.Load += new System.EventHandler(this.Message_Load);
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.RichTextBox richTextBox1;
}
}

View File

@@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace UdpPlugWebsocket
{
public partial class ErrorForm : Form
{
private ErrorForm()
{
//窗体子窗口化
this.TopLevel = false;
this.FormBorderStyle = FormBorderStyle.None;
this.Dock = DockStyle.Fill;
InitializeComponent();
Instance = this;
this.FormClosed += Message_FormClosed;
}
private void Message_FormClosed(object sender, FormClosedEventArgs e)
{
Instance = null;
}
private static ErrorForm _instance;
public static ErrorForm Instance
{
get
{
if (_instance == null)
_instance = new ErrorForm();
return _instance;
}
private set { _instance = value; }
}
String s_output = "";
public void SetOutput(string text)
{
text = DateTime.Now.ToLongDateString() +" "+DateTime.Now.ToLongTimeString()+ " " + text;
Action action = () =>
{
s_output = s_output + text + "\r";
if ((s_output.Length)>5000)
{
s_output = s_output.Substring(s_output.Length - 5000, 5000);
}
//滚到最后
this.richTextBox1.Text = s_output;
this.richTextBox1.Select(richTextBox1.TextLength, 0);
//this.richTextBox1.Focus();
this.richTextBox1.ScrollToCaret();
};
this.richTextBox1.Invoke(action);
}
public static void Log(string text)
{
if (_instance != null)
{
_instance.SetOutput(text);
}
}
private void Message_Load(object sender, EventArgs e)
{
}
}
}

View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@@ -0,0 +1,60 @@
namespace UdpPlugWebsocket
{
partial class MessageForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.richTextBox1 = new System.Windows.Forms.RichTextBox();
this.SuspendLayout();
//
// richTextBox1
//
this.richTextBox1.Dock = System.Windows.Forms.DockStyle.Fill;
this.richTextBox1.Location = new System.Drawing.Point(0, 0);
this.richTextBox1.Name = "richTextBox1";
this.richTextBox1.Size = new System.Drawing.Size(463, 400);
this.richTextBox1.TabIndex = 0;
this.richTextBox1.Text = "";
this.richTextBox1.TextChanged += new System.EventHandler(this.richTextBox1_TextChanged);
//
// MessageForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.richTextBox1);
this.Name = "MessageForm";
this.Size = new System.Drawing.Size(463, 400);
this.Load += new System.EventHandler(this.Log_Load);
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.RichTextBox richTextBox1;
}
}

View File

@@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace UdpPlugWebsocket
{
public partial class MessageForm : Form
{
private MessageForm()
{
//打开添加用户面板
this.TopLevel = false;
this.FormBorderStyle = FormBorderStyle.None;
this.Dock = DockStyle.Fill;
InitializeComponent();
Instance = this;
this.FormClosed += Log_FormClosed;
}
private void Log_FormClosed(object sender, FormClosedEventArgs e)
{
Instance = null;
}
private static MessageForm _instance;
public static MessageForm Instance
{
get
{
if (_instance == null)
_instance = new MessageForm();
return _instance;
}
private set { _instance = value; }
}
String s_output = "";
public void SetOutput(string text)
{
//决定是否屏显
if (SetupForm.cfg.EnableScreenLog == false) return;
text = DateTime.Now.ToLongDateString() +" "+DateTime.Now.ToLongTimeString()+ " " + text;
this.Invoke(new Action(() =>
{
s_output = s_output + text.Replace("\0", "") + "\r";
if ((s_output.Length) > 5000)
{
s_output = s_output.Substring(s_output.Length - 5000, 5000);
}
//滚到最后
this.richTextBox1.Text = s_output;
this.richTextBox1.Select(richTextBox1.TextLength, 0);
//this.richTextBox1.Focus();
this.richTextBox1.ScrollToCaret();
}));
}
public static void Log(string text)
{
if (_instance != null)
{
_instance.SetOutput(text);
}
}
private void Log_Load(object sender, EventArgs e)
{
}
private void richTextBox1_TextChanged(object sender, EventArgs e)
{
}
}
}

View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@@ -0,0 +1,269 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UdpPlugWebsocket
{
public class WSConnection
{
//Websocket连接生存时间
private static int LIFELIMIT = SetupForm.cfg.NODETTL;
public string EndpointString;
public int ttl;
//定义Timer类
private System.Timers.Timer timer;
public WSConnection()
{
System.Timers.Timer timer = new System.Timers.Timer();
ttl = LIFELIMIT;
InitTimer();
IsActive = true;
}
#region
/// <summary>
/// 初始化Timer控件
/// </summary>
private void InitTimer()
{
//设置定时间隔(毫秒为单位)
int interval = 1000;
timer = new System.Timers.Timer(interval);
//设置执行一次false还是一直执行(true)
timer.AutoReset = true;
//设置是否执行System.Timers.Timer.Elapsed事件
timer.Enabled = true;
//绑定Elapsed事件
timer.Elapsed += new System.Timers.ElapsedEventHandler(TimerUp);
}
/// <summary>
/// Timer类执行定时到点事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void TimerUp(object sender, System.Timers.ElapsedEventArgs e)
{
ttl -= (int)((System.Timers.Timer)(sender)).Interval;
if (ttl <= 0)
{
IsActive = false;
HandleWebsocketClosed?.Invoke(this);
timer.Dispose();
}
}
public bool IsActive { get; set; }
public void Reactive()
{
ttl = LIFELIMIT;
}
#endregion
#region
/// <summary>
/// NODE关闭后回调
/// </summary>
public Action<WSConnection> HandleWebsocketClosed { get; set; }
#endregion
}
public class UDPConnection
{
//UDP连接生存时间
private static int LIFELIMIT = SetupForm.cfg.NODETTL;
public string EndpointString;
public int ttl;
//定义Timer类
private System.Timers.Timer timer;
public UDPConnection()
{
System.Timers.Timer timer = new System.Timers.Timer();
ttl = LIFELIMIT;
InitTimer();
IsActive = true;
}
#region
/// <summary>
/// 初始化Timer控件
/// </summary>
private void InitTimer()
{
//设置定时间隔(毫秒为单位)
int interval = 1000;
timer = new System.Timers.Timer(interval);
//设置执行一次false还是一直执行(true)
timer.AutoReset = true;
//设置是否执行System.Timers.Timer.Elapsed事件
timer.Enabled = true;
//绑定Elapsed事件
timer.Elapsed += new System.Timers.ElapsedEventHandler(TimerUp);
}
/// <summary>
/// Timer类执行定时到点事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void TimerUp(object sender, System.Timers.ElapsedEventArgs e)
{
ttl -= (int)((System.Timers.Timer)(sender)).Interval;
if (ttl <= 0)
{
IsActive = false;
HandleUDPClosed?.Invoke(this);
timer.Dispose();
}
}
public bool IsActive { get; set; }
public void Reactive()
{
ttl = LIFELIMIT;
}
#endregion
#region
/// <summary>
/// NODE关闭后回调
/// </summary>
public Action<UDPConnection> HandleUDPClosed { get; set; }
#endregion
}
//交换模块的群组
public class NODE
{
//NODE连接生存时间
private static int LIFELIMIT = SetupForm.cfg.NODETTL;
//剩余生存时间
public int ttl;
//节点的UID
public string ID;
public List<WSConnection> WebsocketConnections;
public List<UDPConnection> UDPConnections;
//定义Timer类
private System.Timers.Timer timer;
public NODE(string ID)
{
WebsocketConnections = new List<WSConnection>();
UDPConnections = new List<UDPConnection>();
System.Timers.Timer timer = new System.Timers.Timer();
ttl = LIFELIMIT;
InitTimer();
IsActive = true;
HandleNodeCreated?.Invoke(this);
}
#region
/// <summary>
/// 初始化Timer控件
/// </summary>
private void InitTimer()
{
//设置定时间隔(毫秒为单位)
int interval = 1000;
timer = new System.Timers.Timer(interval);
//设置执行一次false还是一直执行(true)
timer.AutoReset = true;
//设置是否执行System.Timers.Timer.Elapsed事件
timer.Enabled = true;
//绑定Elapsed事件
timer.Elapsed += new System.Timers.ElapsedEventHandler(TimerUp);
}
/// <summary>
/// Timer类执行定时到点事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void TimerUp(object sender, System.Timers.ElapsedEventArgs e)
{
ttl -= (int)((System.Timers.Timer)(sender)).Interval;
if (ttl <= 0)
{
IsActive = false;
HandleNodeClosed?.Invoke(this);
timer.Dispose();
}
}
#endregion
# region
public void AddSource(string endpoint, Switch.Source source)
{
if (source == Switch.Source.UDPPort) AddUDPConnection(endpoint);
if (source == Switch.Source.WebSocket) AddWebsocketConnection(endpoint);
}
/// <summary>
/// 添加一个新的UDP连接
/// </summary>
/// <param name="endpoint"></param>
public void AddUDPConnection(string endpoint)
{
UDPConnection udp = new UDPConnection();
udp.EndpointString = endpoint;
//节点超时则自动从连接中移除
udp.HandleUDPClosed = new Action<UDPConnection>((conn => {
UDPConnections.Remove(conn); HandleUDPRemoved?.Invoke(conn.EndpointString);
}));
UDPConnections.Add(udp);
HandleUDPAdded?.Invoke(endpoint);
}
/// <summary>
/// 添加一个新的Websocket连接
/// </summary>
/// <param name="endpoint"></param>
public void AddWebsocketConnection(string endpoint)
{
WSConnection web = new WSConnection();
web.EndpointString = endpoint;
//节点超时则自动从连接中移除
web.HandleWebsocketClosed = new Action<WSConnection>((conn => {
WebsocketConnections.Remove(conn); HandleWebSocketRemoved?.Invoke(conn.EndpointString);
}));
WebsocketConnections.Add(web);
HandleWebSocketAdded?.Invoke(endpoint);
}
public bool IsActive { get; set; }
public void Reactive()
{
ttl = LIFELIMIT;
}
#endregion
#region
/// <summary>
/// NODE关闭后回调
/// </summary>
public Action<NODE> HandleNodeClosed { get; set; }
/// <summary>
/// NODE建立后回调
/// </summary>
public Action<NODE> HandleNodeCreated { get; set; }
/// <summary>
/// 新的Websocket连接建立后回调
/// </summary>
public Action<string> HandleWebSocketAdded { get; set; }
/// <summary>
/// 新的Websocket连接断开后回调
/// </summary>
public Action<string> HandleWebSocketRemoved { get; set; }
/// <summary>
/// 新的UDP连接建立后回调
/// </summary>
public Action<string> HandleUDPAdded { get; set; }
/// <summary>
/// 新的UDP连接断开后回调
/// </summary>
public Action<string> HandleUDPRemoved { get; set; }
/// <summary>
/// 异常处理程序
/// </summary>
public Action<Exception> HandleException { get; set; }
#endregion
}
}

149
UdpPlugWebsocket/Panel/Panel.Designer.cs generated Normal file
View File

@@ -0,0 +1,149 @@
namespace UdpPlugWebsocket
{
partial class Panel
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.groupBox2 = new System.Windows.Forms.GroupBox();
this.richTextBox1 = new System.Windows.Forms.RichTextBox();
this.dataGridView1 = new System.Windows.Forms.DataGridView();
this.groupBox1 = new System.Windows.Forms.GroupBox();
this.dgv_nodes = new System.Windows.Forms.DataGridView();
this.splitter1 = new System.Windows.Forms.Splitter();
this.groupBox2.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit();
this.groupBox1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.dgv_nodes)).BeginInit();
this.SuspendLayout();
//
// groupBox2
//
this.groupBox2.Controls.Add(this.richTextBox1);
this.groupBox2.Controls.Add(this.dataGridView1);
this.groupBox2.Dock = System.Windows.Forms.DockStyle.Bottom;
this.groupBox2.Font = new System.Drawing.Font("微软雅黑", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.groupBox2.Location = new System.Drawing.Point(0, 394);
this.groupBox2.Name = "groupBox2";
this.groupBox2.Size = new System.Drawing.Size(685, 222);
this.groupBox2.TabIndex = 5;
this.groupBox2.TabStop = false;
this.groupBox2.Text = "会话:";
//
// richTextBox1
//
this.richTextBox1.Dock = System.Windows.Forms.DockStyle.Fill;
this.richTextBox1.Location = new System.Drawing.Point(3, 25);
this.richTextBox1.Name = "richTextBox1";
this.richTextBox1.Size = new System.Drawing.Size(679, 194);
this.richTextBox1.TabIndex = 5;
this.richTextBox1.Text = "";
//
// dataGridView1
//
this.dataGridView1.AllowUserToAddRows = false;
this.dataGridView1.BackgroundColor = System.Drawing.Color.WhiteSmoke;
this.dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.dataGridView1.Dock = System.Windows.Forms.DockStyle.Fill;
this.dataGridView1.GridColor = System.Drawing.Color.Snow;
this.dataGridView1.Location = new System.Drawing.Point(3, 25);
this.dataGridView1.MultiSelect = false;
this.dataGridView1.Name = "dataGridView1";
this.dataGridView1.ReadOnly = true;
this.dataGridView1.RowHeadersVisible = false;
this.dataGridView1.RowHeadersWidth = 4;
this.dataGridView1.RowTemplate.Height = 23;
this.dataGridView1.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect;
this.dataGridView1.Size = new System.Drawing.Size(679, 194);
this.dataGridView1.TabIndex = 0;
//
// groupBox1
//
this.groupBox1.Controls.Add(this.dgv_nodes);
this.groupBox1.Dock = System.Windows.Forms.DockStyle.Fill;
this.groupBox1.Font = new System.Drawing.Font("微软雅黑", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.groupBox1.Location = new System.Drawing.Point(0, 0);
this.groupBox1.Name = "groupBox1";
this.groupBox1.Size = new System.Drawing.Size(685, 384);
this.groupBox1.TabIndex = 3;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "节点:";
//
// dgv_nodes
//
this.dgv_nodes.AllowUserToAddRows = false;
this.dgv_nodes.BackgroundColor = System.Drawing.Color.WhiteSmoke;
this.dgv_nodes.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.dgv_nodes.Dock = System.Windows.Forms.DockStyle.Fill;
this.dgv_nodes.GridColor = System.Drawing.Color.Snow;
this.dgv_nodes.Location = new System.Drawing.Point(3, 25);
this.dgv_nodes.MultiSelect = false;
this.dgv_nodes.Name = "dgv_nodes";
this.dgv_nodes.ReadOnly = true;
this.dgv_nodes.RowHeadersVisible = false;
this.dgv_nodes.RowHeadersWidth = 4;
this.dgv_nodes.RowTemplate.Height = 23;
this.dgv_nodes.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect;
this.dgv_nodes.Size = new System.Drawing.Size(679, 356);
this.dgv_nodes.TabIndex = 1;
//
// splitter1
//
this.splitter1.Dock = System.Windows.Forms.DockStyle.Bottom;
this.splitter1.Location = new System.Drawing.Point(0, 384);
this.splitter1.Name = "splitter1";
this.splitter1.Size = new System.Drawing.Size(685, 10);
this.splitter1.TabIndex = 7;
this.splitter1.TabStop = false;
//
// Panel
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(685, 616);
this.Controls.Add(this.groupBox1);
this.Controls.Add(this.splitter1);
this.Controls.Add(this.groupBox2);
this.Name = "Panel";
this.Text = "Panel";
this.Load += new System.EventHandler(this.Panel_Load);
this.groupBox2.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit();
this.groupBox1.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.dgv_nodes)).EndInit();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.GroupBox groupBox2;
private System.Windows.Forms.RichTextBox richTextBox1;
private System.Windows.Forms.DataGridView dataGridView1;
private System.Windows.Forms.GroupBox groupBox1;
private System.Windows.Forms.Splitter splitter1;
private System.Windows.Forms.DataGridView dgv_nodes;
}
}

View File

@@ -0,0 +1,142 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Miuser.NUDP.Sockets;
namespace UdpPlugWebsocket
{
public partial class Panel : Form
{
//交换机的实例
public Switch sw;
public Panel()
{
InitializeComponent();
sw = new Switch();
//处理新的结点连接后的事件
sw.HandleNodeCreated = new Action<Switch, NODE>((theSW, theNode) =>
{
//RefreshDataGrid(theSW);
});
//处理节点关闭后的事件
sw.HandleNodeClosed = new Action<Switch, NODE>((theSW, theNode) =>
{
//RefreshDataGrid(theSW);
});
//处理节点状态变化后的事件
sw.HandleNodeChanged = new Action<Switch, NODE>((theSW, theNode) =>
{
RefreshDataGrid(theSW);
});
//节点接收UDP消息后的事件
sw.HandleUDPRsvMsg = new Action<byte[], string>((bytes, endpointString) =>
{
SetOutput("(UDP " + endpointString + ")<=" + System.Text.Encoding.Default.GetString(bytes));
});
//节点收到Websocket消息后的事件
sw.HandleWebsocketRsvMsg = new Action<byte[], string>((bytes, endpointString) =>
{
SetOutput("(WebSocket " + endpointString + ")<=" + System.Text.Encoding.Default.GetString(bytes));
});
//节点发送UDP消息后的事件
sw.HandleUDPSendMsg = new Action<byte[], string>((bytes, endpointString) =>
{
SetOutput("(UDP " + endpointString + ")=>" + System.Text.Encoding.Default.GetString(bytes));
});
//节点发送Websocket消息后的事件
sw.HandleWebSocketSendMsg = new Action<byte[], string>((bytes, endpointString) =>
{
SetOutput("(WebSocket " + endpointString + ")=>" + System.Text.Encoding.Default.GetString(bytes));
});
}
private static Panel _instance;
public static Panel Instance
{
get
{
if (_instance == null)
_instance = new Panel();
return _instance;
}
private set { _instance = value; }
}
private void Panel_Load(object sender, EventArgs e)
{
}
private void RefreshDataGrid(Switch theSw)
{
DataTable dt = GetNodeTable(theSw);
lock (dt)
{
this.Invoke(new Action(() =>
{
dgv_nodes.DataSource = dt;
dgv_nodes.Columns[0].Width = 150;
dgv_nodes.Columns[1].Width = 100;
dgv_nodes.Columns[2].Width = 100;
}));
}
}
private DataTable GetNodeTable(Switch theSw)
{
DataTable dt = new DataTable();
dt.Columns.Add("ID");
dt.Columns.Add("UDP ");
dt.Columns.Add("Websocket");
dt.Columns.Add("Lifetime");
IEnumerable<NODE> nodes = theSw.GetNodeList();
lock (nodes)
{
foreach (NODE node in theSw.GetNodeList().ToArray())
{
DataRow dr = dt.NewRow();
dr[0] = node.ID;
dr[1] = node.UDPConnections==null? 0:node.UDPConnections.Count;
dr[2] = node.WebsocketConnections == null ? 0 : node.WebsocketConnections.Count;
dr[3] = node.ttl;
dt.Rows.Add(dr);
}
}
return dt;
}
String s_output = "";
public void SetOutput(string text)
{
//决定是否屏显
if (SetupForm.cfg.EnableScreenLog == false) return;
text = DateTime.Now.ToLongDateString() + " " + DateTime.Now.ToLongTimeString() + " |" + text;
this.Invoke(new Action(() =>
{
s_output = s_output + text.Replace("\0", "") + "\r";
if ((s_output.Length) > 5000)
{
s_output = s_output.Substring(s_output.Length - 5000, 5000);
}
//滚到最后
this.richTextBox1.Text = s_output;
this.richTextBox1.Select(richTextBox1.TextLength, 0);
//this.richTextBox1.Focus();
this.richTextBox1.ScrollToCaret();
}));
}
}
}

View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@@ -0,0 +1,389 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading;
using Miuser.NUDP.Sockets;
namespace UdpPlugWebsocket
{
public class Switch
{
//存储以ID为
public List<NODE> nodes;
public Switch()
{
nodes = new List<NODE>();
}
/// <summary>
/// 接收来自UDP的数据并进行转发
/// </summary>
/// <param name="bytes"></param>
/// <param name="EndpointString"></param>
public void SendFromUDP(byte[] bytes, string EndpointString)
{
HandleUDPRsvMsg?.Invoke(bytes, EndpointString);
string content = System.Text.Encoding.Default.GetString(bytes);
string ID = "";
if (content.Length >= 19)
{
ID = content.Substring(9, 10);
NODE node = GetTheNode((x =>
{
var Id = (string)x.ID;
return Id == ID;
}));
//如果不存在则新建一个NODE
if (node == null)
{
node = new NODE(ID)
{
//连接Node消息到Switch上
HandleNodeClosed = HandleNodeClosed == null ? null : new Action<NODE>((nod) => { HandleNodeClosed(this, nod); }),
HandleNodeCreated = HandleNodeCreated == null ? null : new Action<NODE>((nod) => { HandleNodeCreated(this, nod); }),
HandleUDPAdded = new Action<string>(target =>
{
HandleNodeChanged?.Invoke(this, node);
}),
HandleUDPRemoved = new Action<string>(target =>
{
HandleNodeChanged?.Invoke(this, node);
}),
HandleWebSocketAdded = new Action<string>(target =>
{
HandleNodeChanged?.Invoke(this, node);
}),
HandleWebSocketRemoved= new Action<string>(target =>
{
HandleNodeChanged?.Invoke(this, node);
}),
HandleException=new Action<Exception>(ex=>
{
HandleException(ex);
})
};
node.ID = ID;
AddNode(node);
}
//判断连接是否为新连接,如果是则增加消息源
if (node.UDPConnections.Where(x => { return EndpointString == x.EndpointString; }).FirstOrDefault() == null)
{
node.AddUDPConnection(EndpointString);
}
else
{
node.UDPConnections.Where(x => { return EndpointString == x.EndpointString; }).FirstOrDefault().Reactive();
}
node.Reactive();
//分发消息给所有在线的Websocket端口
lock (node.WebsocketConnections)
{
foreach (WSConnection ws in node.WebsocketConnections.ToArray())
{
HandleWebSocketSendMsg?.Invoke(bytes, ws.EndpointString);
}
}
//分发消息给所有在线的UDP端口
lock (node.UDPConnections)
{
foreach (UDPConnection udp in node.UDPConnections.ToArray())
{
HandleUDPSendMsg?.Invoke(bytes, udp.EndpointString);
}
}
}
}
public void SendFromWebSocket(byte[] bytes, string EndpointString)
{
HandleWebsocketRsvMsg?.Invoke(bytes, EndpointString);
string content = System.Text.Encoding.Default.GetString(bytes);
string ID = "";
if (content.Length >= 19)
{
ID = content.Substring(9, 10);
NODE node = GetTheNode((x =>
{
var Id = (string)x.ID;
return Id == ID;
}));
//如果不存在则新建一个NODE
if (node == null)
{
node = new NODE(ID)
{
//连接Node消息到Switch上
HandleNodeClosed = HandleNodeClosed == null ? null : new Action<NODE>((nod) => { HandleNodeClosed(this, nod); }),
HandleNodeCreated = HandleNodeCreated == null ? null : new Action<NODE>((nod) => { HandleNodeCreated(this, nod); }),
HandleUDPAdded = new Action<string>(target =>
{
HandleNodeChanged?.Invoke(this, node);
}),
HandleUDPRemoved = new Action<string>(target =>
{
HandleNodeChanged?.Invoke(this, node);
}),
HandleWebSocketAdded = new Action<string>(target =>
{
HandleNodeChanged?.Invoke(this, node);
}),
HandleWebSocketRemoved= new Action<string>(target =>
{
HandleNodeChanged?.Invoke(this, node);
}),
HandleException=new Action<Exception>(ex=>
{
HandleException(ex);
})
};
node.ID = ID;
AddNode(node);
}
//判断连接是否为新连接,如果是则增加消息源
if (node.WebsocketConnections.Where(x => { return EndpointString == x.EndpointString; }).FirstOrDefault() == null)
{
node.AddWebsocketConnection(EndpointString);
}else
{
node.WebsocketConnections.Where(x => { return EndpointString == x.EndpointString; }).FirstOrDefault().Reactive();
}
node.Reactive();
//分发消息给所有在线的Websocket端口
if (node.WebsocketConnections != null)
{
lock (node.WebsocketConnections)
{
foreach (WSConnection ws in node.WebsocketConnections.ToArray())
{
HandleWebSocketSendMsg?.Invoke(bytes, ws.EndpointString);
}
}
}
//分发消息给所有在线的UDP端口
if (node.UDPConnections != null)
{
lock (node.UDPConnections)
{
foreach (UDPConnection udp in node.UDPConnections.ToArray())
{
HandleUDPSendMsg?.Invoke(bytes, udp.EndpointString);
}
}
}
}
}
#region
private void RemoveNode(NODE node)
{
RWLock_ClientList.EnterWriteLock();
try
{
nodes.Remove(node);
}
finally
{
RWLock_ClientList.ExitWriteLock();
}
}
/// <summary>
/// 删除不活动的客户端连接
/// </summary>
/// <param name="theConnection">指定的客户端连接</param>
private void RemoveInactiveNodes()
{
List<NODE> nodes = new List<NODE>();
foreach (NODE node in nodes.ToArray())
{
if (node.IsActive == false)
{
nodes.Add(node);
}
}
foreach (NODE node in nodes.ToArray())
{
RemoveInactiveNode(node);
}
}
/// <summary>
/// 检测该客户端,如果不活动则删除连接
/// </summary>
/// <param name="theConnection">指定的客户端连接</param>
private void RemoveInactiveNode(NODE node)
{
if (node.IsActive == false)
{
RemoveNode(node);
}
}
/// <summary>
/// 当某连接不活动超时激活该函数
/// </summary>
private void HandleConnClientClose(NODE node)
{
RemoveNode(node);
}
#endregion
/// <summary>
/// 维护客户端列表的读写锁
/// </summary>
public ReaderWriterLockSlim RWLock_ClientList { get; } = new ReaderWriterLockSlim();
/// <summary>
/// 关闭指定客户端连接
/// </summary>
/// <param name="theConnection">指定的客户端连接</param>
public void CloseNode(NODE node)
{
RemoveNode(node);
//调用外部回调函数通知连接被关闭
HandleNodeClosed?.Invoke(this,node);
}
/// <summary>
/// 添加客户端连接
/// </summary>
/// <param name="theConnection">需要添加的客户端连接</param>
public enum Source {UDPPort,WebSocket }
public void AddNode(NODE node)
{
RWLock_ClientList.EnterWriteLock();
try
{
nodes.Add(node);
node.HandleNodeClosed = new Action<NODE>((id => { nodes.Remove(id); }));
}
finally
{
RWLock_ClientList.ExitWriteLock();
//调用外部回调函数通知新连接建立
HandleNodeCreated?.Invoke(this, node);
}
}
/// <summary>
/// 通过条件获取客户端连接列表
/// </summary>
/// <param name="predicate">筛选条件</param>
/// <returns></returns>
public IEnumerable<NODE> GetNodeList(Func<NODE, bool> predicate)
{
//RemoveInactiveNodes();
IEnumerable<NODE> ret;
lock (nodes)
{
ret = nodes.Where(predicate);
}
return ret;
}
/// <summary>
/// 获取所有客户端连接列表
/// </summary>
/// <returns></returns>
public List<NODE> GetNodeList()
{
//RemoveInactiveNodes();
return nodes;
}
/// <summary>
/// 寻找特定条件的客户端连接
/// </summary>
/// <param name="predicate">筛选条件</param>
/// <returns></returns>
public NODE GetTheNode(Func<NODE, bool> predicate)
{
NODE node;
lock (nodes)
{
node = nodes.Where(predicate).FirstOrDefault();
}
if (node == null) return null;
//RemoveInactiveNode(node);
if (node.IsActive) return node;
return null;
}
/// <summary>
/// 获取客户端连接数
/// </summary>
/// <returns></returns>
public int GetConnectionCount()
{
int ret;
lock (nodes)
{
ret = nodes.Count;
}
return ret;
}
#region UDP客户端连接事件
/// <summary>
// 从UDP端口接收到消息
/// </summary>
public Action<byte[], string> HandleUDPRsvMsg { get; set; }
/// <summary>
// 转发消息给UDP端口
/// </summary>
public Action<byte[], string> HandleUDPSendMsg { get; set; }
#endregion
#region Websocket客户端连接事件
/// <summary>
// 从Websocket端口接收到消息
/// </summary>
public Action<byte[], string> HandleWebsocketRsvMsg { get; set; }
/// <summary>
/// 转发消息给Websocket端口
/// </summary>
public Action<byte[], string> HandleWebSocketSendMsg { get; set; }
#endregion
#region
/// <summary>
/// 当新NODE建立后执行
/// </summary>
public Action<Switch,NODE> HandleNodeCreated { get; set; }
/// <summary>
/// NODE关闭后回调
/// </summary>
public Action<Switch,NODE> HandleNodeClosed { get; set; }
/// <summary>
/// 异常处理程序
/// </summary>
public Action<Exception> HandleException { get; set; }
/// <summary>
/// 节点状态发生变化
/// </summary>
public Action<Switch, NODE> HandleNodeChanged { get; set; }
#endregion
}
}

View File

@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace UdpPlugWebsocket
{
static class Program
{
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}

View File

@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("DoorControl")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("DoorControl")]
[assembly: AssemblyCopyright("Copyright © 2019")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
//将 ComVisible 设置为 false 将使此程序集中的类型
//对 COM 组件不可见。 如果需要从 COM 访问此程序集中的类型,
//请将此类型的 ComVisible 特性设置为 true。
[assembly: ComVisible(false)]
// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
[assembly: Guid("54cf701b-b076-4fc3-8a01-7434111a1c5b")]
// 程序集的版本信息由下列四个值组成:
//
// 主版本
// 次版本
// 生成号
// 修订号
//
//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值,
// 方法是按如下所示使用“*”: :
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -0,0 +1,63 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 运行时版本:4.0.30319.42000
//
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace UdpPlugWebsocket.Properties {
using System;
/// <summary>
/// 一个强类型的资源类,用于查找本地化的字符串等。
/// </summary>
// 此类是由 StronglyTypedResourceBuilder
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
// (以 /str 作为命令选项),或重新生成 VS 项目。
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// 返回此类使用的缓存的 ResourceManager 实例。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("UdpPlugWebsocket.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// 使用此强类型资源类,为所有资源查找
/// 重写当前线程的 CurrentUICulture 属性。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
}
}

View File

@@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@@ -0,0 +1,26 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 运行时版本:4.0.30319.42000
//
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace UdpPlugWebsocket.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;
}
}
}
}

View File

@@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>

View File

@@ -0,0 +1,60 @@
# UdpPlugWebSocket 简称UPWS服务端程序#
## 项目说明 ##
公司要做物联网硬件产品需要一个服务器组件桥接APP软件终端和物联网硬件。 目前比较火的MQTT物联网协议虽然技术先进但进入门槛较高用户也相对较为复杂。BG了一番没有找到合适的服务端开源代码就决定使用自己比较熟悉的.Net技术从头开发一个服务器组件。又考虑到自己毕竟是一个中国人没必要舍近求远的去用Git就选择了在码云开了这个repository如果能帮到你我很高兴。 有问题欢迎与我微信联系: 微信号miuser00加好友请注明您的来意。
## 项目简介 ##
这是一个独立的可执行Win32服务端程序用于桥接终端APP和物联网硬件。 APP端为Websocet接口的静态html页面硬件端为基于Arduino开发的电路与服务器通讯使用UDP协议通讯为方便您的使用本Respository亦提供了一个C#编写的简易UDP测试程序用来模拟硬件设备)。 APP通过Webscoket接口发送一个自定义的字符串报文给服务端服务端根据报文中的ID把报文转发给相同ID的UDP硬件设备或UDP测试程序
![](.\document\preview.png)
启动方法在本地windows环境下运行.\UdpPlugWebsocket\bin\Debug\UdpPlugWebsocket.exe即可按照config.xml中描述的端口启动服务程序。默认UDP端口为7101Websocket端口为9000。此时外部的UDP连接和Websocket连接都会在UI中显示。其中UDP在终端设备页面中显示Websocket连接在控制页面中显示通讯状况在交换面板页面中显示。您可以通过配置->显示调试系信息 打开或关闭UI的通讯Log。
## 数据包格式 UDP与Websocket相同##
![](.\document\packageintro.png)
**Endpoint是外部连接的唯一索引。**
**Endpoint字符串的格式为"XXX.XXX.XXX.XXX:XXXX" 前四组三位数为IP地址最后一组四位数为远程端口号**
##UDP通讯测试##
运行.\UdpExample\client\bin\Debug\client.exe 在程序控制台中输入如下数据包
004832A08000000000200000000000000001234testtest05 即可在终端设备页面看到本机的连接。
![](.\document\clienttest.png)
## Websocket通讯测试 ##
任意支持websocket的浏览器中运行.\UPW_Browser\index.html?ID=0000000002&MM=0000000000000000与UDP测试程序建立连接参数区分大小写
![](.\document\webtest.png)
## 目录结构 ##
.\UdpPlugWebsocket UPWS服务程序主服务程序
.\UdpPlugWebsocket\UdpExample UDP测试程序
.\UPW_Browser Websocket测试程序
.\bin_UPWS 编译后的Win32项目二进制文
... 其余目录为参考代码目录,可删除
##编译环境##
Visual Studio 2015,C#,Win10
## 使用到的开源库 ##
Websocket-Sharp
https://github.com/sta/websocket-sharp
Coldairarrow.Util.Sockets
https://github.com/Coldairarrow/Sockets
## 关键词 ##
C#,Websocket,UDP,物联网,异步编程,接口,lamda表达式,线程池,Action,Invoke,Arduino,ESP8266
## 版权声明 ##
如果您愿意使用 UdpPlugWebSocket 组件,请遵循 MIT 许可所述内容.

88
UdpPlugWebsocket/UI/setup.Designer.cs generated Normal file
View File

@@ -0,0 +1,88 @@
namespace UserLogin
{
partial class SetupForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.btn_save = new System.Windows.Forms.Button();
this.prg_config = new System.Windows.Forms.PropertyGrid();
this.button1 = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// btn_save
//
this.btn_save.Location = new System.Drawing.Point(443, 641);
this.btn_save.Margin = new System.Windows.Forms.Padding(4);
this.btn_save.Name = "btn_save";
this.btn_save.Size = new System.Drawing.Size(178, 30);
this.btn_save.TabIndex = 3;
this.btn_save.Text = "保存并重启";
this.btn_save.UseVisualStyleBackColor = true;
this.btn_save.Click += new System.EventHandler(this.btn_save_Click);
//
// prg_config
//
this.prg_config.Dock = System.Windows.Forms.DockStyle.Fill;
this.prg_config.Location = new System.Drawing.Point(0, 0);
this.prg_config.Margin = new System.Windows.Forms.Padding(4);
this.prg_config.Name = "prg_config";
this.prg_config.Size = new System.Drawing.Size(663, 684);
this.prg_config.TabIndex = 2;
//
// button1
//
this.button1.Location = new System.Drawing.Point(106, 641);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(97, 29);
this.button1.TabIndex = 4;
this.button1.Text = "button1";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// SetupForm
//
this.AcceptButton = this.btn_save;
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
this.ClientSize = new System.Drawing.Size(663, 684);
this.Controls.Add(this.button1);
this.Controls.Add(this.btn_save);
this.Controls.Add(this.prg_config);
this.Name = "SetupForm";
this.Text = "setup";
this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.SetupForm_FormClosed);
this.Load += new System.EventHandler(this.SetupForm_Load);
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Button btn_save;
private System.Windows.Forms.PropertyGrid prg_config;
private System.Windows.Forms.Button button1;
}
}

View File

@@ -0,0 +1,229 @@
///<summary>
         ///模块编号20180730
///模块名:本地配置模块
         ///作用:生成本地的存储信息
         ///作者Miuser
         ///编写日期20180730
///版本1.0
///</summary>
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
//Common reference
using System.Threading;
using System.Net.Sockets;
using System.Net;
using System.Xml.Serialization;
using System.IO;
using System.Text.RegularExpressions;
//mongo DB
using MongoDB.Driver;
using MongoDB.Bson;
using System.Security.Cryptography;
namespace UserLogin
{
public partial class SetupForm : Form
{
Config cfg;
public SetupForm(ref Config config)
{
cfg = config;
InitializeComponent();
}
private void SetupForm_Load(object sender, EventArgs e)
{
prg_config.SelectedObject = cfg;
}
private void btn_save_Click(object sender, EventArgs e)
{
cfg.SavetoFile("config.xml");
Application.Restart();
}
private void SetupForm_FormClosed(object sender, FormClosedEventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
cfg.ShopList.Add("大岛店[01]");
cfg.ShopList.Add("洞庭路店[02]");
}
}
public class Config
{
[CategoryAttribute("1.MongoDB设置")]
public String url { get; set; } //192.168.0.33
[CategoryAttribute("1.MongoDB设置")]
public String port { get; set; }
[CategoryAttribute("1.MongoDB设置")]
public String user { get; set; }
[CategoryAttribute("1.MongoDB设置")]
[BrowsableAttribute(false)]
public String Epass { get; set; }
[CategoryAttribute("1.MongoDB设置")]
[PasswordPropertyText(true)]
[System.Xml.Serialization.XmlIgnore]
public String pass
{
get
{
return Encryption.Decode(Epass);
}
set
{
Epass = Encryption.Encode(value);
}
}
[CategoryAttribute("1.MongoDB设置")]
public String database { get; set; }
[CategoryAttribute("2.球机设定")]
public int GolfPrivilegeTime { get; set; }
[CategoryAttribute("2.球机设定")]
public string ShopName { get; set; }
[CategoryAttribute("2.球机设定")]
public List<string> ShopList { get; set; }
public int SavetoFile(String filename)
{
try
{
XmlSerializer serializer = new XmlSerializer(typeof(Config));
TextWriter writer = new StreamWriter(filename);
serializer.Serialize(writer, this);
writer.Close();
}
catch (Exception ee)
{
MessageBox.Show(ee.StackTrace, ee.Message);
return 0;
}
return 1;
}
public static Config LoadfromFile(String filename)
{
try
{
Config sptr;
XmlSerializer serializer = new XmlSerializer(typeof(Config));
TextReader reader = new StreamReader(filename);
sptr = (Config)(serializer.Deserialize(reader));
reader.Close();
return sptr;
}
catch (Exception ee)
{
MessageBox.Show(ee.StackTrace, ee.Message);
return null;
}
}
}
public class Encryption
{
/// <summary>
/// 作用将字符串内容转化为16进制数据编码其逆过程是Decode
/// 参数说明:
/// strEncode 需要转化的原始字符串
/// 转换的过程是直接把字符转换成Unicode字符,比如数字"3"-->0033,汉字"我"-->U+6211
/// 函数decode的过程是encode的逆过程.
/// </summary>
public static string Encode(string strEncode)
{
string strReturn = "";// 存储转换后的编码
try
{
foreach (short shortx in strEncode.ToCharArray())
{
strReturn += shortx.ToString("X4");
}
}
catch { }
return strReturn;
}
/// <summary>
/// 作用将16进制数据编码转化为字符串是Encode的逆过程
/// </summary>
public static string Decode(string strDecode)
{
string sResult = "";
try
{
for (int i = 0; i < strDecode.Length / 4; i++)
{
sResult += (char)short.Parse(strDecode.Substring(i * 4, 4),
global::System.Globalization.NumberStyles.HexNumber);
}
}
catch { }
return sResult;
}
/// <summary>
/// 将数字转换成16进制字符串后两位加入随机字符其可逆方法为DecodeForNum
/// </summary>
public static string EncodeForNum(int id)
{
//用户加上起始位置后的
int startUserIndex = id;
//转换成16进制
string hexStr = Convert.ToString(startUserIndex, 16);
//后面两位加入随机数
string randomchars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
string tmpstr = "";
//整除的后得到的数可能大于被除数
tmpstr += randomchars[(id / randomchars.Length) > randomchars.Length ? randomchars.Length - 1 : (id / randomchars.Length)];
//余数不可能大于被除数
tmpstr += randomchars[(id % randomchars.Length) > randomchars.Length ? randomchars.Length - 1 : (id % randomchars.Length)];
//返回拼接后的字符,转成大写
string retStr = (hexStr + tmpstr).ToUpper();
return retStr;
}
/// <summary>
/// 解密16进制字符串此方法只适合后面两位有随机字符的
/// </summary>
public static int DecodeForNum(string strDecode)
{
if (strDecode.Length > 2)
{
strDecode = strDecode.Substring(0, strDecode.Length - 2);
return Convert.ToInt32(strDecode, 16);
}
return 0;
}
}
}

View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@@ -0,0 +1,157 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{54CF701B-B076-4FC3-8A01-7434111A1C5B}</ProjectGuid>
<OutputType>WinExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>UdpPlugWebsocket</RootNamespace>
<AssemblyName>UdpPlugWebsocket</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>
</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Deployment" />
<Reference Include="System.Drawing" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
<Reference Include="websocket-sharp">
<HintPath>..\packages\WebSocketSharp-netstandard.1.0.1\lib\net45\websocket-sharp.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Browser.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Browser.Designer.cs">
<DependentUpon>Browser.cs</DependentUpon>
</Compile>
<Compile Include="Device\Device.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Device\Device.Designer.cs">
<DependentUpon>Device.cs</DependentUpon>
</Compile>
<Compile Include="Device\SocketConnection.cs" />
<Compile Include="Device\SocketServer.cs" />
<Compile Include="Form1.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Form1.Designer.cs">
<DependentUpon>Form1.cs</DependentUpon>
</Compile>
<Compile Include="Message\ErrorForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Message\ErrorForm.designer.cs">
<DependentUpon>ErrorForm.cs</DependentUpon>
</Compile>
<Compile Include="Message\MessageForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Message\MessageForm.Designer.cs">
<DependentUpon>MessageForm.cs</DependentUpon>
</Compile>
<Compile Include="Panel\Node.cs" />
<Compile Include="Panel\Panel.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Panel\Panel.Designer.cs">
<DependentUpon>Panel.cs</DependentUpon>
</Compile>
<Compile Include="Panel\Switch.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="setup.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="setup.designer.cs">
<DependentUpon>setup.cs</DependentUpon>
</Compile>
<EmbeddedResource Include="Browser.resx">
<DependentUpon>Browser.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Device\Device.resx">
<DependentUpon>Device.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Form1.resx">
<DependentUpon>Form1.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Message\ErrorForm.resx">
<DependentUpon>ErrorForm.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Message\MessageForm.resx">
<DependentUpon>MessageForm.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Panel\Panel.resx">
<DependentUpon>Panel.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
<DesignTime>True</DesignTime>
</Compile>
<EmbeddedResource Include="setup.resx">
<DependentUpon>setup.cs</DependentUpon>
</EmbeddedResource>
<None Include="ClassDiagram1.cd" />
<None Include="ClassDiagram2.cd" />
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
<None Include="ReadMe.md" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@@ -0,0 +1,170 @@
namespace DoorControl
{
partial class WebSocketForm
{
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
/// <param name="disposing">如果应释放托管资源,为 true否则为 false。</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows
/// <summary>
/// 设计器支持所需的方法 - 不要修改
/// 使用代码编辑器修改此方法的内容。
/// </summary>
private void InitializeComponent()
{
this.rtb_Server = new System.Windows.Forms.RichTextBox();
this.btn_Send = new System.Windows.Forms.Button();
this.txt_server = new System.Windows.Forms.TextBox();
this.txt_msg_serversend = new System.Windows.Forms.TextBox();
this.label2 = new System.Windows.Forms.Label();
this.label3 = new System.Windows.Forms.Label();
this.groupBox1 = new System.Windows.Forms.GroupBox();
this.button1 = new System.Windows.Forms.Button();
this.label1 = new System.Windows.Forms.Label();
this.lis_sessions = new System.Windows.Forms.ListBox();
this.groupBox1.SuspendLayout();
this.SuspendLayout();
//
// rtb_Server
//
this.rtb_Server.Location = new System.Drawing.Point(377, 66);
this.rtb_Server.Name = "rtb_Server";
this.rtb_Server.Size = new System.Drawing.Size(415, 199);
this.rtb_Server.TabIndex = 0;
this.rtb_Server.Text = "";
//
// btn_Send
//
this.btn_Send.Location = new System.Drawing.Point(706, 23);
this.btn_Send.Name = "btn_Send";
this.btn_Send.Size = new System.Drawing.Size(70, 21);
this.btn_Send.TabIndex = 1;
this.btn_Send.Text = "发送";
this.btn_Send.UseVisualStyleBackColor = true;
this.btn_Send.Click += new System.EventHandler(this.btn_Send_Click);
//
// txt_server
//
this.txt_server.Location = new System.Drawing.Point(101, 27);
this.txt_server.Name = "txt_server";
this.txt_server.Size = new System.Drawing.Size(183, 21);
this.txt_server.TabIndex = 2;
this.txt_server.Text = "0.0.0.0:6666";
//
// txt_msg_serversend
//
this.txt_msg_serversend.Location = new System.Drawing.Point(477, 23);
this.txt_msg_serversend.Name = "txt_msg_serversend";
this.txt_msg_serversend.Size = new System.Drawing.Size(210, 21);
this.txt_msg_serversend.TabIndex = 3;
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(424, 27);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(47, 12);
this.label2.TabIndex = 5;
this.label2.Text = "Message";
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(375, 51);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(23, 12);
this.label3.TabIndex = 6;
this.label3.Text = "Log";
//
// groupBox1
//
this.groupBox1.Controls.Add(this.button1);
this.groupBox1.Controls.Add(this.label1);
this.groupBox1.Controls.Add(this.lis_sessions);
this.groupBox1.Controls.Add(this.label3);
this.groupBox1.Controls.Add(this.rtb_Server);
this.groupBox1.Controls.Add(this.label2);
this.groupBox1.Controls.Add(this.btn_Send);
this.groupBox1.Controls.Add(this.txt_server);
this.groupBox1.Controls.Add(this.txt_msg_serversend);
this.groupBox1.Location = new System.Drawing.Point(46, 63);
this.groupBox1.Name = "groupBox1";
this.groupBox1.Size = new System.Drawing.Size(821, 367);
this.groupBox1.TabIndex = 7;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "Server";
//
// button1
//
this.button1.Location = new System.Drawing.Point(290, 26);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(54, 22);
this.button1.TabIndex = 9;
this.button1.Text = "Start";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click_1);
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(36, 30);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(59, 12);
this.label1.TabIndex = 8;
this.label1.Text = "Server IP";
//
// lis_sessions
//
this.lis_sessions.FormattingEnabled = true;
this.lis_sessions.ItemHeight = 12;
this.lis_sessions.Location = new System.Drawing.Point(38, 66);
this.lis_sessions.Name = "lis_sessions";
this.lis_sessions.Size = new System.Drawing.Size(304, 196);
this.lis_sessions.TabIndex = 7;
//
// WebSocketForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(1060, 693);
this.Controls.Add(this.groupBox1);
this.Name = "WebSocketForm";
this.Text = "WebSocketServer";
this.Load += new System.EventHandler(this.Form1_Load);
this.groupBox1.ResumeLayout(false);
this.groupBox1.PerformLayout();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.RichTextBox rtb_Server;
private System.Windows.Forms.Button btn_Send;
private System.Windows.Forms.TextBox txt_server;
private System.Windows.Forms.TextBox txt_msg_serversend;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.GroupBox groupBox1;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.ListBox lis_sessions;
private System.Windows.Forms.Button button1;
}
}

View File

@@ -0,0 +1,162 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Management;
using WebSocketSharp;
using WebSocketSharp.Server;
namespace DoorControl
{
public partial class WebSocketForm : Form
{
WebSocketServer wssv;
private delegate void DelegateStringFun(string log);//代理
public static WebSocketForm frm;
public string text;
public string textclient;
//自身的句柄
public static void SendMessage(String msg)
{
frm.Log(msg);
}
public static void ConnectionAccepted(String id)
{
frm.AddList(id);
}
public static void ConnectionDismiss(String id)
{
frm.RemoveList(id);
}
public static String logtxt = "";
public WebSocketForm()
{
InitializeComponent();
frm = this;
}
public string Getlog()
{
string ret = logtxt;
logtxt = "";
return ret;
}
public class MyEventArg : EventArgs
{
//传递主窗体的数据信息
public string Text { get; set; }
}
private void Form1_Load(object sender, EventArgs e)
{
wssv = new WebSocketServer("ws://" +txt_server.Text);
}
public class Laputa : WebSocketBehavior
{
protected override void OnOpen()
{
base.OnOpen();
SendMessage("Connect from "+Context.Host + " was accepted");
ConnectionAccepted(ID+"|"+ Context.Host.ToString());
}
protected override void OnClose(CloseEventArgs e)
{
SendMessage("Connect from " + Context.Host + " was lost");
ConnectionDismiss(ID + "|" + Context.Host.ToString());
base.OnClose(e);
}
protected override void OnMessage(MessageEventArgs e)
{
string s_data = e.Data;
Logger log=new Logger();
SendMessage("Server Received: "+e.Data);
Console.Write(e.Data);
//Loopback
//Send(e.Data);
}
}
public void Log(string log)
{
if (this.rtb_Server.InvokeRequired)
{
DelegateStringFun d = new DelegateStringFun(Log);
this.Invoke(d, new object[] { log });
}
else
{
text= text + log.Replace("\0","") + "\n";
rtb_Server.Text = text;
}
}
public void AddList(string id)
{
if (this.rtb_Server.InvokeRequired)
{
DelegateStringFun d = new DelegateStringFun(AddList);
this.Invoke(d, new object[] { id });
}
else
{
lis_sessions.Items.Add(id);
}
}
public void RemoveList(string id)
{
if (this.rtb_Server.InvokeRequired)
{
DelegateStringFun d = new DelegateStringFun(RemoveList);
this.Invoke(d, new object[] { id });
}
else
{
lis_sessions.Items.Remove(id);
}
}
private void btn_Send_Click(object sender, EventArgs e)
{
try
{
string s_mixid = lis_sessions.SelectedItem.ToString();
string s_id = s_mixid.Substring(0, s_mixid.IndexOf("|"));
wssv.WebSocketServices["/"].Sessions.SendToAsync(txt_msg_serversend.Text, s_id, null);
}catch
{
MessageBox.Show("请先选择session");
}
}
private void button1_Click_1(object sender, EventArgs e)
{
}
}
}

View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@@ -0,0 +1,97 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{D5A6D030-FDDE-462F-94E5-A78F312107FD}</ProjectGuid>
<OutputType>WinExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>WebSocketServer</RootNamespace>
<AssemblyName>WebSocketServer</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Management" />
<Reference Include="System.Web" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Deployment" />
<Reference Include="System.Drawing" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
<Reference Include="websocket-sharp, Version=1.0.1.0, Culture=neutral, PublicKeyToken=5660b08a1845a91e, processorArchitecture=MSIL">
<HintPath>..\packages\WebSocketSharp-netstandard.1.0.1\lib\net45\websocket-sharp.dll</HintPath>
<Private>True</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="WebSocketForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="WebSocketForm.Designer.cs">
<DependentUpon>WebSocketForm.cs</DependentUpon>
</Compile>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<EmbeddedResource Include="WebSocketForm.resx">
<DependentUpon>WebSocketForm.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<None Include="packages.config" />
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

209
UdpPlugWebsocket/setup.cs Normal file
View File

@@ -0,0 +1,209 @@
///<summary>
         ///模块编号20180730
///模块名:本地配置模块
         ///作用:生成本地的存储信息
         ///作者Miuser
         ///编写日期20180730
///版本1.0
///</summary>
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
//Common reference
using System.Threading;
using System.Net.Sockets;
using System.Net;
using System.Xml.Serialization;
using System.IO;
using System.Text.RegularExpressions;
//mongo DB
using System.Security.Cryptography;
namespace UdpPlugWebsocket
{
public partial class SetupForm : Form
{
public static Config cfg;
public SetupForm(ref Config config)
{
cfg = config;
InitializeComponent();
}
public SetupForm()
{
cfg = Config.LoadfromFile("config.xml");
InitializeComponent();
}
private void SetupForm_Load(object sender, EventArgs e)
{
prg_config.SelectedObject = cfg;
}
private void btn_save_Click(object sender, EventArgs e)
{
cfg.SavetoFile("config.xml");
Application.Restart();
}
private void SetupForm_FormClosed(object sender, FormClosedEventArgs e)
{
}
}
public class Config
{
[CategoryAttribute("1.Websocket Port")]
public String BrowserPort { get; set; }
[CategoryAttribute("2.UDP Port")]
public int UDPPort { get; set; }
[CategoryAttribute("2.UDP TTL (ms)")]
public int UDPTTL { get; set; }
[CategoryAttribute("3.NODE TTL (ms)")]
public int NODETTL { get; set; }
//记录日志
//[CategoryAttribute("4.Enable File Log")]
//public bool EnableFileLog { get; set; }
//屏幕显示
[CategoryAttribute("4.Enable Screen Log")]
public bool EnableScreenLog { get; set; }
public int SavetoFile(String filename)
{
try
{
XmlSerializer serializer = new XmlSerializer(typeof(Config));
TextWriter writer = new StreamWriter(filename);
serializer.Serialize(writer, this);
writer.Close();
}
catch (Exception ee)
{
MessageBox.Show(ee.StackTrace, ee.Message);
return 0;
}
return 1;
}
public static Config LoadfromFile(String filename)
{
try
{
Config sptr;
XmlSerializer serializer = new XmlSerializer(typeof(Config));
TextReader reader = new StreamReader(filename);
sptr = (Config)(serializer.Deserialize(reader));
reader.Close();
return sptr;
}
catch (Exception ee)
{
MessageBox.Show(ee.StackTrace, ee.Message);
return new Config();
}
}
}
public class Encryption
{
/// <summary>
/// 作用将字符串内容转化为16进制数据编码其逆过程是Decode
/// 参数说明:
/// strEncode 需要转化的原始字符串
/// 转换的过程是直接把字符转换成Unicode字符,比如数字"3"-->0033,汉字"我"-->U+6211
/// 函数decode的过程是encode的逆过程.
/// </summary>
public static string Encode(string strEncode)
{
string strReturn = "";// 存储转换后的编码
try
{
foreach (short shortx in strEncode.ToCharArray())
{
strReturn += shortx.ToString("X4");
}
}
catch { }
return strReturn;
}
/// <summary>
/// 作用将16进制数据编码转化为字符串是Encode的逆过程
/// </summary>
public static string Decode(string strDecode)
{
string sResult = "";
try
{
for (int i = 0; i < strDecode.Length / 4; i++)
{
sResult += (char)short.Parse(strDecode.Substring(i * 4, 4),
global::System.Globalization.NumberStyles.HexNumber);
}
}
catch { }
return sResult;
}
/// <summary>
/// 将数字转换成16进制字符串后两位加入随机字符其可逆方法为DecodeForNum
/// </summary>
public static string EncodeForNum(int id)
{
//用户加上起始位置后的
int startUserIndex = id;
//转换成16进制
string hexStr = Convert.ToString(startUserIndex, 16);
//后面两位加入随机数
string randomchars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
string tmpstr = "";
//整除的后得到的数可能大于被除数
tmpstr += randomchars[(id / randomchars.Length) > randomchars.Length ? randomchars.Length - 1 : (id / randomchars.Length)];
//余数不可能大于被除数
tmpstr += randomchars[(id % randomchars.Length) > randomchars.Length ? randomchars.Length - 1 : (id % randomchars.Length)];
//返回拼接后的字符,转成大写
string retStr = (hexStr + tmpstr).ToUpper();
return retStr;
}
/// <summary>
/// 解密16进制字符串此方法只适合后面两位有随机字符的
/// </summary>
public static int DecodeForNum(string strDecode)
{
if (strDecode.Length > 2)
{
strDecode = strDecode.Substring(0, strDecode.Length - 2);
return Convert.ToInt32(strDecode, 16);
}
return 0;
}
}
}

75
UdpPlugWebsocket/setup.designer.cs generated Normal file
View File

@@ -0,0 +1,75 @@
namespace UdpPlugWebsocket
{
partial class SetupForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.btn_save = new System.Windows.Forms.Button();
this.prg_config = new System.Windows.Forms.PropertyGrid();
this.SuspendLayout();
//
// btn_save
//
this.btn_save.Location = new System.Drawing.Point(443, 641);
this.btn_save.Margin = new System.Windows.Forms.Padding(4);
this.btn_save.Name = "btn_save";
this.btn_save.Size = new System.Drawing.Size(178, 30);
this.btn_save.TabIndex = 3;
this.btn_save.Text = "保存并重启";
this.btn_save.UseVisualStyleBackColor = true;
this.btn_save.Click += new System.EventHandler(this.btn_save_Click);
//
// prg_config
//
this.prg_config.Dock = System.Windows.Forms.DockStyle.Fill;
this.prg_config.Location = new System.Drawing.Point(0, 0);
this.prg_config.Margin = new System.Windows.Forms.Padding(4);
this.prg_config.Name = "prg_config";
this.prg_config.Size = new System.Drawing.Size(663, 684);
this.prg_config.TabIndex = 2;
//
// SetupForm
//
this.AcceptButton = this.btn_save;
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
this.ClientSize = new System.Drawing.Size(663, 684);
this.Controls.Add(this.btn_save);
this.Controls.Add(this.prg_config);
this.Name = "SetupForm";
this.Text = "setup";
this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.SetupForm_FormClosed);
this.Load += new System.EventHandler(this.SetupForm_Load);
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Button btn_save;
private System.Windows.Forms.PropertyGrid prg_config;
}
}

120
UdpPlugWebsocket/setup.resx Normal file
View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

Binary file not shown.

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
</startup>
</configuration>

Binary file not shown.

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
</startup>
</configuration>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
</assembly>

View File

@@ -0,0 +1,37 @@
<html>
<head>
<script>
var socket;
if ("WebSocket" in window) {
var ws = new WebSocket("ws://127.0.0.1:9000");
socket = ws;
ws.onopen = function() {
console.log('连接成功');
};
ws.onmessage = function(evt) {
var received_msg = evt.data;
document.getElementById("showMes").value+=evt.data+"\n";
};
ws.onclose = function() {
alert("断开了连接");
};
} else {
alert("浏览器不支持WebSocket");
}
function login(){
var message=document.getElementById("name").value+":"+document.getElementById("mes").value;
socket.send(message);
}
</script>
</head>
<body>
<textarea rows="3" cols="30" id="showMes" style="width:300px;height:500px;"></textarea>
<br/>
<label>名称</label>
<input type="text" id="name"/>
<br/>
<label>消息</label>
<input type="text" id="mes"/>
<button onclick="login();">发送</button>
</body>
</html>

BIN
bin_UPWS/client.exe Normal file

Binary file not shown.

9
bin_UPWS/config.xml Normal file
View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<Config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<BrowserPort>9000</BrowserPort>
<UDPPort>7101</UDPPort>
<UDPTTL>30000</UDPTTL>
<NODETTL>20000</NODETTL>
<EnableFileLog>true</EnableFileLog>
<EnableScreenLog>true</EnableScreenLog>
</Config>

File diff suppressed because one or more lines are too long

2
bin_UPWS/main/jquery-1.8.3.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
!function(a,b){"function"==typeof define&&define.amd?define([],b):"undefined"!=typeof module&&module.exports?module.exports=b():a.ReconnectingWebSocket=b()}(this,function(){function a(b,c,d){function l(a,b){var c=document.createEvent("CustomEvent");return c.initCustomEvent(a,!1,!1,b),c}var e={debug:!1,automaticOpen:!0,reconnectInterval:1e3,maxReconnectInterval:3e4,reconnectDecay:1.5,timeoutInterval:2e3};d||(d={});for(var f in e)this[f]="undefined"!=typeof d[f]?d[f]:e[f];this.url=b,this.reconnectAttempts=0,this.readyState=WebSocket.CONNECTING,this.protocol=null;var h,g=this,i=!1,j=!1,k=document.createElement("div");k.addEventListener("open",function(a){g.onopen(a)}),k.addEventListener("close",function(a){g.onclose(a)}),k.addEventListener("connecting",function(a){g.onconnecting(a)}),k.addEventListener("message",function(a){g.onmessage(a)}),k.addEventListener("error",function(a){g.onerror(a)}),this.addEventListener=k.addEventListener.bind(k),this.removeEventListener=k.removeEventListener.bind(k),this.dispatchEvent=k.dispatchEvent.bind(k),this.open=function(b){h=new WebSocket(g.url,c||[]),b||k.dispatchEvent(l("connecting")),(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","attempt-connect",g.url);var d=h,e=setTimeout(function(){(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","connection-timeout",g.url),j=!0,d.close(),j=!1},g.timeoutInterval);h.onopen=function(){clearTimeout(e),(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onopen",g.url),g.protocol=h.protocol,g.readyState=WebSocket.OPEN,g.reconnectAttempts=0;var d=l("open");d.isReconnect=b,b=!1,k.dispatchEvent(d)},h.onclose=function(c){if(clearTimeout(e),h=null,i)g.readyState=WebSocket.CLOSED,k.dispatchEvent(l("close"));else{g.readyState=WebSocket.CONNECTING;var d=l("connecting");d.code=c.code,d.reason=c.reason,d.wasClean=c.wasClean,k.dispatchEvent(d),b||j||((g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onclose",g.url),k.dispatchEvent(l("close")));var e=g.reconnectInterval*Math.pow(g.reconnectDecay,g.reconnectAttempts);setTimeout(function(){g.reconnectAttempts++,g.open(!0)},e>g.maxReconnectInterval?g.maxReconnectInterval:e)}},h.onmessage=function(b){(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onmessage",g.url,b.data);var c=l("message");c.data=b.data,k.dispatchEvent(c)},h.onerror=function(b){(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onerror",g.url,b),k.dispatchEvent(l("error"))}},1==this.automaticOpen&&this.open(!1),this.send=function(b){if(h)return(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","send",g.url,b),h.send(b);throw"INVALID_STATE_ERR : Pausing to reconnect websocket"},this.close=function(a,b){"undefined"==typeof a&&(a=1e3),i=!0,h&&h.close(a,b)},this.refresh=function(){h&&h.close()}}return a.prototype.onopen=function(){},a.prototype.onclose=function(){},a.prototype.onconnecting=function(){},a.prototype.onmessage=function(){},a.prototype.onerror=function(){},a.debugAll=!1,a.CONNECTING=WebSocket.CONNECTING,a.OPEN=WebSocket.OPEN,a.CLOSING=WebSocket.CLOSING,a.CLOSED=WebSocket.CLOSED,a});

BIN
bin_UPWS/udp.dll Normal file

Binary file not shown.

Binary file not shown.

10326
bin_UPWS/websocket-sharp.xml Normal file

File diff suppressed because it is too large Load Diff

BIN
document/clienttest.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

111
document/function.md Normal file
View File

@@ -0,0 +1,111 @@
## 工作原理 ##
本项目仿照硬件设计思想基于模块化原理开发分为UDP连接池、Websocket连接池、交换面板三个大的模块。各个模块相互独立除消息通讯外无模块间耦合。每个模块的功能部分和UI部分彼此亦基本独立。UDP和Websocket两个模块采用.net内置的Action对象与交换面板进行异步消息通讯。
**Endpoint是外部连接的唯一索引。**
**Endpoint字符串的格式为"XXX.XXX.XXX.XXX:XXXX" 前四组三位数为IP地址最后一组四位数为远程端口号**
UDP连接池是一个UDP连接管理模块默认开放7101端口供外部连接接入。每个成功连接的设备或模拟程序会被分配一个SocketConnectionSocketConnction通过Tag属性进行索引Tag的值默认为Endpoint并添加到一个列表里该列表可以通过 GetConnectionList()获得也可以使用GetTheConnection(Func<SocketConnection, bool> predicate)函数通过LINQ语句进行指定连接的查询。
以下为UDP连接池对外暴露的函数接口
/// 开始服务,监听客户端
public void StartServer()
///按照IP地址和端口发送字符串
public void Send(string EndpointString, byte[] bytes)
///开始异步监听端口
public void Listening()
/// 关闭指定客户端连接
public void CloseConnection(SocketConnection theConnection)
/// 添加客户端连接
public void AddConnection(SocketConnection theConnection)
/// 通过条件获取客户端连接列表
public IEnumerable<SocketConnection> GetConnectionList(Func<SocketConnection, bool> predicate)
/// 获取所有客户端连接列表
public IEnumerable<SocketConnection> GetConnectionList()
/// 寻找特定条件的客户端连接
public SocketConnection GetTheConnection(Func<SocketConnection, bool> predicate)
/// 获取客户端连接数
public int GetConnectionCount()
以下为UDP连接池对外暴露的事件接口
/// 服务启动后执行
public Action<SocketServer> HandleServerStarted { get; set; }
/// 当新客户端连接后执行
public Action<SocketServer, SocketConnection> HandleNewClientConnected { get; set; }
/// 客户端连接接受新的消息后调用
public Action<byte[], SocketConnection, SocketServer> HandleRecMsg { get; set; }
/// 客户端连接发送消息后回调
public Action<byte[], SocketConnection, SocketServer> HandleSendMsg { get; set; }
/// 客户端连接关闭后回调
public Action<SocketConnection, SocketServer> HandleClientClose { get; set; }
/// 异常处理程序
public Action<Exception> HandleException { get; set; }
Websocket连接池是一个websocket管理模块默认开放9000端口供外部连接接入。每个成功联机的APP页面会分配一个唯一的sessionWebsocket对象维护了一个Sessions列表该列表可以通过Where表达式通过LINQ语句筛选。
以下为Websocket连接池对外暴露的函数接口
/// 发送字节
public void Send(string EndpointString,byte[] bytes)
以下为Websocket连接池对外暴露的事件接口
/// 当新客户端连接后执行
public Action<WebSocketSharp.Net.WebSockets.WebSocketContext> HandleNewWebSocketClientConnected { get; set; }
/// 客户端连接接受新的消息后调用
public Action<byte[], WebSocketSharp.Net.WebSockets.WebSocketContext> HandleWebSocketRecMsg { get; set; }
/// 客户端连接发送消息后回调
public Action<byte[], WebSocketSharp.Net.WebSockets.WebSocketContext> HandleWebSocketSendMsg { get; set; }
/// 客户端连接关闭后回调
public Action<WebSocketSharp.Net.WebSockets.WebSocketContext> HandleWebSocketClientClose { get; set; }
交换面板Switch是一个消息转发器他接收UDP与Websocket传入的数据包并根据数据包中ID部分对信息进行分组转发既收到信息后向ID相同的全部在线端口转发收到的原始消息。MM是标识设备控制权限的供上层应用使用服务端不做处理。
对于每一个IDSwitch会生成一个结点Node该节点存储了含有相同ID的Websocket和UDP连接信息 1TTL剩余生存时间2 Endpoint客户连接端口每个结点自带生命计时器靠上层程序定期执行Reactive()函数保活。
private void Init_Forms()
{
try
{
...
//连接模块与系统终端窗口
Device.Instance.HandleMessage += new Action<string>(MessageForm.Log);
Device.Instance.HandleError += new Action<string>(ErrorForm.Log);
Browser.Instance.HandleMessage += new Action<string>(MessageForm.Log);
Browser.Instance.HandleError += new Action<string>(ErrorForm.Log);
//连接Pannel数据输入端 Device->Panel
Device.Instance.server.HandleRecMsg += new Action<byte[], Miuser.NUDP.Sockets.SocketConnection, Miuser.NUDP.Sockets.SocketServer>((bytes, conn, server) =>
{
Panel.Instance.sw.SendFromUDP(bytes, conn.Tag.ToString());
});
//连接Pannel数据输入端 Browser->Panel
Browser.Instance.HandleWebSocketRecMsg += new Action<byte[], WebSocketSharp.Net.WebSockets.WebSocketContext>((bytes, context) =>
{
Panel.Instance.sw.SendFromWebSocket(bytes, context.UserEndPoint.ToString());
});
//连接Pannel数据输出端 Panel->Device
Panel.Instance.sw.HandleUDPSendMsg += new Action<byte[], string>((bytes, endpointString) =>
{
Device.Instance.server.Send(endpointString, bytes);
});
//连接Panel数据输出端 Panel->Browser
Panel.Instance.sw.HandleWebSocketSendMsg += new Action<byte[], string>((bytes, endpointString)=>
{
Browser.Instance.Send(endpointString, bytes);
});
...
}
catch (Exception e)
{
MessageBox.Show(e.StackTrace,"系统模块初始化错误");
}
}

BIN
document/packageintro.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

BIN
document/preview.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

BIN
document/webtest.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

View File

@@ -0,0 +1,26 @@
using System.Reflection;
using System.Runtime.CompilerServices;
// Information about this assembly is defined by the following attributes.
// Change them to the values specific to your project.
[assembly: AssemblyTitle("websocket-sharp")]
[assembly: AssemblyDescription("A C# implementation of the WebSocket protocol client and server")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("websocket-sharp.dll")]
[assembly: AssemblyCopyright("sta.blockhead")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
[assembly: AssemblyVersion("1.0.2.*")]
// The following attributes are used to specify the signing key for the assembly,
// if desired. See the Mono documentation for more information about signing.
//[assembly: AssemblyDelaySign(false)]
//[assembly: AssemblyKeyFile("")]

View File

@@ -0,0 +1,47 @@
#region License
/*
* ByteOrder.cs
*
* The MIT License
*
* Copyright (c) 2012-2015 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
using System;
namespace WebSocketSharp
{
/// <summary>
/// Specifies the byte order.
/// </summary>
public enum ByteOrder
{
/// <summary>
/// Specifies Little-endian.
/// </summary>
Little,
/// <summary>
/// Specifies Big-endian.
/// </summary>
Big
}
}

View File

@@ -0,0 +1,142 @@
#region License
/*
* CloseEventArgs.cs
*
* The MIT License
*
* Copyright (c) 2012-2016 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
using System;
namespace WebSocketSharp
{
/// <summary>
/// Represents the event data for the <see cref="WebSocket.OnClose"/> event.
/// </summary>
/// <remarks>
/// <para>
/// That event occurs when the WebSocket connection has been closed.
/// </para>
/// <para>
/// If you would like to get the reason for the close, you should access
/// the <see cref="Code"/> or <see cref="Reason"/> property.
/// </para>
/// </remarks>
public class CloseEventArgs : EventArgs
{
#region Private Fields
private bool _clean;
private PayloadData _payloadData;
#endregion
#region Internal Constructors
internal CloseEventArgs ()
{
_payloadData = PayloadData.Empty;
}
internal CloseEventArgs (ushort code)
: this (code, null)
{
}
internal CloseEventArgs (CloseStatusCode code)
: this ((ushort) code, null)
{
}
internal CloseEventArgs (PayloadData payloadData)
{
_payloadData = payloadData;
}
internal CloseEventArgs (ushort code, string reason)
{
_payloadData = new PayloadData (code, reason);
}
internal CloseEventArgs (CloseStatusCode code, string reason)
: this ((ushort) code, reason)
{
}
#endregion
#region Internal Properties
internal PayloadData PayloadData {
get {
return _payloadData;
}
}
#endregion
#region Public Properties
/// <summary>
/// Gets the status code for the close.
/// </summary>
/// <value>
/// A <see cref="ushort"/> that represents the status code for the close if any.
/// </value>
public ushort Code {
get {
return _payloadData.Code;
}
}
/// <summary>
/// Gets the reason for the close.
/// </summary>
/// <value>
/// A <see cref="string"/> that represents the reason for the close if any.
/// </value>
public string Reason {
get {
return _payloadData.Reason ?? String.Empty;
}
}
/// <summary>
/// Gets a value indicating whether the connection has been closed cleanly.
/// </summary>
/// <value>
/// <c>true</c> if the connection has been closed cleanly; otherwise, <c>false</c>.
/// </value>
public bool WasClean {
get {
return _clean;
}
internal set {
_clean = value;
}
}
#endregion
}
}

View File

@@ -0,0 +1,120 @@
#region License
/*
* CloseStatusCode.cs
*
* The MIT License
*
* Copyright (c) 2012-2016 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
using System;
namespace WebSocketSharp
{
/// <summary>
/// Indicates the status code for the WebSocket connection close.
/// </summary>
/// <remarks>
/// <para>
/// The values of this enumeration are defined in
/// <see href="http://tools.ietf.org/html/rfc6455#section-7.4">
/// Section 7.4</see> of RFC 6455.
/// </para>
/// <para>
/// "Reserved value" cannot be sent as a status code in
/// closing handshake by an endpoint.
/// </para>
/// </remarks>
public enum CloseStatusCode : ushort
{
/// <summary>
/// Equivalent to close status 1000. Indicates normal close.
/// </summary>
Normal = 1000,
/// <summary>
/// Equivalent to close status 1001. Indicates that an endpoint is
/// going away.
/// </summary>
Away = 1001,
/// <summary>
/// Equivalent to close status 1002. Indicates that an endpoint is
/// terminating the connection due to a protocol error.
/// </summary>
ProtocolError = 1002,
/// <summary>
/// Equivalent to close status 1003. Indicates that an endpoint is
/// terminating the connection because it has received a type of
/// data that it cannot accept.
/// </summary>
UnsupportedData = 1003,
/// <summary>
/// Equivalent to close status 1004. Still undefined. A Reserved value.
/// </summary>
Undefined = 1004,
/// <summary>
/// Equivalent to close status 1005. Indicates that no status code was
/// actually present. A Reserved value.
/// </summary>
NoStatus = 1005,
/// <summary>
/// Equivalent to close status 1006. Indicates that the connection was
/// closed abnormally. A Reserved value.
/// </summary>
Abnormal = 1006,
/// <summary>
/// Equivalent to close status 1007. Indicates that an endpoint is
/// terminating the connection because it has received a message that
/// contains data that is not consistent with the type of the message.
/// </summary>
InvalidData = 1007,
/// <summary>
/// Equivalent to close status 1008. Indicates that an endpoint is
/// terminating the connection because it has received a message that
/// violates its policy.
/// </summary>
PolicyViolation = 1008,
/// <summary>
/// Equivalent to close status 1009. Indicates that an endpoint is
/// terminating the connection because it has received a message that
/// is too big to process.
/// </summary>
TooBig = 1009,
/// <summary>
/// Equivalent to close status 1010. Indicates that a client is
/// terminating the connection because it has expected the server to
/// negotiate one or more extension, but the server did not return
/// them in the handshake response.
/// </summary>
MandatoryExtension = 1010,
/// <summary>
/// Equivalent to close status 1011. Indicates that a server is
/// terminating the connection because it has encountered an unexpected
/// condition that prevented it from fulfilling the request.
/// </summary>
ServerError = 1011,
/// <summary>
/// Equivalent to close status 1015. Indicates that the connection was
/// closed due to a failure to perform a TLS handshake. A Reserved value.
/// </summary>
TlsHandshakeFailure = 1015
}
}

View File

@@ -0,0 +1,52 @@
#region License
/*
* CompressionMethod.cs
*
* The MIT License
*
* Copyright (c) 2013-2017 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
using System;
namespace WebSocketSharp
{
/// <summary>
/// Specifies the method for compression.
/// </summary>
/// <remarks>
/// The methods are defined in
/// <see href="https://tools.ietf.org/html/rfc7692">
/// Compression Extensions for WebSocket</see>.
/// </remarks>
public enum CompressionMethod : byte
{
/// <summary>
/// Specifies no compression.
/// </summary>
None,
/// <summary>
/// Specifies DEFLATE.
/// </summary>
Deflate
}
}

View File

@@ -0,0 +1,109 @@
#region License
/*
* ErrorEventArgs.cs
*
* The MIT License
*
* Copyright (c) 2012-2016 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
#region Contributors
/*
* Contributors:
* - Frank Razenberg <frank@zzattack.org>
*/
#endregion
using System;
namespace WebSocketSharp
{
/// <summary>
/// Represents the event data for the <see cref="WebSocket.OnError"/> event.
/// </summary>
/// <remarks>
/// <para>
/// That event occurs when the <see cref="WebSocket"/> gets an error.
/// </para>
/// <para>
/// If you would like to get the error message, you should access
/// the <see cref="ErrorEventArgs.Message"/> property.
/// </para>
/// <para>
/// And if the error is due to an exception, you can get it by accessing
/// the <see cref="ErrorEventArgs.Exception"/> property.
/// </para>
/// </remarks>
public class ErrorEventArgs : EventArgs
{
#region Private Fields
private Exception _exception;
private string _message;
#endregion
#region Internal Constructors
internal ErrorEventArgs (string message)
: this (message, null)
{
}
internal ErrorEventArgs (string message, Exception exception)
{
_message = message;
_exception = exception;
}
#endregion
#region Public Properties
/// <summary>
/// Gets the exception that caused the error.
/// </summary>
/// <value>
/// An <see cref="System.Exception"/> instance that represents the cause of
/// the error if it is due to an exception; otherwise, <see langword="null"/>.
/// </value>
public Exception Exception {
get {
return _exception;
}
}
/// <summary>
/// Gets the error message.
/// </summary>
/// <value>
/// A <see cref="string"/> that represents the error message.
/// </value>
public string Message {
get {
return _message;
}
}
#endregion
}
}

2032
websocket-sharp/Ext.cs Normal file

File diff suppressed because it is too large Load Diff

51
websocket-sharp/Fin.cs Normal file
View File

@@ -0,0 +1,51 @@
#region License
/*
* Fin.cs
*
* The MIT License
*
* Copyright (c) 2012-2015 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
using System;
namespace WebSocketSharp
{
/// <summary>
/// Indicates whether a WebSocket frame is the final frame of a message.
/// </summary>
/// <remarks>
/// The values of this enumeration are defined in
/// <see href="http://tools.ietf.org/html/rfc6455#section-5.2">Section 5.2</see> of RFC 6455.
/// </remarks>
internal enum Fin : byte
{
/// <summary>
/// Equivalent to numeric value 0. Indicates more frames of a message follow.
/// </summary>
More = 0x0,
/// <summary>
/// Equivalent to numeric value 1. Indicates the final frame of a message.
/// </summary>
Final = 0x1
}
}

208
websocket-sharp/HttpBase.cs Normal file
View File

@@ -0,0 +1,208 @@
#region License
/*
* HttpBase.cs
*
* The MIT License
*
* Copyright (c) 2012-2014 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Text;
using System.Threading;
using WebSocketSharp.Net;
namespace WebSocketSharp
{
internal abstract class HttpBase
{
#region Private Fields
private NameValueCollection _headers;
private const int _headersMaxLength = 8192;
private Version _version;
#endregion
#region Internal Fields
internal byte[] EntityBodyData;
#endregion
#region Protected Fields
protected const string CrLf = "\r\n";
#endregion
#region Protected Constructors
protected HttpBase (Version version, NameValueCollection headers)
{
_version = version;
_headers = headers;
}
#endregion
#region Public Properties
public string EntityBody {
get {
if (EntityBodyData == null || EntityBodyData.LongLength == 0)
return String.Empty;
Encoding enc = null;
var contentType = _headers["Content-Type"];
if (contentType != null && contentType.Length > 0)
enc = HttpUtility.GetEncoding (contentType);
return (enc ?? Encoding.UTF8).GetString (EntityBodyData);
}
}
public NameValueCollection Headers {
get {
return _headers;
}
}
public Version ProtocolVersion {
get {
return _version;
}
}
#endregion
#region Private Methods
private static byte[] readEntityBody (Stream stream, string length)
{
long len;
if (!Int64.TryParse (length, out len))
throw new ArgumentException ("Cannot be parsed.", "length");
if (len < 0)
throw new ArgumentOutOfRangeException ("length", "Less than zero.");
return len > 1024
? stream.ReadBytes (len, 1024)
: len > 0
? stream.ReadBytes ((int) len)
: null;
}
private static string[] readHeaders (Stream stream, int maxLength)
{
var buff = new List<byte> ();
var cnt = 0;
Action<int> add = i => {
if (i == -1)
throw new EndOfStreamException ("The header cannot be read from the data source.");
buff.Add ((byte) i);
cnt++;
};
var read = false;
while (cnt < maxLength) {
if (stream.ReadByte ().EqualsWith ('\r', add) &&
stream.ReadByte ().EqualsWith ('\n', add) &&
stream.ReadByte ().EqualsWith ('\r', add) &&
stream.ReadByte ().EqualsWith ('\n', add)) {
read = true;
break;
}
}
if (!read)
throw new WebSocketException ("The length of header part is greater than the max length.");
return Encoding.UTF8.GetString (buff.ToArray ())
.Replace (CrLf + " ", " ")
.Replace (CrLf + "\t", " ")
.Split (new[] { CrLf }, StringSplitOptions.RemoveEmptyEntries);
}
#endregion
#region Protected Methods
protected static T Read<T> (Stream stream, Func<string[], T> parser, int millisecondsTimeout)
where T : HttpBase
{
var timeout = false;
var timer = new Timer (
state => {
timeout = true;
stream.Close ();
},
null,
millisecondsTimeout,
-1);
T http = null;
Exception exception = null;
try {
http = parser (readHeaders (stream, _headersMaxLength));
var contentLen = http.Headers["Content-Length"];
if (contentLen != null && contentLen.Length > 0)
http.EntityBodyData = readEntityBody (stream, contentLen);
}
catch (Exception ex) {
exception = ex;
}
finally {
timer.Change (-1, -1);
timer.Dispose ();
}
var msg = timeout
? "A timeout has occurred while reading an HTTP request/response."
: exception != null
? "An exception has occurred while reading an HTTP request/response."
: null;
if (msg != null)
throw new WebSocketException (msg, exception);
return http;
}
#endregion
#region Public Methods
public byte[] ToByteArray ()
{
return Encoding.UTF8.GetBytes (ToString ());
}
#endregion
}
}

View File

@@ -0,0 +1,217 @@
#region License
/*
* HttpRequest.cs
*
* The MIT License
*
* Copyright (c) 2012-2015 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
#region Contributors
/*
* Contributors:
* - David Burhans
*/
#endregion
using System;
using System.Collections.Specialized;
using System.IO;
using System.Text;
using WebSocketSharp.Net;
namespace WebSocketSharp
{
internal class HttpRequest : HttpBase
{
#region Private Fields
private CookieCollection _cookies;
private string _method;
private string _uri;
#endregion
#region Private Constructors
private HttpRequest (string method, string uri, Version version, NameValueCollection headers)
: base (version, headers)
{
_method = method;
_uri = uri;
}
#endregion
#region Internal Constructors
internal HttpRequest (string method, string uri)
: this (method, uri, HttpVersion.Version11, new NameValueCollection ())
{
Headers["User-Agent"] = "websocket-sharp/1.0";
}
#endregion
#region Public Properties
public AuthenticationResponse AuthenticationResponse {
get {
var res = Headers["Authorization"];
return res != null && res.Length > 0
? AuthenticationResponse.Parse (res)
: null;
}
}
public CookieCollection Cookies {
get {
if (_cookies == null)
_cookies = Headers.GetCookies (false);
return _cookies;
}
}
public string HttpMethod {
get {
return _method;
}
}
public bool IsWebSocketRequest {
get {
return _method == "GET"
&& ProtocolVersion > HttpVersion.Version10
&& Headers.Upgrades ("websocket");
}
}
public string RequestUri {
get {
return _uri;
}
}
#endregion
#region Internal Methods
internal static HttpRequest CreateConnectRequest (Uri uri)
{
var host = uri.DnsSafeHost;
var port = uri.Port;
var authority = String.Format ("{0}:{1}", host, port);
var req = new HttpRequest ("CONNECT", authority);
req.Headers["Host"] = port == 80 ? host : authority;
return req;
}
internal static HttpRequest CreateWebSocketRequest (Uri uri)
{
var req = new HttpRequest ("GET", uri.PathAndQuery);
var headers = req.Headers;
// Only includes a port number in the Host header value if it's non-default.
// See: https://tools.ietf.org/html/rfc6455#page-17
var port = uri.Port;
var schm = uri.Scheme;
headers["Host"] = (port == 80 && schm == "ws") || (port == 443 && schm == "wss")
? uri.DnsSafeHost
: uri.Authority;
headers["Upgrade"] = "websocket";
headers["Connection"] = "Upgrade";
return req;
}
internal HttpResponse GetResponse (Stream stream, int millisecondsTimeout)
{
var buff = ToByteArray ();
stream.Write (buff, 0, buff.Length);
return Read<HttpResponse> (stream, HttpResponse.Parse, millisecondsTimeout);
}
internal static HttpRequest Parse (string[] headerParts)
{
var requestLine = headerParts[0].Split (new[] { ' ' }, 3);
if (requestLine.Length != 3)
throw new ArgumentException ("Invalid request line: " + headerParts[0]);
var headers = new WebHeaderCollection ();
for (int i = 1; i < headerParts.Length; i++)
headers.InternalSet (headerParts[i], false);
return new HttpRequest (
requestLine[0], requestLine[1], new Version (requestLine[2].Substring (5)), headers);
}
internal static HttpRequest Read (Stream stream, int millisecondsTimeout)
{
return Read<HttpRequest> (stream, Parse, millisecondsTimeout);
}
#endregion
#region Public Methods
public void SetCookies (CookieCollection cookies)
{
if (cookies == null || cookies.Count == 0)
return;
var buff = new StringBuilder (64);
foreach (var cookie in cookies.Sorted)
if (!cookie.Expired)
buff.AppendFormat ("{0}; ", cookie.ToString ());
var len = buff.Length;
if (len > 2) {
buff.Length = len - 2;
Headers["Cookie"] = buff.ToString ();
}
}
public override string ToString ()
{
var output = new StringBuilder (64);
output.AppendFormat ("{0} {1} HTTP/{2}{3}", _method, _uri, ProtocolVersion, CrLf);
var headers = Headers;
foreach (var key in headers.AllKeys)
output.AppendFormat ("{0}: {1}{2}", key, headers[key], CrLf);
output.Append (CrLf);
var entity = EntityBody;
if (entity.Length > 0)
output.Append (entity);
return output.ToString ();
}
#endregion
}
}

View File

@@ -0,0 +1,209 @@
#region License
/*
* HttpResponse.cs
*
* The MIT License
*
* Copyright (c) 2012-2014 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
using System;
using System.Collections.Specialized;
using System.IO;
using System.Text;
using WebSocketSharp.Net;
namespace WebSocketSharp
{
internal class HttpResponse : HttpBase
{
#region Private Fields
private string _code;
private string _reason;
#endregion
#region Private Constructors
private HttpResponse (string code, string reason, Version version, NameValueCollection headers)
: base (version, headers)
{
_code = code;
_reason = reason;
}
#endregion
#region Internal Constructors
internal HttpResponse (HttpStatusCode code)
: this (code, code.GetDescription ())
{
}
internal HttpResponse (HttpStatusCode code, string reason)
: this (((int) code).ToString (), reason, HttpVersion.Version11, new NameValueCollection ())
{
Headers["Server"] = "websocket-sharp/1.0";
}
#endregion
#region Public Properties
public CookieCollection Cookies {
get {
return Headers.GetCookies (true);
}
}
public bool HasConnectionClose {
get {
var comparison = StringComparison.OrdinalIgnoreCase;
return Headers.Contains ("Connection", "close", comparison);
}
}
public bool IsProxyAuthenticationRequired {
get {
return _code == "407";
}
}
public bool IsRedirect {
get {
return _code == "301" || _code == "302";
}
}
public bool IsUnauthorized {
get {
return _code == "401";
}
}
public bool IsWebSocketResponse {
get {
return ProtocolVersion > HttpVersion.Version10
&& _code == "101"
&& Headers.Upgrades ("websocket");
}
}
public string Reason {
get {
return _reason;
}
}
public string StatusCode {
get {
return _code;
}
}
#endregion
#region Internal Methods
internal static HttpResponse CreateCloseResponse (HttpStatusCode code)
{
var res = new HttpResponse (code);
res.Headers["Connection"] = "close";
return res;
}
internal static HttpResponse CreateUnauthorizedResponse (string challenge)
{
var res = new HttpResponse (HttpStatusCode.Unauthorized);
res.Headers["WWW-Authenticate"] = challenge;
return res;
}
internal static HttpResponse CreateWebSocketResponse ()
{
var res = new HttpResponse (HttpStatusCode.SwitchingProtocols);
var headers = res.Headers;
headers["Upgrade"] = "websocket";
headers["Connection"] = "Upgrade";
return res;
}
internal static HttpResponse Parse (string[] headerParts)
{
var statusLine = headerParts[0].Split (new[] { ' ' }, 3);
if (statusLine.Length != 3)
throw new ArgumentException ("Invalid status line: " + headerParts[0]);
var headers = new WebHeaderCollection ();
for (int i = 1; i < headerParts.Length; i++)
headers.InternalSet (headerParts[i], true);
return new HttpResponse (
statusLine[1], statusLine[2], new Version (statusLine[0].Substring (5)), headers);
}
internal static HttpResponse Read (Stream stream, int millisecondsTimeout)
{
return Read<HttpResponse> (stream, Parse, millisecondsTimeout);
}
#endregion
#region Public Methods
public void SetCookies (CookieCollection cookies)
{
if (cookies == null || cookies.Count == 0)
return;
var headers = Headers;
foreach (var cookie in cookies.Sorted)
headers.Add ("Set-Cookie", cookie.ToResponseString ());
}
public override string ToString ()
{
var output = new StringBuilder (64);
output.AppendFormat ("HTTP/{0} {1} {2}{3}", ProtocolVersion, _code, _reason, CrLf);
var headers = Headers;
foreach (var key in headers.AllKeys)
output.AppendFormat ("{0}: {1}{2}", key, headers[key], CrLf);
output.Append (CrLf);
var entity = EntityBody;
if (entity.Length > 0)
output.Append (entity);
return output.ToString ();
}
#endregion
}
}

Some files were not shown because too many files have changed in this diff Show More