WebSocket介绍

WebSocket是HTML5开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。

在WebSocket API中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。

浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。

当你获取 Web Socket 连接后,你可以通过 send() 方法来向服务器发送数据,并通过 onmessage 事件来接收服务器返回的数据。

其实WebSocket与Socket区别不大,只是客户端是在浏览器上实现的,替代了传统的轮询机制,减少带宽和资源

C#中WebSocket定义事件

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace WebSocketsServer
{/// <summary>/// 声明新连接处理事件/// </summary>/// <param name="loginName"></param>/// <param name="e"></param>public delegate void NewConnection_EventHandler(string loginName, EventArgs args);/// <summary>/// 声明接收数据处理事件/// </summary>/// <param name="sender"></param>/// <param name="message"></param>/// <param name="args"></param>public delegate void DataReceive_EventHandler(object sender, string message, EventArgs args);/// <summary>/// 声明断开连接处理事件/// </summary>/// <param name="sender"></param>/// <param name="args"></param>public delegate void Disconncetion_EventHandler(object sender, string message, EventArgs args);
}

WebSocket服务端实现代码

WebSocketServer代码

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;namespace WebSocketsServer
{/// <summary>/// Socket服务端/// </summary>public class WebSocketServer : IDisposable{#region 私有变量/// <summary>/// ip/// </summary>private string _ip = string.Empty;/// <summary>/// 端口/// </summary>private int _port = 0;/// <summary>/// 服务器地址/// </summary>private string _serverLocation = string.Empty;/// <summary>/// Socket对象/// </summary>private Socket _socket = null;/// <summary>/// 监听的最大连接数/// </summary>private int maxListenConnect = 10;/// <summary>/// 是否关闭Socket对象/// </summary>private bool isDisposed = false;private Logger logger = null;/// <summary>/// buffer缓存区字节数/// </summary>private int maxBufferSize = 0;/// <summary>/// 第一个字节,以0x00开始/// </summary>private byte[] FirstByte;/// <summary>/// 最后一个字节,以0xFF结束/// </summary>private byte[] LastByte;#endregion#region 声明Socket处理事件/// <summary>/// Socket新连接事件/// </summary>public event NewConnection_EventHandler NewConnectionHandler;/// <summary>/// Socket接收消息事件/// </summary>public event DataReceive_EventHandler DataReceiveHandler;/// <summary>/// Socket断开连接事件/// </summary>public event Disconncetion_EventHandler DisconnectionHandler;#endregion/// <summary>/// 存放SocketConnection集合/// </summary>List<SocketConnection> SocketConnections = new List<SocketConnection>();#region 构造函数public WebSocketServer(){this._ip = GetLocalMachineIPAddress().ToString();this._port = 9000;this._serverLocation = string.Format("ws://{0}:{1}", this._ip, this._port);Initialize();}public WebSocketServer(string ip, int port){this._ip = ip;this._port = port;this._serverLocation = string.Format("ws://{0}:{1}", this._ip, this._port);Initialize();}public WebSocketServer(string ip, int port, string serverLocation){this._ip = ip;this._port = port;this._serverLocation = serverLocation;Initialize();}#endregion/// <summary>/// 初始化私有变量/// </summary>private void Initialize(){isDisposed = false;logger = new Logger(){LogEvents = true};maxBufferSize = 1024 * 1024;maxListenConnect = 500;FirstByte = new byte[maxBufferSize];LastByte = new byte[maxBufferSize];FirstByte[0] = 0x00;LastByte[0] = 0xFF;}/// <summary>/// 开启服务/// </summary>public void StartServer(){try{//实例化套接字_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//创建IP对象IPAddress address = GetLocalMachineIPAddress();//创建网络端点,包括ip和portIPEndPoint endPoint = new IPEndPoint(address, _port);//将socket与本地端点绑定_socket.Bind(endPoint);//设置最大监听数_socket.Listen(maxListenConnect);logger.Log(string.Format("聊天服务器启动。监听地址:{0}, 端口:{1}", this._ip, this._port));logger.Log(string.Format("WebSocket服务器地址: ws://{0}:{1}", this._ip, this._port));//开始监听客户端Thread thread = new Thread(ListenClientConnect);thread.Start();}catch (Exception ex){logger.Log(ex.Message);}}/// <summary>/// 监听客户端连接/// </summary>private void ListenClientConnect(){try{while (true){//为新建连接创建的SocketSocket socket = _socket.Accept();if (socket != null){//线程不休眠的话,会导致回调函数的AsyncState状态出异常Thread.Sleep(100);SocketConnection socketConnection = new SocketConnection(this._ip, this._port, this._serverLocation){ConnectionSocket = socket};//绑定事件socketConnection.NewConnectionHandler += SocketConnection_NewConnectionHandler;socketConnection.DataReceiveHandler += SocketConnection_DataReceiveHandler;socketConnection.DisconnectionHandler += SocketConnection_DisconnectionHandler;//从开始连接的Socket中异步接收消息socketConnection.ConnectionSocket.BeginReceive(socketConnection.receivedDataBuffer,0, socketConnection.receivedDataBuffer.Length,0, new AsyncCallback(socketConnection.ManageHandshake),socketConnection.ConnectionSocket.Available);//存入集合,以便在Socket发送消息时发送给所有连接的Socket套接字SocketConnections.Add(socketConnection);}}}catch (Exception ex){Console.WriteLine(ex.Message);}}/// <summary>/// SocketConnection监听的新连接事件/// </summary>/// <param name="loginName"></param>/// <param name="args"></param>private void SocketConnection_NewConnectionHandler(string loginName, EventArgs args){NewConnectionHandler?.Invoke(loginName, EventArgs.Empty);}/// <summary>/// SocketConnection监听的消息接收事件/// </summary>/// <param name="sender"></param>/// <param name="msgData"></param>/// <param name="args"></param>private void SocketConnection_DataReceiveHandler(object sender, string msgData, EventArgs args){//新用户连接进来时显示欢迎信息//SocketConnection socketConnection = sender as SocketConnection;Send(msgData);}/// <summary>/// SocketConnection监听的断开连接事件/// </summary>/// <param name="sender"></param>/// <param name="args"></param>private void SocketConnection_DisconnectionHandler(object sender, string message, EventArgs args){if (sender is SocketConnection socket){Send(message);socket.ConnectionSocket.Close();SocketConnections.Remove(socket);}}/// <summary>/// 发送消息/// </summary>/// <param name="message"></param>public void Send(string message){//给所有连接上的发送消息foreach (SocketConnection socket in SocketConnections){if (!socket.ConnectionSocket.Connected){continue;}try{if (socket.IsDataMasked){DataFrame dataFrame = new DataFrame(message);socket.ConnectionSocket.Send(dataFrame.GetBytes());}else{socket.ConnectionSocket.Send(FirstByte);socket.ConnectionSocket.Send(Encoding.UTF8.GetBytes(message));socket.ConnectionSocket.Send(LastByte);}}catch (Exception ex){logger.Log(ex.Message);}}}/// <summary>/// 获取当前主机的IP地址/// </summary>/// <returns></returns>private IPAddress GetLocalMachineIPAddress(){//获取计算机主机名string hostName = Dns.GetHostName();//将主机名解析为IPHostEntryIPHostEntry hostEntry = Dns.GetHostEntry(hostName);foreach (IPAddress address in hostEntry.AddressList){//IP4寻址协议if (address.AddressFamily == AddressFamily.InterNetwork){return address;}}return hostEntry.AddressList[0];}~WebSocketServer(){Close();}public void Dispose(){Close();}public void Close(){if (!isDisposed){isDisposed = true;if (_socket != null){_socket.Close();}foreach (SocketConnection socketConnection in SocketConnections){socketConnection.ConnectionSocket.Close();}SocketConnections.Clear();GC.SuppressFinalize(this);}}}
}

