web端代码就是js代码,C#有两种方式:使用第三方库,如Fleck,使用C#原生socket编程实现
 
web端:
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>下发网站上文件到学生机</title>
<script type="text/javascript">
function callDesktopReceiveFile(button) {
var ws = null;
if (button.innerHTML == '下发') {
button.innerHTML = '取消'; try {
if (ws) ws.close();
} catch(e) {
console.log(e)
} ws = new WebSocket('ws://127.0.0.1:14567');
//监听是否连接成功
ws.onopen = function () {
console.log('ws连接状态[成功]:' + ws.readyState);
ws.send("content:receiveFile;url:http://127.0.0.1:5500/2023CQGKMNT.dat;");
// ws.close();
console.log('发送消息');
}; // 接听服务器发回的信息并处理展示
ws.onmessage = function (e) {
console.log('接收到来自服务器的消息:' + e.data);
// 如果服务器在业务上主动关闭连接,则此处无需再关闭
// if (e.data === 'roger') {
// ws.close();
// }
}; // 监听连接关闭事件
ws.onclose = function () {
// 监听整个过程中websocket的状态
console.log('ws连接状态[关闭]:' + ws.readyState);
}; // 监听并处理error事件
ws.onerror = function (error) {
console.log(error);
}; } else if (button.innerHTML == '取消') {
try {
if (ws) ws.close();
} catch(e) {
console.log(e)
}
button.innerHTML = '下发';
}
} </script>
</head>
<body>
<div>
<table border="1" cellspacing="0">
<tr>
<th>试卷号</th>
<th>试卷名称</th>
<th>描述</th>
<th>操作</th>
</tr>
<tr>
<td>JCLX01</td>
<td>基础练习一</td>
<td>建账、会计期间设置、部门职员设置、银行账户设置、科目设置等</td>
<td><button id="btnDownload" onclick="callDesktopReceiveFile(this)">下发</button></td>
</tr>
<tr>
<td>JCLX02</td>
<td>基础练习二</td>
<td>建账、会计期间设置、部门职员设置、银行账户设置、科目设置等</td>
<td><button id="btnDownload" onclick="callDesktopReceiveFile(this)">下发</button></td>
</tr>
</table> </div>
</body>
</html>
 
C#端
方式一:使用第三方库Fleck
 
