代码地址:https://gitee.com/qq28069933146_admin/csharp_networkprotocol_research
视频地址: C#-WebSocket通讯示例演示_WinForm版、 C#-WebSocket通讯示例演示_Web应用版
一、WinForm版服务器
1、WebSocket知识点
(1)侦听
① 使用httpListener进行侦听
② 若侦听到的内容是WebSocket协议,即httpListener.GetContextAsync().Request.IsWebSocketRequest
,则对内容进行处理
private static HttpListener httpListener = new HttpListener(); // HttpListener
/// <summary>
/// 开启Socket服务,对参数地址进行监听
/// </summary>
/// <param name="ipAdress">监听地址,记得以 / 结尾</param>
private async void Start(string ipAdress)
{
try
{
httpListener.Prefixes.Add(ipAdress); // 添加监听的URL范围
// 通过连接名称可以区分多个websocket服务。如可以通过 http://localhost:8080/learn http://localhost:8080/work 使用两个服务,不过需要多线程和两个http监听对象等
httpListener.Start();
lblListen.Text = "监听中...";
while (true)
{
// http端口监听获取内容
HttpListenerContext httpListenerContext = await httpListener.GetContextAsync();
if (httpListenerContext.Request.IsWebSocketRequest) // 如果是websocket请求
{
// 处理SocketRequest
ProcessRequest(httpListenerContext);
}
else
{
httpListenerContext.Response.StatusCode = 400;
httpListenerContext.Response.Close();
}
}
}
catch (Exception ex)
{
txtInfo.AppendText(ex.ToString() + DateTime.Now.ToString() + "\n");
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
(2)侦听-处理内容并维护WebSocket客户端连接
/// <summary>
/// 处理端口监听到的请求
/// </summary>
/// <param name="httpListenerContext"></param>
private async void ProcessRequest(HttpListenerContext httpListenerContext)
{
WebSocketContext webSocketContext = null; // WebSocketContext 类用于访问websocket握手中的信息
try
{
webSocketContext = await httpListenerContext.AcceptWebSocketAsync(subProtocol: null); // 配置协议为空
// 获取客户端IP
string ipAddress = httpListenerContext.Request.RemoteEndPoint.Address.ToString();
txtInfo.AppendText("客户端IP地址:" + ipAddress + "\n");
}
catch (Exception e) // 如果出错
{
httpListenerContext.Response.StatusCode = 500;
httpListenerContext.Response.Close();
txtInfo.AppendText("Exception:" + e.ToString() + DateTime.Now.ToString() + "\n");
return;
}
// 获取websocket连接
WebSocket webSocket = webSocketContext.WebSocket;
_sockets.Add(webSocket); // 此处将web socket对象加入一个静态列表中
SendToNewConnection(webSocket); // 将当前服务器上最新的数据(a,b的值)发送过去
try
{
// 我们定义一个常数,它将表示接收到的数据的大小。 它是由我们建立的,我们可以设定任何值。 我们知道在这种情况下,发送的数据的大小非常小。
const int maxMessageSize = 2048;
// received bits的缓冲区
while (webSocket != null && webSocket.State == WebSocketState.Open) // 如果连接是打开的
{
// 此句放在while里面,每次使用都重新初始化。如果放在外面,由于没有进行清空操作,下一次接收的数据若比上一次短,则会多出一部分内容。
var receiveBuffer = new ArraySegment<Byte>(new Byte[maxMessageSize]);
WebSocketReceiveResult receiveResult = null;
byte[] payloadData = null;
do
{
// 读取数据。此类的实例表示在 WebSocket 上执行单个 ReceiveAsync 操作所得到的结果
receiveResult = await webSocket.ReceiveAsync(receiveBuffer, CancellationToken.None);
// 字节数组
payloadData = receiveBuffer.Array.Where(b => b != 0).ToArray();
}
while (!receiveResult.EndOfMessage); // 如果指示已完整接收消息则停止
// 如果输入帧为取消帧,发送close命令。
// MessageType指示当前消息是utf-8消息还是二进制信息。Text(0,明文形式),Close(2,收到关闭消息,接受已完成),Binary(1,消息采用二进制格式)
if (receiveResult.MessageType == WebSocketMessageType.Close)
{
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, String.Empty, CancellationToken.None);
_sockets.Remove(webSocket); // 从列表移除当前连接
}
else
{
// 因为我们知道这是一个字符串,我们转换它
string receiveString = System.Text.Encoding.UTF8.GetString(payloadData, 0, payloadData.Length);
try // 将反序列化内容放入try中,避免无法匹配、内容为空等可能报错的地方
{
// 将转换后的字符串内容进行json反序列化。参考:
TestValue tv = JsonConvert.DeserializeObject<TestValue>(receiveString);
// 将收到的a,b的值显示到文本框
if (tv != null)
{
string valueA = string.Empty, valueB = string.Empty;
if (tv.a != null && tv.a.Length > 0) { valueA = tv.a; }
if (tv.a != null && tv.b.Length > 0) { valueB = tv.b; }
txtAvalue.Text = valueA;
txtBvalue.Text = valueB;
}
RefreshConnectionList(); // 先清理无效的连接,否则会导致服务端websocket被dispose
// 当接收到文本消息时,对当前服务器上所有web socket连接进行广播
foreach (var innerSocket in _sockets)
{
await innerSocket.SendAsync(new ArraySegment<byte>(payloadData), WebSocketMessageType.Text, true, CancellationToken.None);
}
}
catch (Exception ex)
{
// 如果json反序列化出了问题
txtInfo.AppendText(ex.ToString() + DateTime.Now.ToString() + "\n"); // 将错误类型显示出来
txtInfo.AppendText(receiveString + DateTime.Now.ToString() + "\n"); // 将收到的原始字符串显示出来
}
}
}
}
catch (Exception e)
{
if (e.GetType().ToString() == "System.Net.WebSockets.WebSocketException")
{
// 客户端关闭时会抛出此错误
txtInfo.AppendText("连接已关闭" + DateTime.Now.ToString() + "\n");
}
else
{
txtInfo.AppendText(e.ToString() + DateTime.Now.ToString() + "\n");
}
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
- 103.
- 104.
- 105.
(3)发送
2、完整示例
(1)WinForm服务器端:
① WinSocketServer1.Designer.cs
查看代码
namespace WinSocketServer
{
partial class WinSocketServer1
{
/// <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()
{
btnModify = new System.Windows.Forms.Button();
label1 = new System.Windows.Forms.Label();
txtIPAddress = new System.Windows.Forms.TextBox();
label2 = new System.Windows.Forms.Label();
label3 = new System.Windows.Forms.Label();
txtAvalue = new System.Windows.Forms.TextBox();
txtBvalue = new System.Windows.Forms.TextBox();
btnBroadcast = new System.Windows.Forms.Button();
panel1 = new System.Windows.Forms.Panel();
btnClose = new System.Windows.Forms.Button();
btnOpenServer = new System.Windows.Forms.Button();
txtInfo = new System.Windows.Forms.TextBox();
btnShowCon = new System.Windows.Forms.Button();
lblListen = new System.Windows.Forms.Label();
panel1.SuspendLayout();
SuspendLayout();
//
// btnModify
//
btnModify.Location = new System.Drawing.Point(271, 10);
btnModify.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
btnModify.Name = "btnModify";
btnModify.Size = new System.Drawing.Size(73, 25);
btnModify.TabIndex = 0;
btnModify.Text = "修改";
btnModify.UseVisualStyleBackColor = true;
btnModify.Click += btnModify_Click;
//
// label1
//
label1.AutoSize = true;
label1.Location = new System.Drawing.Point(9, 14);
label1.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
label1.Name = "label1";
label1.Size = new System.Drawing.Size(56, 17);
label1.TabIndex = 1;
label1.Text = "监听地址";
//
// txtIPAddress
//
txtIPAddress.Enabled = false;
txtIPAddress.Location = new System.Drawing.Point(68, 11);
txtIPAddress.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
txtIPAddress.Name = "txtIPAddress";
txtIPAddress.Size = new System.Drawing.Size(200, 23);
txtIPAddress.TabIndex = 2;
txtIPAddress.Text = "http://127.0.0.1:8080/";
//
// label2
//
label2.AutoSize = true;
label2.Location = new System.Drawing.Point(9, 48);
label2.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
label2.Name = "label2";
label2.Size = new System.Drawing.Size(27, 17);
label2.TabIndex = 3;
label2.Text = "a:";
//
// label3
//
label3.AutoSize = true;
label3.Location = new System.Drawing.Point(9, 78);
label3.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
label3.Name = "label3";
label3.Size = new System.Drawing.Size(28, 17);
label3.TabIndex = 4;
label3.Text = "b:";
//
// txtAvalue
//
txtAvalue.Location = new System.Drawing.Point(68, 45);
txtAvalue.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
txtAvalue.Name = "txtAvalue";
txtAvalue.Size = new System.Drawing.Size(161, 23);
txtAvalue.TabIndex = 5;
//
// txtBvalue
//
txtBvalue.Location = new System.Drawing.Point(68, 76);
txtBvalue.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
txtBvalue.Name = "txtBvalue";
txtBvalue.Size = new System.Drawing.Size(161, 23);
txtBvalue.TabIndex = 6;
//
// btnBroadcast
//
btnBroadcast.Font = new System.Drawing.Font("Microsoft YaHei UI", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
btnBroadcast.Location = new System.Drawing.Point(244, 48);
btnBroadcast.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
btnBroadcast.Name = "btnBroadcast";
btnBroadcast.Size = new System.Drawing.Size(100, 48);
btnBroadcast.TabIndex = 7;
btnBroadcast.Text = "广播";
btnBroadcast.UseVisualStyleBackColor = true;
btnBroadcast.Click += btnBroadcast_Click;
//
// panel1
//
panel1.Controls.Add(btnClose);
panel1.Controls.Add(btnOpenServer);
panel1.Location = new System.Drawing.Point(368, 3);
panel1.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
panel1.Name = "panel1";
panel1.Size = new System.Drawing.Size(110, 96);
panel1.TabIndex = 10;
//
// btnClose
//
btnClose.Location = new System.Drawing.Point(19, 60);
btnClose.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
btnClose.Name = "btnClose";
btnClose.Size = new System.Drawing.Size(73, 25);
btnClose.TabIndex = 11;
btnClose.Text = "关闭监听";
btnClose.UseVisualStyleBackColor = true;
btnClose.Click += btnClose_Click;
//
// btnOpenServer
//
btnOpenServer.Location = new System.Drawing.Point(19, 11);
btnOpenServer.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
btnOpenServer.Name = "btnOpenServer";
btnOpenServer.Size = new System.Drawing.Size(73, 25);
btnOpenServer.TabIndex = 10;
btnOpenServer.Text = "开启监听";
btnOpenServer.UseVisualStyleBackColor = true;
btnOpenServer.Click += btnOpenServer_Click;
//
// txtInfo
//
txtInfo.Location = new System.Drawing.Point(9, 121);
txtInfo.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
txtInfo.Multiline = true;
txtInfo.Name = "txtInfo";
txtInfo.Size = new System.Drawing.Size(604, 252);
txtInfo.TabIndex = 11;
//
// btnShowCon
//
btnShowCon.Location = new System.Drawing.Point(510, 56);
btnShowCon.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
btnShowCon.Name = "btnShowCon";
btnShowCon.Size = new System.Drawing.Size(92, 39);
btnShowCon.TabIndex = 12;
btnShowCon.Text = "显示当前连接";
btnShowCon.UseVisualStyleBackColor = true;
btnShowCon.Click += btnShowCon_Click;
//
// lblListen
//
lblListen.AutoSize = true;
lblListen.Font = new System.Drawing.Font("Microsoft YaHei UI", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
lblListen.Location = new System.Drawing.Point(510, 18);
lblListen.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
lblListen.Name = "lblListen";
lblListen.Size = new System.Drawing.Size(92, 17);
lblListen.TabIndex = 13;
lblListen.Text = "服务为关闭状态";
lblListen.TextAlign = System.Drawing.ContentAlignment.TopRight;
//
// WinSocketServer1
//
AutoScaleDimensions = new System.Drawing.SizeF(7F, 17F);
AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
ClientSize = new System.Drawing.Size(622, 382);
Controls.Add(lblListen);
Controls.Add(btnShowCon);
Controls.Add(txtInfo);
Controls.Add(panel1);
Controls.Add(btnBroadcast);
Controls.Add(txtBvalue);
Controls.Add(txtAvalue);
Controls.Add(label3);
Controls.Add(label2);
Controls.Add(txtIPAddress);
Controls.Add(label1);
Controls.Add(btnModify);
Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
Name = "WinSocketServer1";
Text = "Win_Socker服务器";
Load += WinSockerServer1_Load;
panel1.ResumeLayout(false);
ResumeLayout(false);
PerformLayout();
}
#endregion
private System.Windows.Forms.Button btnModify;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.TextBox txtIPAddress;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.TextBox txtAvalue;
private System.Windows.Forms.TextBox txtBvalue;
private System.Windows.Forms.Button btnBroadcast;
private System.Windows.Forms.Panel panel1;
private System.Windows.Forms.Button btnClose;
private System.Windows.Forms.Button btnOpenServer;
private System.Windows.Forms.TextBox txtInfo;
private System.Windows.Forms.Button btnShowCon;
private System.Windows.Forms.Label lblListen;
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
- 103.
- 104.
- 105.
- 106.
- 107.
- 108.
- 109.
- 110.
- 111.
- 112.
- 113.
- 114.
- 115.
- 116.
- 117.
- 118.
- 119.
- 120.
- 121.
- 122.
- 123.
- 124.
- 125.
- 126.
- 127.
- 128.
- 129.
- 130.
- 131.
- 132.
- 133.
- 134.
- 135.
- 136.
- 137.
- 138.
- 139.
- 140.
- 141.
- 142.
- 143.
- 144.
- 145.
- 146.
- 147.
- 148.
- 149.
- 150.
- 151.
- 152.
- 153.
- 154.
- 155.
- 156.
- 157.
- 158.
- 159.
- 160.
- 161.
- 162.
- 163.
- 164.
- 165.
- 166.
- 167.
- 168.
- 169.
- 170.
- 171.
- 172.
- 173.
- 174.
- 175.
- 176.
- 177.
- 178.
- 179.
- 180.
- 181.
- 182.
- 183.
- 184.
- 185.
- 186.
- 187.
- 188.
- 189.
- 190.
- 191.
- 192.
- 193.
- 194.
- 195.
- 196.
- 197.
- 198.
- 199.
- 200.
- 201.
- 202.
- 203.
- 204.
- 205.
- 206.
- 207.
- 208.
- 209.
- 210.
- 211.
- 212.
- 213.
- 214.
- 215.
- 216.
- 217.
- 218.
- 219.
- 220.
- 221.
- 222.
- 223.
- 224.
- 225.
- 226.
- 227.
- 228.
- 229.
- 230.
- 231.
- 232.
- 233.
- 234.
② WinSocketServer1.cs
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WinSocketServer
{
public partial class WinSocketServer1 : Form
{
public WinSocketServer1()
{
InitializeComponent();
}
private void WinSockerServer1_Load(object sender, EventArgs e)
{
}
/// <summary>
/// 打开服务
/// </summary>
private void btnOpenServer_Click(object sender, EventArgs e)
{
if (txtIPAddress.Enabled == true)
{
MessageBox.Show("请先确认地址");
return;
}
string IpAdress = txtIPAddress.Text;
txtInfo.AppendText("打开监听" + DateTime.Now.ToString() + "\n");
Start(IpAdress);
}
/// <summary>
/// 关闭服务
/// </summary>
private void btnClose_Click(object sender, EventArgs e)
{
if (httpListener.IsListening)
{
try
{
httpListener.Stop();
txtInfo.AppendText("成功关闭" + DateTime.Now.ToString() + "\n");
lblListen.Text = "服务为关闭状态";
}
catch (Exception ex)
{
txtInfo.AppendText(ex.ToString() + DateTime.Now.ToString() + "\n");
}
}
else
{
txtInfo.AppendText("此时服务并未处于监听状态,无法关闭" + DateTime.Now.ToString() + "\n");
lblListen.Text = "服务为关闭状态";
return;
}
}
/// <summary>
/// 修改
/// </summary>
private void btnModify_Click(object sender, EventArgs e)
{
if (btnModify.Text == "修改")
{
txtIPAddress.Enabled = true;
btnModify.Text = "确定";
}
else if (btnModify.Text == "确定")
{
txtIPAddress.Enabled = false;
btnModify.Text = "修改";
}
}
/// <summary>
/// 广播
/// </summary>
private void btnBroadcast_Click(object sender, EventArgs e)
{
string jsonmessage = RequestMsg.SerializeJson(txtAvalue.Text, txtBvalue.Text);
txtInfo.AppendText("广播" + DateTime.Now.ToString() + ":\n");
txtInfo.AppendText("内容:" + jsonmessage + "\n");
Broadcast(jsonmessage);
}
/// <summary>
/// 显示链接列表
/// </summary>
private void btnShowCon_Click(object sender, EventArgs e)
{
int ConnectionCount = _sockets.Count;
txtInfo.AppendText("服务端当前存储了" + ConnectionCount + "个客户端连接:\n" + DateTime.Now.ToString() + "\n");
foreach (WebSocket innersocket in _sockets)
{
txtInfo.AppendText(innersocket.GetHashCode().ToString() + innersocket.State.ToString() + "\n");
}
}
#region 监听方法
private static List<WebSocket> _sockets = new List<WebSocket>(); // 存储当前所有连接的静态列表
private static HttpListener httpListener = new HttpListener(); // HttpListener
/// <summary>
/// 开启Socket服务,对参数地址进行监听
/// </summary>
/// <param name="ipAdress">监听地址,记得以 / 结尾</param>
private async void Start(string ipAdress)
{
try
{
httpListener.Prefixes.Add(ipAdress); // 添加监听的URL范围
// 通过连接名称可以区分多个websocket服务。如可以通过 http://localhost:8080/learn http://localhost:8080/work 使用两个服务,不过需要多线程和两个http监听对象等
httpListener.Start();
lblListen.Text = "监听中...";
while (true)
{
// http端口监听获取内容
HttpListenerContext httpListenerContext = await httpListener.GetContextAsync();
if (httpListenerContext.Request.IsWebSocketRequest) // 如果是websocket请求
{
// 处理SocketRequest
ProcessRequest(httpListenerContext);
}
else
{
httpListenerContext.Response.StatusCode = 400;
httpListenerContext.Response.Close();
}
}
}
catch (Exception ex)
{
txtInfo.AppendText(ex.ToString() + DateTime.Now.ToString() + "\n");
}
}
/// <summary>
/// 处理端口监听到的请求
/// </summary>
/// <param name="httpListenerContext"></param>
private async void ProcessRequest(HttpListenerContext httpListenerContext)
{
WebSocketContext webSocketContext = null; // WebSocketContext 类用于访问websocket握手中的信息
try
{
webSocketContext = await httpListenerContext.AcceptWebSocketAsync(subProtocol: null); // 配置协议为空
// 获取客户端IP
string ipAddress = httpListenerContext.Request.RemoteEndPoint.Address.ToString();
txtInfo.AppendText("客户端IP地址:" + ipAddress + "\n");
}
catch (Exception e) // 如果出错
{
httpListenerContext.Response.StatusCode = 500;
httpListenerContext.Response.Close();
txtInfo.AppendText("Exception:" + e.ToString() + DateTime.Now.ToString() + "\n");
return;
}
// 获取websocket连接
WebSocket webSocket = webSocketContext.WebSocket;
_sockets.Add(webSocket); // 此处将web socket对象加入一个静态列表中
SendToNewConnection(webSocket); // 将当前服务器上最新的数据(a,b的值)发送过去
try
{
// 我们定义一个常数,它将表示接收到的数据的大小。 它是由我们建立的,我们可以设定任何值。 我们知道在这种情况下,发送的数据的大小非常小。
const int maxMessageSize = 2048;
// received bits的缓冲区
while (webSocket != null && webSocket.State == WebSocketState.Open) // 如果连接是打开的
{
// 此句放在while里面,每次使用都重新初始化。如果放在外面,由于没有进行清空操作,下一次接收的数据若比上一次短,则会多出一部分内容。
var receiveBuffer = new ArraySegment<Byte>(new Byte[maxMessageSize]);
WebSocketReceiveResult receiveResult = null;
byte[] payloadData = null;
do
{
// 读取数据。此类的实例表示在 WebSocket 上执行单个 ReceiveAsync 操作所得到的结果
receiveResult = await webSocket.ReceiveAsync(receiveBuffer, CancellationToken.None);
// 字节数组
payloadData = receiveBuffer.Array.Where(b => b != 0).ToArray();
}
while (!receiveResult.EndOfMessage); // 如果指示已完整接收消息则停止
// 如果输入帧为取消帧,发送close命令。
// MessageType指示当前消息是utf-8消息还是二进制信息。Text(0,明文形式),Close(2,收到关闭消息,接受已完成),Binary(1,消息采用二进制格式)
if (receiveResult.MessageType == WebSocketMessageType.Close)
{
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, String.Empty, CancellationToken.None);
_sockets.Remove(webSocket); // 从列表移除当前连接
}
else
{
// 因为我们知道这是一个字符串,我们转换它
string receiveString = System.Text.Encoding.UTF8.GetString(payloadData, 0, payloadData.Length);
try // 将反序列化内容放入try中,避免无法匹配、内容为空等可能报错的地方
{
// 将转换后的字符串内容进行json反序列化。参考:
TestValue tv = JsonConvert.DeserializeObject<TestValue>(receiveString);
// 将收到的a,b的值显示到文本框
if (tv != null)
{
string valueA = string.Empty, valueB = string.Empty;
if (tv.a != null && tv.a.Length > 0) { valueA = tv.a; }
if (tv.a != null && tv.b.Length > 0) { valueB = tv.b; }
txtAvalue.Text = valueA;
txtBvalue.Text = valueB;
}
RefreshConnectionList(); // 先清理无效的连接,否则会导致服务端websocket被dispose
// 当接收到文本消息时,对当前服务器上所有web socket连接进行广播
foreach (var innerSocket in _sockets)
{
await innerSocket.SendAsync(new ArraySegment<byte>(payloadData), WebSocketMessageType.Text, true, CancellationToken.None);
}
}
catch (Exception ex)
{
// 如果json反序列化出了问题
txtInfo.AppendText(ex.ToString() + DateTime.Now.ToString() + "\n"); // 将错误类型显示出来
txtInfo.AppendText(receiveString + DateTime.Now.ToString() + "\n"); // 将收到的原始字符串显示出来
}
}
}
}
catch (Exception e)
{
if (e.GetType().ToString() == "System.Net.WebSockets.WebSocketException")
{
// 客户端关闭时会抛出此错误
txtInfo.AppendText("连接已关闭" + DateTime.Now.ToString() + "\n");
}
else
{
txtInfo.AppendText(e.ToString() + DateTime.Now.ToString() + "\n");
}
}
}
#endregion 监听方法
#region 广播
/// <summary>
/// 服务端主动向所有客户端广播
/// </summary>
/// <param name="jsonmessage">传过来的应该是序列化后的json字符串,接收方会通过TestValue类进行反序列化获取a,b的内容</param>
public async void Broadcast(string jsonmessage)
{
try
{
Byte[] bytes = System.Text.Encoding.UTF8.GetBytes(jsonmessage);
RefreshConnectionList();//先清理无效的连接,否则会导致服务端websocket被dispose
//当接收到文本消息时,对当前服务器上所有web socket连接进行广播
foreach (var innerSocket in _sockets)
{
await innerSocket.SendAsync(new ArraySegment<byte>(bytes), WebSocketMessageType.Text, true, CancellationToken.None);
}
}
catch (Exception ex)
{
txtInfo.AppendText(ex.ToString() + DateTime.Now.ToString() + "\n");
MessageBox.Show("某些连接出了问题,如果广播多次出问题,请重启服务端");
}
}
/// <summary>
/// 监听到一个新的websocket连接后,将服务器当前最新数据同步过去
/// </summary>
/// <param name="currentWebsocket">当前新接入的websocket连接</param>
public async void SendToNewConnection(WebSocket currentWebsocket)
{
try
{
string jsonmessage = RequestMsg.SerializeJson(txtAvalue.Text, txtBvalue.Text);
Byte[] bytes = System.Text.Encoding.UTF8.GetBytes(jsonmessage);
if (currentWebsocket.State == WebSocketState.Open)
{
await currentWebsocket.SendAsync(new ArraySegment<byte>(bytes), WebSocketMessageType.Text, true, CancellationToken.None);//try访问已释放对象问题
}
else
{
//此处并不对该链接进行移除,会导致调用本方法后的代码出问题,只需在进行发送时确认状态即可
txtInfo.AppendText("新接入连接:" + currentWebsocket.GetHashCode().ToString() + "的连接状态不为open,因此停止向其同步数据" + DateTime.Now.ToString() + "\n");
}
}
catch (Exception ex)
{
txtInfo.AppendText(ex.ToString() + DateTime.Now.ToString() + "\n");
}
}
/// <summary>
/// 刷新当前websocket连接列表,如果状态为Closed,则移除。连接异常断开后会被dispose,如果访问会报错,但可以获取状态为closed
/// </summary>
public static void RefreshConnectionList()
{
if (_sockets != null)//lock不能锁定空值
{
lock (_sockets)//锁定数据源
{
//System.InvalidOperationException: 集合已修改;可能无法执行枚举操作。
//使用foreach不能执行删除、修改,这是规定。你可以使用for循环遍历修改。删除数据正确做法,for循环 i 要从大到小
for (int i = _sockets.Count - 1; i >= 0; i--)
{
if (_sockets[i].State != WebSocketState.Open)
{
_sockets.Remove(_sockets[i]);
}
}
}
}
}
#endregion
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
- 103.
- 104.
- 105.
- 106.
- 107.
- 108.
- 109.
- 110.
- 111.
- 112.
- 113.
- 114.
- 115.
- 116.
- 117.
- 118.
- 119.
- 120.
- 121.
- 122.
- 123.
- 124.
- 125.
- 126.
- 127.
- 128.
- 129.
- 130.
- 131.
- 132.
- 133.
- 134.
- 135.
- 136.
- 137.
- 138.
- 139.
- 140.
- 141.
- 142.
- 143.
- 144.
- 145.
- 146.
- 147.
- 148.
- 149.
- 150.
- 151.
- 152.
- 153.
- 154.
- 155.
- 156.
- 157.
- 158.
- 159.
- 160.
- 161.
- 162.
- 163.
- 164.
- 165.
- 166.
- 167.
- 168.
- 169.
- 170.
- 171.
- 172.
- 173.
- 174.
- 175.
- 176.
- 177.
- 178.
- 179.
- 180.
- 181.
- 182.
- 183.
- 184.
- 185.
- 186.
- 187.
- 188.
- 189.
- 190.
- 191.
- 192.
- 193.
- 194.
- 195.
- 196.
- 197.
- 198.
- 199.
- 200.
- 201.
- 202.
- 203.
- 204.
- 205.
- 206.
- 207.
- 208.
- 209.
- 210.
- 211.
- 212.
- 213.
- 214.
- 215.
- 216.
- 217.
- 218.
- 219.
- 220.
- 221.
- 222.
- 223.
- 224.
- 225.
- 226.
- 227.
- 228.
- 229.
- 230.
- 231.
- 232.
- 233.
- 234.
- 235.
- 236.
- 237.
- 238.
- 239.
- 240.
- 241.
- 242.
- 243.
- 244.
- 245.
- 246.
- 247.
- 248.
- 249.
- 250.
- 251.
- 252.
- 253.
- 254.
- 255.
- 256.
- 257.
- 258.
- 259.
- 260.
- 261.
- 262.
- 263.
- 264.
- 265.
- 266.
- 267.
- 268.
- 269.
- 270.
- 271.
- 272.
- 273.
- 274.
- 275.
- 276.
- 277.
- 278.
- 279.
- 280.
- 281.
- 282.
- 283.
- 284.
- 285.
- 286.
- 287.
- 288.
- 289.
- 290.
- 291.
- 292.
- 293.
- 294.
- 295.
- 296.
- 297.
- 298.
- 299.
- 300.
- 301.
- 302.
- 303.
- 304.
- 305.
- 306.
- 307.
- 308.
- 309.
- 310.
- 311.
- 312.
- 313.
- 314.
- 315.
- 316.
- 317.
- 318.
- 319.
- 320.
- 321.
- 322.
- 323.
- 324.
- 325.
- 326.
- 327.
- 328.
- 329.
- 330.
(2)WebForm客户端:
WinSocketClent.cshtml (注:Clent改为Client,写错了)
@{
ViewData["Title"] = "WinSocket客户端";
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>WinSocket客户端</title>
</head>
<body>
<div>
<form id="Form">
<label id="address">服务端地址:</label>
<input name="" id="IpAddress" value="ws://127.0.0.1:8080" />
<button type="submit" onclick="Connect();">连接</button><br />
<label id="a">a</label>
<input name="" id="ValueA" value="" />
<button type="submit" onclick="SendData();">提交</button><br />
<label id="b">b</label>
<input name="" id="ValueB" value="" />
</form>
<div id="txtInfo" style="border: dashed 1px black;height: 500px;width: 500px;margin-top: 10px;"></div>
</div>
</body>
</html>
@{
<script type="text/javascript">
var test = function () {
var print = document.getElementById('txtInfo');
var form = document.getElementById('Form');
var inputStrA = document.getElementById('ValueA');
var ipaddress = document.getElementById('IpAddress').value;
print.innerHTML += "connecting to server ..<br/>";
//参数即想要连接服务器的ip。 服务器可以是node.js, 也可以是其他项目,如c#
//window.ws = new WebSocket('ws://10.206.14.152:8080/'); //连接服务
//window.ws = new WebSocket('ws://192.168.43.78:8080/'); //连接服务
window.ws = new WebSocket(ipaddress); //连接服务
//监听消息状态
ws.onmessage = function (msg) {
var jsobj = JSON.parse(msg.data);//json字符串转为对象
document.getElementById('ValueA').value = jsobj.a;//写入输入框中
document.getElementById('ValueB').value = jsobj.b;
print.innerHTML += msg.data + '<br/>'
}
//监听链接状态
ws.onopen = function () {
print.innerHTML += '监听 打开 <br/>'
}
//监听关闭状态
ws.onclose = function () {
print.innerHTML += '监听 关闭<br/>';
}
//向服务器端发送消息
form.addEventListener('submit', function (e) {
e.preventDefault();
//序列化后的字符串发给服务端,服务端以字节数组格式接收,若转成字符串并添加部分信息再转回字节数组时,string.format函数会报错
//var jsonObj = { "name": "zhangsan", "age": 18 };
//传入一个json字符串进行测试,服务器会进行判断。
//ws.send(JSON.stringify(jsonObj));
//ws.send(jsonObj);
//传入一个非json字符串进行测试,服务器会进行判断。
//ws.send("test");
})
}
//window.onload = test();
function Connect() {
test();
}
function SendData() {
var valueA = document.getElementById('ValueA').value;
var valueB = document.getElementById('ValueB').value;
if (valueA.length == 0) { valueA = "-"; }
if (valueB.length == 0) { valueB = "-"; }
var jsonobj = {
"a": valueA,
"b": valueB
};
var jsonstr = JSON.stringify(jsonobj);
//初始化WebSocket
//InitWebSocket();
//如果WebSocket打开,发送数据
if (ws.OPEN && ws.readyState == 1) {
ws.send(jsonstr);
}
console.log(ws.readyState);
//如果WebSocket关闭,显示消息
if (ws.readyState == 2 || ws.readyState == 3) {
console.log("进来关闭函数了")
alert("WebSocket关闭了,无法发送数据");
}
}
</script>
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
- 103.
- 104.
- 105.
- 106.
- 107.
- 108.
二、WebAPI版服务器
- 可以使用“基于Http/2 的WebSockets 支持”
- 标头压缩。
- 多路复用,可减少向服务器发出多个请求时所需的时间和资源。
- HTTP/2 WebSockets 使用 CONNECT 请求而不是 GET,因此可能需要更新你自己的路由和控制器。见: 为现有控制器添加 HTTP/2 WebSockets 支持。
- Chrome 和 Edge 默认启用 HTTP/2 WebSocket,对于 FireFox,则可在
about:config
页面中使用network.http.spdy.websockets
标志来启用它。
- HTTP/2 WebSockets 使用 CONNECT 请求而不是 GET,因此可能需要更新你自己的路由和控制器。
- 与WebSocket 相比,SignalR具有以下优点:
- 可为 WebSocket 不可用的环境提供传输回退。
- 可提供基本的远程过程调用应用模型。
- 在大多数情况下,与使用原始 WebSocket 相比,没有显著的性能缺点。
- IIS部署时需要手动启用WebSocket且Win8/Win Server2012及更高版本,请参阅 IIS/IIS Express 支持。
1、WebSocket服务器_Web应用版
(1)在 Program.cs
中启用 WebSockets 中间件:
app.UseWebSockets(); // 1、启用 WebSocket 中间件
// 1、启用 WebSocket 中间件并自定义配置
//Microsoft.AspNetCore.Builder.WebSocketOptions webSocketOptions = new Microsoft.AspNetCore.Builder.WebSocketOptions
//{
// KeepAliveInterval = TimeSpan.FromMinutes(2),
//};
WebSocket 源限制只允许 https://client.com、https://www.client.com的客户端反馈;
//webSocketOptions.AllowedOrigins.Add("https://client.com");
//webSocketOptions.AllowedOrigins.Add("https://www.client.com");
//app.UseWebSockets(webSocketOptions);
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
(2)创建一个WebSockets帮助类
- 示例方法1:循环发送并反馈消息-未加密
- 示例方法2:循环发送和转发消息(聊天室)-未加密
using System.Net.WebSockets;
using System.Threading.Tasks;
using System.Threading;
using System;
using System.Collections.Generic;
namespace WebAPIWebSockets
{
/// <summary>
/// WebSockets帮助类
/// </summary>
public static class WebSocketsHelper
{
/// <summary>
/// 循环发送并反馈消息-未加密
/// 逻辑:如果在开始循环之前接受 WebSocket 连接,中间件管道会结束。 关闭套接字后,管道展开。 即接受 WebSocket 时,请求停止在管道中推进。 循环结束且套接字关闭时,请求继续回到管道。
/// </summary>
/// <param name="webSocket">WebSocket对象</param>
/// <returns></returns>
public static async Task Echo(WebSocket webSocket)
{
var buffer = new byte[1024 * 4];
var receiveResult = await webSocket.ReceiveAsync( // 接收消息
new ArraySegment<byte>(buffer), CancellationToken.None);
while (!receiveResult.CloseStatus.HasValue) // webSocket接收开启时,循环接收、转发
{
await webSocket.SendAsync( // 转发消息
new ArraySegment<byte>(buffer, 0, receiveResult.Count),
receiveResult.MessageType,
receiveResult.EndOfMessage,
CancellationToken.None);
receiveResult = await webSocket.ReceiveAsync( // 接收消息
new ArraySegment<byte>(buffer), CancellationToken.None);
}
await webSocket.CloseAsync( // 关闭WebSocket连接
receiveResult.CloseStatus.Value,
receiveResult.CloseStatusDescription,
CancellationToken.None);
}
private static List<WebSocket> _webSockets = new List<WebSocket>();
/// <summary>
/// 循环发送和转发消息(聊天室)-未加密
/// 逻辑:如果在开始循环之前接受 WebSocket 连接,中间件管道会结束。 关闭套接字后,管道展开。 即接受 WebSocket 时,请求停止在管道中推进。 循环结束且套接字关闭时,请求继续回到管道。
/// </summary>
/// <param name="webSocket">WebSocket对象</param>
/// <returns></returns>
public static async Task ChatRoom(WebSocket webSocket)
{
// 将webSocket加入到_webSockets
if (!_webSockets.Contains(webSocket))
{
_webSockets.Add(webSocket);
}
var buffer = new byte[1024 * 4];
var receiveResult = await webSocket.ReceiveAsync( // 接收消息
new ArraySegment<byte>(buffer), CancellationToken.None);
while (!receiveResult.CloseStatus.HasValue) // webSocket接收开启时,循环接收、转发
{
List<WebSocket> webSockets = new List<WebSocket>();
foreach (WebSocket webSocket1 in _webSockets)
{
if (webSocket1.State == WebSocketState.Open)
{
await webSocket1.SendAsync( // 转发消息
new ArraySegment<byte>(buffer, 0, receiveResult.Count),
receiveResult.MessageType,
receiveResult.EndOfMessage,
CancellationToken.None);
webSockets.Add(webSocket1);
}
}
_webSockets= webSockets;
receiveResult = await webSocket.ReceiveAsync( // 接收消息
new ArraySegment<byte>(buffer), CancellationToken.None);
}
if (_webSockets.Count <= 0)
{
await webSocket.CloseAsync( // 关闭WebSocket连接
receiveResult.CloseStatus.Value,
receiveResult.CloseStatusDescription,
CancellationToken.None);
}
}
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
(3)控制器中接收ws协议
- 示例方法1:循环发送并反馈消息-未加密
- 示例方法2:循环发送和转发消息(聊天室)-未加密
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Net.WebSockets;
using System.Threading.Tasks;
namespace WebAPIWebSockets.Controllers
{
public class WebSocketController : ControllerBase
{
[Route("/wsTest")]
public async Task Get()
{
if (HttpContext.WebSockets.IsWebSocketRequest)
{
using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
await WebSocketsHelper.Echo(webSocket);
}
else
{
HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
}
}
private static List<WebSocket> _sockets = new List<WebSocket>(); // 存储当前所有连接的静态列表
[Route("/ChatRoom")]
public async Task ChatRoom()
{
try
{
if (HttpContext.WebSockets.IsWebSocketRequest)
{
using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
await WebSocketsHelper.ChatRoom(webSocket);
}
else
{
HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
}
}
catch(Exception ex)
{
string message = ex.Message;
}
}
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
2、WebSocket客户端_JavaScript版
- 创建 WebSocket客户端 连接对象:
socket = new WebSocket(connectionUrl.value);
- 发送:
socket.send(data);
- 关闭:
socket.close(1000, "从客户端关闭");
- 打开事件:
socket.onopen = function (event) { ...}; // 发送事件
- 接收事件:
socket.onmessage = function (event) { ...}; // 接收事件;消息内容为event.data
- 关闭事件:
socket.onclose = function (event) { ...}; // 关闭事件
- 出错事件:
socket.onerror = updateState; // 报错; function updateState() {}
完整代码如下:
@{
ViewData["Title"] = "WebSocket示例页";
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<style>
table {
border: 0
}
.commslog-data {
font-family: Consolas, Courier New, Courier, monospace;
}
.commslog-server {
background-color: red;
color: white
}
.commslog-client {
background-color: green;
color: white
}
</style>
</head>
<body>
<h1>WebSocket示例</h1>
<p id="stateLabel">准备连接...</p>
<div>
<label for="connectionUrl">WebSocket服务器的URL:</label>
<input id="connectionUrl" />
<button id="connectButton" type="submit">连接</button>
</div>
<p></p>
<div>
<label for="sendMessage">要发送的消息:</label>
<input id="sendMessage" disabled />
<button id="sendButton" type="submit" disabled>发送</button>
<button id="closeButton" disabled>关闭Socket</button>
</div>
<h2>通信日志</h2>
<table style="width: 800px">
<thead>
<tr>
<td style="width: 100px">源</td>
<td style="width: 100px">目标</td>
<td>数据</td>
</tr>
</thead>
<tbody id="commsLog">
</tbody>
</table>
<script>
var connectionUrl = document.getElementById("connectionUrl");
var connectButton = document.getElementById("connectButton");
var stateLabel = document.getElementById("stateLabel");
var sendMessage = document.getElementById("sendMessage");
var sendButton = document.getElementById("sendButton");
var commsLog = document.getElementById("commsLog");
var closeButton = document.getElementById("closeButton");
var socket;
var scheme = document.location.protocol === "https:" ? "wss" : "ws";
var port = document.location.port ? (":" + document.location.port) : "";
connectionUrl.value = scheme + "://" + document.location.hostname + port + "/ChatRoom";
function updateState() {
function disable() {
sendMessage.disabled = true;
sendButton.disabled = true;
closeButton.disabled = true;
}
function enable() {
sendMessage.disabled = false;
sendButton.disabled = false;
closeButton.disabled = false;
}
connectionUrl.disabled = true;
connectButton.disabled = true;
if (!socket) {
disable();
} else {
switch (socket.readyState) {
case WebSocket.CLOSED:
stateLabel.innerHTML = "关闭";
disable();
connectionUrl.disabled = false;
connectButton.disabled = false;
break;
case WebSocket.CLOSING:
stateLabel.innerHTML = "正在关闭...";
disable();
break;
case WebSocket.CONNECTING:
stateLabel.innerHTML = "正在连接...";
disable();
break;
case WebSocket.OPEN:
stateLabel.innerHTML = "打开";
enable();
break;
default:
stateLabel.innerHTML = "未知的WebSocket状态: " + htmlEscape(socket.readyState);
disable();
break;
}
}
}
closeButton.onclick = function () {
if (!socket || socket.readyState !== WebSocket.OPEN) {
alert("socket未连接");
}
socket.close(1000, "从客户端关闭");
};
sendButton.onclick = function () { // 发送
if (!socket || socket.readyState !== WebSocket.OPEN) { // 检测socket是否已连接
alert("socket未连接");
}
var data = sendMessage.value;
socket.send(data); // 发送
commsLog.innerHTML += '<tr>' +
'<td class="commslog-client">客户端</td>' +
'<td class="commslog-server">服务器</td>' +
'<td class="commslog-data">' + htmlEscape(data) + '</td></tr>';
};
connectButton.onclick = function() { // 连接
stateLabel.innerHTML = "连接中...";
socket = new WebSocket(connectionUrl.value); // 创建WebSocket对象
socket.onopen = function (event) { // WebSocket打开
socket.send("有新的伙伴加入了聊天室"); // 发送
updateState();
commsLog.innerHTML += '<tr>' +
'<td colspan="3" class="commslog-data">连接已打开!</td>' +
'</tr>';
};
socket.onclose = function (event) { // WebSocket关闭
updateState();
commsLog.innerHTML += '<tr>' +
'<td colspan="3" class="commslog-data">连接已关闭!Code: ' + htmlEscape(event.code) + '. Message: ' + htmlEscape(event.reason) + '</td>' +
'</tr>';
};
socket.onerror = updateState; // 报错
socket.onmessage = function (event) { // 接收消息,消息内容为event.data
commsLog.innerHTML += '<tr>' +
'<td class="commslog-server">服务器</td>' +
'<td class="commslog-client">客户端</td>' +
'<td class="commslog-data">' + htmlEscape(event.data) + '</td></tr>';
};
};
function htmlEscape(str) {
return str.toString()
.replace(/&/g, '&')
.replace(/"/g, '"')
.replace(/'/g, ''')
.replace(/</g, '<')
.replace(/>/g, '>');
}
</script>
</body>
</html>
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
- 103.
- 104.
- 105.
- 106.
- 107.
- 108.
- 109.
- 110.
- 111.
- 112.
- 113.
- 114.
- 115.
- 116.
- 117.
- 118.
- 119.
- 120.
- 121.
- 122.
- 123.
- 124.
- 125.
- 126.
- 127.
- 128.
- 129.
- 130.
- 131.
- 132.
- 133.
- 134.
- 135.
- 136.
- 137.
- 138.
- 139.
- 140.
- 141.
- 142.
- 143.
- 144.
- 145.
- 146.
- 147.
- 148.
- 149.
- 150.
- 151.
- 152.
- 153.
- 154.
- 155.
- 156.
- 157.
- 158.
- 159.
- 160.
- 161.
- 162.
- 163.
- 164.
- 165.
- 166.
- 167.
- 168.
- 169.
- 170.
- 171.
- 172.
- 173.
- 174.
作者:꧁执笔小白꧂