自定义的SocketConnection类

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;namespace WebSocketsServer
{/// <summary>/// Socket成功建立的连接/// </summary>public class SocketConnection{/// <summary>/// 新的Socket连接/// </summary>public Socket ConnectionSocket = null;#region Socket监听事件/// <summary>/// 新连接事件/// </summary>public event NewConnection_EventHandler NewConnectionHandler;/// <summary>/// 数据接收事件/// </summary>public event DataReceive_EventHandler DataReceiveHandler;/// <summary>/// 断开连接事件/// </summary>public event Disconncetion_EventHandler DisconnectionHandler;#endregion#region 私有变量private string _ip = string.Empty;private int _port = 0;private string _serverLocation = string.Empty;private Logger logger;private string loginId;public string LoginId{get => loginId; set => loginId = value;}private bool isDataMasked;public bool IsDataMasked { get => isDataMasked; set => isDataMasked = value; }/// <summary>/// 最大缓存区字节数/// </summary>private int maxBufferSize = 0;/// <summary>/// 握手协议信息/// </summary>private string handshake = string.Empty;/// <summary>/// 握手协议信息(new)/// </summary>private string newHandshake = string.Empty;/// <summary>/// 接收消息的数据缓存区/// </summary>public byte[] receivedDataBuffer;private byte[] firstByte;private byte[] lastByte;private byte[] serverKey1;private byte[] serverKey2;#endregion#region 构造函数public SocketConnection(){Initialize();}public SocketConnection(string ip, int port, string serverLocation){this._ip = ip;this._port = port;this._serverLocation = serverLocation;Initialize();}#endregion/// <summary>/// 初始化变量/// </summary>private void Initialize(){logger = new Logger();maxBufferSize = 1024 * 1024;receivedDataBuffer = new byte[maxBufferSize];firstByte = new byte[maxBufferSize];lastByte = new byte[maxBufferSize];firstByte[0] = 0x00;lastByte[0] = 0xFF;//webSocket携带头信息handshake = "HTTP/1.1 101 Web Socket Protocol Handshake" + Environment.NewLine;handshake += "Upgrade: WebSocket" + Environment.NewLine;handshake += "Connection: Upgrade" + Environment.NewLine;handshake += "Sec-WebSocket-Origin: " + "{0}" + Environment.NewLine;handshake += string.Format("Sec-WebSocket-Location: " + "ws://{0}:{1}" + Environment.NewLine, this._ip, this._port);handshake += Environment.NewLine;newHandshake = "HTTP/1.1 101 Switching Protocols" + Environment.NewLine;newHandshake += "Upgrade: WebSocket" + Environment.NewLine;newHandshake += "Connection: Upgrade" + Environment.NewLine;newHandshake += "Sec-WebSocket-Accept: {0}" + Environment.NewLine;newHandshake += Environment.NewLine;}/// <summary>/// 处理异步接收消息回调方法/// </summary>/// <param name="asyncResult"></param>public void ManageHandshake(IAsyncResult asyncResult){try{string header = "Sec-WebSocket-Version:";int HandshakeLength = (int)asyncResult.AsyncState;byte[] last8Bytes = new byte[8];UTF8Encoding encoding = new UTF8Encoding();String rawClientHandshake = encoding.GetString(receivedDataBuffer, 0, HandshakeLength);Array.Copy(receivedDataBuffer, HandshakeLength - 8, last8Bytes, 0, 8);//现在使用的是比较新的WebSocket协议if (rawClientHandshake.IndexOf(header) != -1){this.isDataMasked = true;string[] rawClientHandshakeLines = rawClientHandshake.Split(new string[] { Environment.NewLine }, System.StringSplitOptions.RemoveEmptyEntries);string acceptKey = "";foreach (string line in rawClientHandshakeLines){if (line.Contains("Sec-WebSocket-Key:")){acceptKey = ComputeWebSocketHandshakeSecurityHash09(line.Substring(line.IndexOf(":") + 2));}}newHandshake = string.Format(newHandshake, acceptKey);byte[] newHandshakeText = Encoding.UTF8.GetBytes(newHandshake);//将数据异步发送到连接的socket上ConnectionSocket.BeginSend(newHandshakeText, 0, newHandshakeText.Length, SocketFlags.None, HandshakeFinished, null);return;}string clientHandshake = encoding.GetString(receivedDataBuffer, 0, receivedDataBuffer.Length - 8);string[] clientHandshakeLines = clientHandshake.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);logger.Log("新的连接请求来自:" + ConnectionSocket.LocalEndPoint + ".正准备进行连接...");// Welcome the new clientforeach (string Line in clientHandshakeLines){logger.Log(Line);if (Line.Contains("Sec-WebSocket-Key1:"))BuildServerPartialKey(1, Line.Substring(Line.IndexOf(":") + 2));if (Line.Contains("Sec-WebSocket-Key2:"))BuildServerPartialKey(2, Line.Substring(Line.IndexOf(":") + 2));if (Line.Contains("Origin:"))try{handshake = string.Format(handshake, Line.Substring(Line.IndexOf(":") + 2));}catch{handshake = string.Format(handshake, "null");}}//为客户端建立响应byte[] handshakeText = Encoding.UTF8.GetBytes(handshake);byte[] serverHandshakeResponse = new byte[handshakeText.Length + 16];byte[] serverKey = BuildServerFullKey(last8Bytes);Array.Copy(handshakeText, serverHandshakeResponse, handshakeText.Length);Array.Copy(serverKey, 0, serverHandshakeResponse, handshakeText.Length, 16);logger.Log("发送握手信息 ...");ConnectionSocket.BeginSend(serverHandshakeResponse, 0, handshakeText.Length + 16, 0, HandshakeFinished, null);logger.Log(handshake);}catch (Exception ex){Console.WriteLine(ex.Message);}}/// <summary>/// 由服务端像客户端发送消息完成回调/// </summary>/// <param name="asyncResult"></param>private void HandshakeFinished(IAsyncResult asyncResult){//结束挂起的异步发送ConnectionSocket.EndSend(asyncResult);ConnectionSocket.BeginReceive(receivedDataBuffer, 0, receivedDataBuffer.Length,0, new AsyncCallback(Read), null);NewConnectionHandler?.Invoke("", EventArgs.Empty);}private void Read(IAsyncResult asyncResult){if (!ConnectionSocket.Connected){return;}string message = string.Empty;DataFrame dataFrame = new DataFrame(receivedDataBuffer);try{if (!this.isDataMasked){//WebSocket协议:消息以0x00和0xFF作为填充字节发送UTF8Encoding encoding = new UTF8Encoding();int startIndex = 0;int endIndex = 0;// Search for the start bytewhile (receivedDataBuffer[startIndex] == firstByte[0]){startIndex++;}// Search for the end byteendIndex = startIndex + 1;while (receivedDataBuffer[endIndex] != lastByte[0] && endIndex != maxBufferSize - 1){endIndex++;}if (endIndex == maxBufferSize - 1){endIndex = maxBufferSize;}// Get the messagemessage = encoding.GetString(receivedDataBuffer, startIndex, endIndex - startIndex);}//ifelse{message = dataFrame.Text;}if ((message.Length == maxBufferSize && message[0] == Convert.ToChar(65533)) ||message.Length == 0){//断开连接logger.Log("message");if (string.IsNullOrEmpty(message)){MessageInfo messageInfo = new MessageInfo(){MsgType = MessageType.None,Message = ""};message = JsonConvert.SerializeObject(messageInfo);}DisconnectionHandler?.Invoke(this, message, EventArgs.Empty);}else{if (DataReceiveHandler != null){logger.Log("接受到的信息 [\"" + message + "\"]");//消息发送DataReceiveHandler(this, message, EventArgs.Empty);}Array.Clear(receivedDataBuffer, 0, receivedDataBuffer.Length);ConnectionSocket.BeginReceive(receivedDataBuffer, 0, receivedDataBuffer.Length, 0, Read, null);}}catch (Exception ex){logger.Log(ex.Message);logger.Log("Socket连接将会被终止.");MessageInfo messageInfo = new MessageInfo(){MsgType = MessageType.Error,Message = ex.Message + Environment.NewLine + "Socket连接将会被终止"};DisconnectionHandler?.Invoke(this, JsonConvert.SerializeObject(messageInfo), EventArgs.Empty);}}private byte[] BuildServerFullKey(byte[] last8Bytes){byte[] concatenatedKeys = new byte[16];Array.Copy(serverKey1, 0, concatenatedKeys, 0, 4);Array.Copy(serverKey2, 0, concatenatedKeys, 4, 4);Array.Copy(last8Bytes, 0, concatenatedKeys, 8, 8);// MD5 HashMD5 MD5Service = MD5.Create();return MD5Service.ComputeHash(concatenatedKeys);}private void BuildServerPartialKey(int keyNum, string clientKey){string partialServerKey = "";byte[] currentKey;int spacesNum = 0;char[] keyChars = clientKey.ToCharArray();foreach (char currentChar in keyChars){if (char.IsDigit(currentChar)) partialServerKey += currentChar;if (char.IsWhiteSpace(currentChar)) spacesNum++;}try{currentKey = BitConverter.GetBytes((int)(Int64.Parse(partialServerKey) / spacesNum));if (BitConverter.IsLittleEndian){Array.Reverse(currentKey);}if (keyNum == 1){serverKey1 = currentKey;}else{serverKey2 = currentKey;}}catch{if (serverKey1 != null){Array.Clear(serverKey1, 0, serverKey1.Length);}if (serverKey2 != null){Array.Clear(serverKey2, 0, serverKey2.Length);}}}private string ComputeWebSocketHandshakeSecurityHash09(string secWebSocketKey){const String MagicKEY = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";String secWebSocketAccept = String.Empty;// 1. Combine the request Sec-WebSocket-Key with magic key.String ret = secWebSocketKey + MagicKEY;// 2. Compute the SHA1 hashSHA1 sha = new SHA1CryptoServiceProvider();byte[] sha1Hash = sha.ComputeHash(Encoding.UTF8.GetBytes(ret));// 3. Base64 encode the hashsecWebSocketAccept = Convert.ToBase64String(sha1Hash);return secWebSocketAccept;}}
}