方式二:使用C#原生socket编程自行实现
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;
using System.Threading;
using U8FileTransfer.TcpHelper;
using System.Net.Sockets;
using System.Net;
using System.Security.Cryptography; namespace CodeExperiment
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Thread thread = new Thread(websocketListen);
thread.IsBackground = true;
thread.Start(); } /// <summary>
/// 解析客户端数据包,防止乱码
/// </summary>
/// <param name="recBytes">服务器接收的数据包</param>
/// <param name="recByteLength">有效数据长度</param>
/// <returns></returns>
private static string AnalyticData(byte[] recBytes, int recByteLength)
{
if (recByteLength < 2) { return string.Empty; }
bool fin = (recBytes[0] & 0x80) == 0x80; // 1bit,1表示最后一帧
if (!fin)
{
return string.Empty;// 超过一帧暂不处理
}
bool mask_flag = (recBytes[1] & 0x80) == 0x80; // 是否包含掩码
if (!mask_flag)
{
return string.Empty;// 不包含掩码的暂不处理
}
int payload_len = recBytes[1] & 0x7F; // 数据长度
byte[] masks = new byte[4];
byte[] payload_data;
if (payload_len == 126)
{
Array.Copy(recBytes, 4, masks, 0, 4);
payload_len = (UInt16)(recBytes[2] << 8 | recBytes[3]);
payload_data = new byte[payload_len];
Array.Copy(recBytes, 8, payload_data, 0, payload_len);
}
else if (payload_len == 127)
{
Array.Copy(recBytes, 10, masks, 0, 4);
byte[] uInt64Bytes = new byte[8];
for (int i = 0; i < 8; i++)
{
uInt64Bytes[i] = recBytes[9 - i];
}
UInt64 len = BitConverter.ToUInt64(uInt64Bytes, 0);
payload_data = new byte[len];
for (UInt64 i = 0; i < len; i++)
{
payload_data[i] = recBytes[i + 14];
}
}
else
{
Array.Copy(recBytes, 2, masks, 0, 4);
payload_data = new byte[payload_len];
Array.Copy(recBytes, 6, payload_data, 0, payload_len);
}
for (var i = 0; i < payload_len; i++)
{
payload_data[i] = (byte)(payload_data[i] ^ masks[i % 4]);
}
return Encoding.UTF8.GetString(payload_data);
} /// <summary>
/// 打包服务器数据,防止乱码
/// </summary>
/// <param name="message">数据</param>
/// <returns>数据包</returns>
private static byte[] PackData(string message)
{
byte[] contentBytes = null;
byte[] temp = Encoding.UTF8.GetBytes(message);
if (temp.Length < 126)
{
contentBytes = new byte[temp.Length + 2];
contentBytes[0] = 0x81;
contentBytes[1] = (byte)temp.Length;
Array.Copy(temp, 0, contentBytes, 2, temp.Length);
}
else if (temp.Length < 0xFFFF)
{
contentBytes = new byte[temp.Length + 4];
contentBytes[0] = 0x81;
contentBytes[1] = 126;
contentBytes[2] = (byte)(temp.Length & 0xFF);
contentBytes[3] = (byte)(temp.Length >> 8 & 0xFF);
Array.Copy(temp, 0, contentBytes, 4, temp.Length);
}
else
{
// 暂不处理超长内容
}
return contentBytes;
} /// <summary>
/// 客户端消息结构化
/// message参数格式为多个key:value键值对通过分号拼接组成,示例:
/// content:download_and_send;file-url:https://www.a.com/a.zip;
///
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
private Dictionary<string, string> ProcessRemoteMessage(string message)
{
Dictionary<string, string> dic = new Dictionary<string, string>();
if (message.Substring(message.Length - 1, 1) == ";")
{
message = message.Substring(0, message.Length - 1);
}
string[] strs = message.Split(';');
if (strs.Length > 0)
{
Console.WriteLine("- - - - - - - - - - - - - - - - - - -");
foreach (string s in strs)
{
Console.WriteLine(s);
string[] split = s.Split(new char[] { ':' }, 2);
Console.WriteLine("[" + split[0] + "][" + split[1] + "]");
dic.Add(split[0], split[1]);
}
Console.WriteLine("- - - - - - - - - - - - - - - - - - -");
}
return dic;
} private void websocketListen()
{
Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
EndPoint endPoint = new IPEndPoint(IPAddress.Any, 14567);//监听端口
server.Bind(endPoint);
server.Listen(10); // 排队等待连接最大数量10 // 监听多个客户端连接
while (true)
{
Socket client = server.Accept();
Console.WriteLine("有客户端连上来了"); //接收客户端发来的HTTP-Header消息
byte[] bytes = new byte[1024];
int len = client.Receive(bytes);
string strMessage = Encoding.UTF8.GetString(bytes, 0, len);
Console.WriteLine(strMessage); //获取Sec-WebSocket-Key,为握手做准备
string[] strings = strMessage.Split('\n');
string strSecWebSocketKey = "";
foreach (var item in strings)
{
string[] strings1 = item.Split(':');
if (strings1[0] == "Sec-WebSocket-Key")
{
strSecWebSocketKey = strings1[1].Trim();
}
} //生成服务端Sec-WebSocket-Accept,迎合客户端的握手请求
byte[] secKeyBytes = SHA1.Create().ComputeHash(Encoding.ASCII.GetBytes(strSecWebSocketKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"));
string secKey = Convert.ToBase64String(secKeyBytes); // 发送给客户端完成握手(会触发Websocket的open()回调函数),针对websocket必须使用以下header
string strHeader = "";
strHeader += "HTTP/1.1 101 Switching Protocols" + Environment.NewLine;
strHeader += "Upgrade: websocket" + Environment.NewLine;
strHeader += "Connection: Upgrade" + Environment.NewLine;
strHeader += "Sec-WebSocket-Accept: " + secKey + Environment.NewLine + Environment.NewLine;
client.Send(Encoding.UTF8.GetBytes(strHeader)); string remoteFileUrl = null;
bool clientClose = false;
// 循环接收websocket发来的消息实现双方交流
while (!clientClose)
{
//接收客户端发来的消息
byte[] bytes2 = new byte[1024];
int len2 = client.Receive(bytes2);
string strMessage2 = AnalyticData(bytes2, len2);
if (strMessage2.Length > 0)
{
Console.WriteLine("客户端发来消息:{0}", strMessage2);
Dictionary<string, string> messageDic = ProcessRemoteMessage(strMessage2);
string content = null;
messageDic.TryGetValue("content", out content);
Console.WriteLine("message content:" + content);
if (content == "receiveFile")
{
messageDic.TryGetValue("url", out remoteFileUrl);
client.Send(PackData("roger"));
Console.WriteLine("remoteFileUrl: " + remoteFileUrl);
Console.WriteLine("to do invoke download.");
// 关闭连接
client.Shutdown(SocketShutdown.Both);
client.Close();
clientClose = true;
}
}
else
{
Console.WriteLine("客户端关闭了连接");
client.Shutdown(SocketShutdown.Both);
client.Close();
}
}
}
} }
}
websocket关闭机制
通信双方都可以主动关闭连接,不管谁关闭连接,对方都能收到消息。
client端关闭连接,server端能收到内容长度为0的消息。
server端关闭连接,client端会触发onclose事件。
 

