websocket介绍 WEBSocket是HTML5开始提供的一种在单个 tcp 连接上进行全双工通讯的协议。 在WebSocket api中,浏览器和服务器只需要做一个握手的动作
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和port
IPEndPoint 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)
{
//为新建连接创建的Socket
Socket 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();
//将主机名解析为IPHostEntry
IPHostEntry 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 client
foreach (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 byte
while (receivedDataBuffer[startIndex] == firstByte[0])
{
startIndex++;
}
// Search for the end byte
endIndex = startIndex + 1;
while (receivedDataBuffer[endIndex] != lastByte[0] && endIndex != maxBufferSize - 1)
{
endIndex++;
}
if (endIndex == maxBufferSize - 1)
{
endIndex = maxBufferSize;
}
// Get the message
message = encoding.GetString(receivedDataBuffer, startIndex, endIndex - startIndex);
}//if
else
{
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 Hash
MD5 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 hash
SHA1 sha = new SHA1CryptoServiceProvider();
byte[] sha1Hash = sha.ComputeHash(Encoding.UTF8.GetBytes(ret));
// 3. Base64 encode the hash
secWebSocketAccept = 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实现聊天功能)
控制台获取的信息如下
完结。
--结束END--
本文标题: C#使用WebSocket实现聊天室功能
本文链接: https://lsjlt.com/news/138830.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
2024-03-01
2024-03-01
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0