数据文件相关的类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace WebSocketsServer
{public class DataFrame{DataFrameHeader _header;private byte[] _extend = new byte[0];private byte[] _mask = new byte[0];private byte[] _content = new byte[0];public DataFrame(byte[] buffer){//帧头_header = new DataFrameHeader(buffer);//扩展长度if (_header.Length == 126){_extend = new byte[2];Buffer.BlockCopy(buffer, 2, _extend, 0, 2);}else if (_header.Length == 127){_extend = new byte[8];Buffer.BlockCopy(buffer, 2, _extend, 0, 8);}//是否有掩码if (_header.HasMask){_mask = new byte[4];Buffer.BlockCopy(buffer, _extend.Length + 2, _mask, 0, 4);}//消息体if (_extend.Length == 0){_content = new byte[_header.Length];Buffer.BlockCopy(buffer, _extend.Length + _mask.Length + 2, _content, 0, _content.Length);}else if (_extend.Length == 2){int contentLength = (int)_extend[0] * 256 + (int)_extend[1];_content = new byte[contentLength];Buffer.BlockCopy(buffer, _extend.Length + _mask.Length + 2, _content, 0, contentLength > 1024 * 100 ? 1024 * 100 : contentLength);}else{long len = 0;int n = 1;for (int i = 7; i >= 0; i--){len += (int)_extend[i] * n;n *= 256;}_content = new byte[len];Buffer.BlockCopy(buffer, _extend.Length + _mask.Length + 2, _content, 0, _content.Length);}if (_header.HasMask) _content = Mask(_content, _mask);}public DataFrame(string content){_content = Encoding.UTF8.GetBytes(content);int length = _content.Length;if (length < 126){_extend = new byte[0];_header = new DataFrameHeader(true, false, false, false, 1, false, length);}else if (length < 65536){_extend = new byte[2];_header = new DataFrameHeader(true, false, false, false, 1, false, 126);_extend[0] = (byte)(length / 256);_extend[1] = (byte)(length % 256);}else{_extend = new byte[8];_header = new DataFrameHeader(true, false, false, false, 1, false, 127);int left = length;int unit = 256;for (int i = 7; i > 1; i--){_extend[i] = (byte)(left % unit);left = left / unit;if (left == 0)break;}}}public byte[] GetBytes(){byte[] buffer = new byte[2 + _extend.Length + _mask.Length + _content.Length];Buffer.BlockCopy(_header.GetBytes(), 0, buffer, 0, 2);Buffer.BlockCopy(_extend, 0, buffer, 2, _extend.Length);Buffer.BlockCopy(_mask, 0, buffer, 2 + _extend.Length, _mask.Length);Buffer.BlockCopy(_content, 0, buffer, 2 + _extend.Length + _mask.Length, _content.Length);return buffer;}public string Text{get{if (_header.OpCode != 1)return string.Empty;return Encoding.UTF8.GetString(_content);}}private byte[] Mask(byte[] data, byte[] mask){for (var i = 0; i < data.Length; i++){data[i] = (byte)(data[i] ^ mask[i % 4]);}return data;}}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace WebSocketsServer
{public class DataFrameHeader{private bool _fin;private bool _rsv1;private bool _rsv2;private bool _rsv3;private sbyte _opcode;private bool _maskcode;private sbyte _payloadlength;public bool FIN { get { return _fin; } }public bool RSV1 { get { return _rsv1; } }public bool RSV2 { get { return _rsv2; } }public bool RSV3 { get { return _rsv3; } }public sbyte OpCode { get { return _opcode; } }public bool HasMask { get { return _maskcode; } }public sbyte Length { get { return _payloadlength; } }public DataFrameHeader(byte[] buffer){if (buffer.Length < 2)throw new Exception("无效的数据头.");//第一个字节_fin = (buffer[0] & 0x80) == 0x80;_rsv1 = (buffer[0] & 0x40) == 0x40;_rsv2 = (buffer[0] & 0x20) == 0x20;_rsv3 = (buffer[0] & 0x10) == 0x10;_opcode = (sbyte)(buffer[0] & 0x0f);//第二个字节_maskcode = (buffer[1] & 0x80) == 0x80;_payloadlength = (sbyte)(buffer[1] & 0x7f);}//发送封装数据public DataFrameHeader(bool fin, bool rsv1, bool rsv2, bool rsv3, sbyte opcode, bool hasmask, int length){_fin = fin;_rsv1 = rsv1;_rsv2 = rsv2;_rsv3 = rsv3;_opcode = opcode;//第二个字节_maskcode = hasmask;_payloadlength = (sbyte)length;}//返回帧头字节public byte[] GetBytes(){byte[] buffer = new byte[2] { 0, 0 };if (_fin) buffer[0] ^= 0x80;if (_rsv1) buffer[0] ^= 0x40;if (_rsv2) buffer[0] ^= 0x20;if (_rsv3) buffer[0] ^= 0x10;buffer[0] ^= (byte)_opcode;if (_maskcode) buffer[1] ^= 0x80;buffer[1] ^= (byte)_payloadlength;return buffer;}}
}

自定义的枚举,实体,封装客户端输出类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace WebSocketsServer
{public enum MessageType{Error = -1,None = 0,/// <summary>/// 登录/// </summary>Login = 1,/// <summary>/// 退出/// </summary>Logout = 2,/// <summary>/// 聊天消息/// </summary>ChatInfo = 3,}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace WebSocketsServer
{public class MessageInfo{/// <summary>/// 唯一标识/// </summary>public Guid Identity { get; set; }/// <summary>/// 用户名/// </summary>public string UserName { get; set; }/// <summary>/// 消息类型/// </summary>public MessageType MsgType { get; set; }/// <summary>/// 发送信息/// </summary>public string Message { get; set; }}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace WebSocketsServer
{public class Logger{public bool LogEvents { get; set; }public Logger(){LogEvents = true;}public void Log(string Text){if (LogEvents) Console.WriteLine(Text);}}
}

Program类的实现

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;/// <summary>
/// WebSocket服务端
/// </summary>
namespace WebSocketsServer
{class Program{static void Main(string[] args){WebSocketServer server = new WebSocketServer();server.StartServer();Console.ReadKey();}}
}

HTML页面实现代码如下(客户端)

<!DOCTYPE html>
<html>
<head><meta charset="utf-8" /><title>WebSocket聊天室</title><style type="text/css">.container {font-family: "Courier New";width: 500px;height: 400px;overflow: auto;border: 1px solid black;padding: 8px;background-color: lightgray;}.LockOff {display: none;visibility: hidden;}.LockOn {display: block;visibility: visible;position: absolute;z-index: 999;top: 0px;left: 0px;width: 1024%;height: 768%;background-color: #ccc;text-align: center;padding-top: 20%;filter: alpha(opacity=75);opacity: 0.75;}.userName {color: white;font-size: 12px;}.chatLeft {display: inline-block;color: black;font-size: 14px;margin-left: 20px;padding: 3px;border: 1px solid #ccc;background-color: #fff;text-align: left;vertical-align: middle;}.chatRight {display: inline-block;color: white;font-size: 14px;padding: 3px;border: 1px solid #ccc;background-color: #9eea6a;text-align: left;vertical-align: middle;}.login {width: 100%;display: inline-block;text-align: center;color: #ffff33;font-size: 14px;font-weight: 700;}.logout {width: 100%;display: inline-block;text-align: center;color: #ffa31a;font-size: 14px;}.systemInfo {color: gray;font-size: 15px;}.error {width: 100%;display: inline-block;text-align: center;color: red;font-size: 16px;font-weight: 700;}</style></head>
<body><div id="skm_LockPane" class="LockOff"></div><form id="form1" runat="server"><h1>WebSocket 聊天室</h1><div>按下连接按钮,会通过WebSocket发起一个到聊天浏览器的连接。</div>服务器地址: <input type="text" id="Connection" /> 用户名: <input type="text" id="txtName" value="陈先生" /><button id='ToggleConnection' type="button" onclick='ToggleConnectionClicked();'>连接</button><input type="hidden" value="" id="identity" /><br /><br /><div id='LogContainer' class='container'></div><br /><div id='SendDataContainer'><input type="text" id="DataToSend" size="68" /><button id='SendData' type="button" onclick='SendDataClicked();'>发送</button></div><br /></form><script src="Scripts/jquery-3.3.1.min.js"></script><script type="text/javascript">//webSocket对象var ws;//Socket是否创建var SocketCreated = false;//用户是否退出登录var isUserloggedout = false;//模拟用户唯一标识var identity = "";var userName = "";var LOGIN = 1, LOGOUT = 2, CHATINFO = 3, SYSYEMINFO = 4, ERROR = -1;function lockOn(str) {var lock = document.getElementById('skm_LockPane');if (lock)lock.className = 'LockOn';lock.innerHTML = str;}function lockOff() {var lock = document.getElementById('skm_LockPane');lock.className = 'LockOff';}function ToggleConnectionClicked() {userName = document.getElementById("txtName").value.trim();if (identity.trim() == "") {identity = newGuid();}//(连接尚未建立||连接已建立)if (SocketCreated && (ws.readyState == 0 || ws.readyState == 1)) {lockOn("离开聊天室...");SocketCreated = false;isUserloggedout = true;var data = MsgData(LOGOUT, "【" + userName + "】" + "离开了聊天室!");ws.send(JSON.stringify(data));ws.close();} else {lockOn("进入聊天室...");var data = MsgData(SYSYEMINFO, "准备连接到聊天服务器...");Log(data);try {if ("WebSocket" in window) {ws = new WebSocket("ws://" + document.getElementById("Connection").value);}else if ("MozWebSocket" in window) {ws = new MozWebSocket("ws://" + document.getElementById("Connection").value);}SocketCreated = true;isUserloggedout = false;} catch (ex) {var data = MsgData(ERROR, ex);Log(data);return;}document.getElementById("ToggleConnection").innerHTML = "断开";ws.onopen = WSonOpen;ws.onmessage = WSonMessage;ws.onclose = WSonClose;ws.onerror = WSonError;}};//WebSocket打开事件function WSonOpen() {lockOff();var data = MsgData(SYSYEMINFO, "连接已经建立.");Log(data);$("#SendDataContainer").show();var data = MsgData(LOGIN, "欢迎【" + userName + "】来到聊天室!");ws.send(JSON.stringify(data));};//WebSocket接收消息事件function WSonMessage(event) {Log(event.data);};//WebSocket关闭连接事件function WSonClose() {lockOff();if (isUserloggedout) {var data = MsgData(LOGOUT, "【" + userName + "】" + "离开了聊天室!");Log(JSON.stringify(data));}document.getElementById("ToggleConnection").innerHTML = "连接";$("#SendDataContainer").hide();};//WebSocket发生错误function WSonError() {lockOff();var data = MsgData(ERROR, "远程连接中断...");Log(data);};function SendDataClicked() {if (document.getElementById("DataToSend").value.trim() != "") {var data = MsgData(CHATINFO, document.getElementById("DataToSend").value)ws.send(JSON.stringify(data));document.getElementById("DataToSend").value = "";}};//传递的消息对象function MsgData(MsgType, Message) {var data = new Object();data.Identity = identity;data.UserName = userName;data.MsgType = MsgType;data.Message = Message;return data;}function Log(data) {console.log(data);if (!(data.constructor === Object)) {data = JSON.parse(data);}var html = "";if (data.MsgType === CHATINFO) {if (data.Identity === identity) {html = "<div style='display:inline-block;width:100%;text-align:right;margin-bottom:2px'>";html += "<span class='chatRight'>" + data.Message + "</span>";html += "</div>";}else {html += "<span class='userName'>" + data.UserName + ":</span>";html += "</br>";html += "<span class='chatLeft'>" + data.Message + "</span>";}}else if (data.MsgType === LOGIN) {html = "<span class='login'>" + data.Message + "</span>"}else if (data.MsgType === LOGOUT) {html = "<span class='logout'>" + data.Message + "</span>"}else if (data.MsgType === SYSYEMINFO) {html += "<span class='systemInfo'>" + data.Message + "</span>";}else if (data.MsgType === ERROR) {html = "<span class='error'>" + data + "</span>";}document.getElementById("LogContainer").innerHTML = document.getElementById("LogContainer").innerHTML + html + "<br />";var LogContainer = document.getElementById("LogContainer");LogContainer.scrollTop = LogContainer.scrollHeight;};//JS生成GUID函数,类似.net中的NewID();function newGuid() {var guid = "";for (var i = 1; i <= 32; i++) {var n = Math.floor(Math.random() * 16.0).toString(16);guid += n;if ((i == 8) || (i == 12) || (i == 16) || (i == 20))guid += "-";}return guid;}$(document).ready(function () {$("#SendDataContainer").hide();var WebSocketsExist = false;if ("WebSocket" in window) {WebSocketsExist = true;}if (WebSocketsExist) {var data = MsgData(SYSYEMINFO, "您的浏览器支持WebSocket. 您可以尝试连接到聊天服务器!");Log(data);document.getElementById("Connection").value = "192.168.137.1:9000";} else {var data = MsgData(ERROR, "您的浏览器不支持WebSocket。请选择其他的浏览器再尝试连接服务器。");Log(data);document.getElementById("ToggleConnection").disabled = true;}$("#DataToSend").keypress(function (evt) {if (evt.keyCode == 13) {$("#SendData").click();evt.preventDefault();}})});</script>
</body>
</html>

实现效果如图(打开两个HTML实现聊天功能)

控制台获取的信息如下

完结:

挺有趣的事情,希望熬夜不猝死了,哈哈

C# 使用WebSocket创建聊天室案例相关推荐

  1. springboot 使用 Spring Boot WebSocket 创建聊天室 2-11

    什么是 WebSocket WebSocket 协议是基于 TCP 的一种网络协议,它实现了浏览器与服务器全双工(Full-duplex)通信-允许服务器主动发送信息给客户端. 以前,很多网站为了实现 ...

  2. C# 使用WebSocket创建聊天室

    WebSocket介绍 WebSocket是HTML5开始提供的一种在单个 TCP 连接上进行全双工通讯的协议. 在WebSocket API中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服 ...

  3. 微信小程序之swoole/WebSocket创建聊天室(php)

     一.php安装扩展组件Swoole 参考连接 二.配置linux服务器 在站点的配置文件中#SSL-END下面添加代码 location /wss/ {#通过配置端口指向部署websocker的项目 ...

  4. 基于WebSocket实现聊天室(Node)

    基于WebSocket实现聊天室(Node) WebSocket是基于TCP的长连接通信协议,服务端可以主动向前端传递数据,相比比AJAX轮询服务器,WebSocket采用监听的方式,减轻了服务器压力 ...

  5. FluorineFx + Flex视频聊天室案例开发----客户端

    上一篇<FluorineFx + Flex视频聊天室案例开发----服务器端>详细的介绍了如何利用FluorineFx开发一个及时通信的视频聊天室服务器处理程序,并通过Web网站来宿主这个 ...

  6. 菜鸟学习笔记:Java提升篇10(网络2——UDP编程、TCPSocket通信、聊天室案例)

    菜鸟学习笔记:Java提升篇10(网络2--UDP编程.TCPSocket通信) UDP编程 TCP编程(Socket通信) 单个客户端的连接 多个客户端的连接(聊天室案例) UDP编程 在上一篇中讲 ...

  7. 基于django channel 实现websocket的聊天室

    websocket ​ 网易聊天室? ​ web微信? ​ 直播? 假如你工作以后,你的老板让你来开发一个内部的微信程序,你需要怎么办?我们先来分析一下里面的技术难点 消息的实时性? 实现群聊 现在有 ...

  8. 视频教程-基于Java的WebSocket的聊天室-Java

    基于Java的WebSocket的聊天室 多年 Java 企业级应用开发工作经验,曾参与中国人寿.华夏人寿.泰康人寿.信达财险等保险行业内部业务管理系统以及线上在线产品的开发:参与中国人民银行.清华大 ...

  9. vue手把手带你创建聊天室(vue-native-websocket)

    vue手把手带你创建聊天室(vue-native-websocket) 谓套接字(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象.一个套接字就是网络上进程通信的一端,提供 ...

最新文章

  1. 解决全网90%以上的日期格式转换、日期序列等骚操作问题
  2. 简谈-Python一些常用的爬虫技巧
  3. Operation Queues并发编程
  4. AI 如果 “智力爆炸” ,只有普通智力的人类是蝼蚁还是宠物?
  5. kafka传递文件_从面试角度一文学完 Kafka
  6. 阿里巴巴在 Serverless 计算领域的探索
  7. ×××--PPTP 服务器配置(详细)
  8. 触发Full GC的原因
  9. 深度学习之keras (一) 初探
  10. Chapter1-5_Speech_Recognition(Alignment of HMM, CTC and RNN-T)
  11. 窗体之间传递值的几种方法
  12. break and continue;
  13. Deeplabv3+ 环境配置-Anaconda3 + Pytorch1.8 + Cuda10.1 + opencv3.2.0
  14. 开源可视化bi工具有哪些,干货推荐
  15. 基于Proteus的51单片机仿真
  16. 深度学习模型显示工具netron
  17. 2020上海大学生网络安全赛MISC可乐加冰
  18. Web安全学习系列(1)
  19. 图书管理系统(查找图书和输出全部图书)
  20. python利用公式计算e的值

热门文章

  1. 微信小程序之手机号快速注册
  2. foxmail不能添加google账户
  3. cisco IOS,nexus和Arista 的vrrp
  4. 游戏开发unity性能优化:DOTS导航帖
  5. 代理插件SwitchyOmega安装配置
  6. SUST OJ 1671: 数字拼图
  7. busyer试做MFC简易画板
  8. codeforces 718E. Matvey's Birthday
  9. OSChina 周五乱弹 ——这年头吃屎都不安全了
  10. OCC实战1:搭建QT+OCC+VS环境