websocket与C# socket相互通信的更多相关文章

  1. webSocket协议与Socket的区别

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

  2. Demo源码放送:打通B/S与C/S !让HTML5 WebSocket与.NET Socket公用同一个服务端!

    随着HTML5 WebSocket技术的日益成熟与普及,我们可以借助WebSocket来更加方便地打通BS与CS -- 因为B/S中的WebSocket可以直接连接到C/S的服务端,并进行双向通信.如 ...

  3. Socket进程通信机制及应用

    Socket通常称为“套接字”,用于描述IP地址和端口,是一个通信链的句柄.应用程序通过套接字向网络发出请求或者应答网络请求.Socket即不是一个程序,也不是一个协议,其只是操作系统提供的通信层的一 ...

  4. Socket进程通信机制

    1.Socket通常称为“套接字”,用于描述IP地址和端口,是一个通信链的句柄. 2.应用程序通过套接字向网络发出请求或者应答网络请求. 3.Socket既不是一个程序,也不是一种协议,其只是操作系统 ...

  5. VC SOCKET 压缩通信学习

    Server................// Server.cpp : Defines the entry point for the console application. // #inclu ...

  6. 【Android实战】Socket消息通信

    这篇博客的内容是基于http://my.oschina.net/fengcunhan/blog/178155进行改造的.所以须要先看完这篇博客,然后再来看以下的内容. 1.须要完毕的功能是直播间的so ...

  7. 继承c3,网络编程,相互通信

    继承: 面向对象多继承,a先找左,再找右 py2中有经典类,新式类,py3中只有新式类 py2中用ascii 编译 py3中用unicode 编译 py2: 经典类 新式类:如果自己或自己的前辈继承o ...

  8. wmware 怎么 跟主机相互通信

    VMnet1和VMware8其实就是软件模拟出来的两块网卡提供DHCP服务,两块网卡对应VMware的两种不同的模式VMWare提供了三种工作模式,它们是bridged(桥接模式).NAT(网络地址转 ...

  9. 打通B/S与C/S !让HTML5 WebSocket与.NET Socket公用同一个服务端!

    随着HTML5 WebSocket技术的日益成熟与普及,我们可以借助WebSocket来更加方便地打通BS与CS -- 因为B/S中的WebSocket可以直接连接到C/S的服务端,并进行双向通信.如 ...

  10. 使用Socket进行通信

    客户端通常可使用Socket的构造器来连接到指定服务器,Socket通常可使用如下两个构造器. Socket(lnetAddress/String  remoteAddress , int  port ...

随机推荐

  1. GitHub远程仓库与本地仓库链接问题

    git clone ...时,Failed to connect to 127.0.0.1 port 1080: Connection refused 步骤1------git查看: 查询动态代理 g ...

  2. 从babel编译结果分析class的实现原理

    示例: class A { // 属性表达式 prop1 = 1; // get方法 get value() { console.log('Getting the current value!'); ...

  3. sql union 和 union all

    UNION 操作符用于合并两个或多个 SELECT 语句的结果集. 但是需要注意: 1.union内部的select语句必须拥有相同数量的列. 2.列必须拥有相似的数据类型. 3.每条select语句 ...

  4. mybatis-plus #和$的使用场景

    #防止sql注入,但是会把里面的内容默认为是字符串 $使用场景:如果条件查询要加入数据权限判断,那么久需要使用$符号而不是#符号

  5. 做bad apple第一步:超级好用的you-get下载各大网站音频!!!!

    1 安装在cmd中输入pip3 install you-get 就行了 2 简单用法下载视频: 超级暴力,直接 you-get + "网站" 就没了,会下载到当前路径.我想下载到哪 ...

  6. 2.错误代码C2440

    错误 C2440 "初始化": 无法从"const char [5]"转换为"char *" 从整型强制转换为指针类型要求 reinterp ...

  7. 2PC(两阶段提交)【XA 与 Seata方案】

    更多内容,前往 IT-BLOG 一.概述 2PC(two phase commit protocol,2PC)即两阶段提交协议,是将整个事务流程分为两个阶段,准备阶段(Prepare phase).提 ...

  8. 使用 ApplicationContextAware 定义 SpringContextHolder 类

    需求:使用 @autowired注入一些对象,但发现不可以直接使用@Autowired,因为方法是static的,要使用该方法当前对象也必须是static,正常情况下@Autowired无法注入静态的 ...

  9. Tomcat启动JSP项目,搞起来了

    虽然有点复古,但是还是有很多小伙伴在使用的,小编来一篇保姆级教程 1.用idea打开jsp项目 2.添加tomcat配置 3.点击后会出现配置框,这里画框的地方都选上,版本选择1.8,其他的信息内容默 ...

  10. 配置 RSTP

    实验1-5-2 配置 RSTP [实验名称] 配置 RSTP. [实验目的] 理解快速生成树协议 RSTP 的配置及原理. [背景描述] 某学校为了开展计算机教学和网络办公,建立了一个计算机教室和一个 ...