本文实例为大家分享了C#基于WebSocket实现聊天室功能的具体代码,供大家参考,具体内容如下
前面两篇温习了,C# Socket内容
本章根据Socket异步聊天室修改成WebSocket聊天室
WebSocket特别的地方是 握手和消息内容的编码、解码(添加了ServerHelper协助处理)
ServerHelper:
using System;
using System.Collections;
using System.Text;
using System.Security.Cryptography;
namespace SocketDemo
{
// Server助手 负责:1 握手 2 请求转换 3 响应转换
class ServerHelper
{
/// <summary>
/// 输出连接头信息
/// </summary>
public static string ResponseHeader(string requestHeader)
{
Hashtable table = new Hashtable();
// 拆分成键值对,保存到哈希表
string[] rows = requestHeader.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
foreach (string row in rows)
{
int splitIndex = row.IndexOf(':');
if (splitIndex > 0)
{
table.Add(row.Substring(0, splitIndex).Trim(), row.Substring(splitIndex + 1).Trim());
}
}
StringBuilder header = new StringBuilder();
header.Append("HTTP/1.1 101 Web Socket Protocol Handshake\r\n");
header.AppendFormat("Upgrade: {0}\r\n", table.ContainsKey("Upgrade") ? table["Upgrade"].ToString() : string.Empty);
header.AppendFormat("Connection: {0}\r\n", table.ContainsKey("Connection") ? table["Connection"].ToString() : string.Empty);
header.AppendFormat("WebSocket-Origin: {0}\r\n", table.ContainsKey("Sec-WebSocket-Origin") ? table["Sec-WebSocket-Origin"].ToString() : string.Empty);
header.AppendFormat("WebSocket-Location: {0}\r\n", table.ContainsKey("Host") ? table["Host"].ToString() : string.Empty);
string key = table.ContainsKey("Sec-WebSocket-Key") ? table["Sec-WebSocket-Key"].ToString() : string.Empty;
string magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
header.AppendFormat("Sec-WebSocket-Accept: {0}\r\n", Convert.ToBase64String(SHA1.Create().ComputeHash(Encoding.ASCII.GetBytes(key + magic))));
header.Append("\r\n");
return header.ToString();
}
/// <summary>
/// 解码请求内容
/// </summary>
public static string DecodeMsg(Byte[] buffer, int len)
{
if (buffer[0] != 0x81
|| (buffer[0] & 0x80) != 0x80
|| (buffer[1] & 0x80) != 0x80)
{
return null;
}
Byte[] mask = new Byte[4];
int beginIndex = 0;
int payload_len = buffer[1] & 0x7F;
if (payload_len == 0x7E)
{
Array.Copy(buffer, 4, mask, 0, 4);
payload_len = payload_len & 0x00000000;
payload_len = payload_len | buffer[2];
payload_len = (payload_len << 8) | buffer[3];
beginIndex = 8;
}
else if (payload_len != 0x7F)
{
Array.Copy(buffer, 2, mask, 0, 4);
beginIndex = 6;
}
for (int i = 0; i < payload_len; i++)
{
buffer[i + beginIndex] = (byte)(buffer[i + beginIndex] ^ mask[i % 4]);
}
return Encoding.UTF8.GetString(buffer, beginIndex, payload_len);
}
/// <summary>
/// 编码响应内容
/// </summary>
public static byte[] EncodeMsg(string content)
{
byte[] bts = null;
byte[] temp = Encoding.UTF8.GetBytes(content);
if (temp.Length < 126)
{
bts = new byte[temp.Length + 2];
bts[0] = 0x81;
bts[1] = (byte)temp.Length;
Array.Copy(temp, 0, bts, 2, temp.Length);
}
else if (temp.Length < 0xFFFF)
{
bts = new byte[temp.Length + 4];
bts[0] = 0x81;
bts[1] = 126;
bts[2] = (byte)(temp.Length & 0xFF);
bts[3] = (byte)(temp.Length >> 8 & 0xFF);
Array.Copy(temp, 0, bts, 4, temp.Length);
}
else
{
byte[] st = System.Text.Encoding.UTF8.GetBytes(string.Format("暂不处理超长内容").ToCharArray());
}
return bts;
}
}
}
Server:
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
namespace SocketDemo
{
class ClientInfo
{
public Socket Socket { get; set; }
public bool IsOpen { get; set; }
public string Address { get; set; }
}
// 管理Client
class ClientManager
{
static List<ClientInfo> clientList = new List<ClientInfo>();
public static void Add(ClientInfo info)
{
if (!IsExist(info.Address))
{
clientList.Add(info);
}
}
public static bool IsExist(string address)
{
return clientList.Exists(item => string.Compare(address, item.Address, true) == 0);
}
public static bool IsExist(string address, bool isOpen)
{
return clientList.Exists(item => string.Compare(address, item.Address, true) == 0 && item.IsOpen == isOpen);
}
public static void Open(string address)
{
clientList.ForEach(item =>
{
if (string.Compare(address, item.Address, true) == 0)
{
item.IsOpen = true;
}
});
}
public static void Close(string address = null)
{
clientList.ForEach(item =>
{
if (address == null || string.Compare(address, item.Address, true) == 0)
{
item.IsOpen = false;
item.Socket.Shutdown(SocketShutdown.Both);
}
});
}
// 发送消息到ClientList
public static void SendMsgToClientList(string msg, string address = null)
{
clientList.ForEach(item =>
{
if (item.IsOpen && (address == null || item.Address != address))
{
SendMsgToClient(item.Socket, msg);
}
});
}
public static void SendMsgToClient(Socket client, string msg)
{
byte[] bt = ServerHelper.EncodeMsg(msg);
client.BeginSend(bt, 0, bt.Length, SocketFlags.None, new AsyncCallback(SendTarget), client);
}
private static void SendTarget(IAsyncResult res)
{
//Socket client = (Socket)res.AsyncState;
//int size = client.EndSend(res);
}
}
// 接收消息
class ReceiveHelper
{
public byte[] Bytes { get; set; }
public void ReceiveTarget(IAsyncResult res)
{
Socket client = (Socket)res.AsyncState;
int size = client.EndReceive(res);
if (size > 0)
{
string address = client.RemoteEndPoint.ToString(); // 获取Client的IP和端口
string stringdata = null;
if (ClientManager.IsExist(address, false)) // 握手
{
stringdata = Encoding.UTF8.GetString(Bytes, 0, size);
ClientManager.SendMsgToClient(client, ServerHelper.ResponseHeader(stringdata));
ClientManager.Open(address);
}
else
{
stringdata = ServerHelper.DecodeMsg(Bytes, size);
}
if (stringdata.IndexOf("exit") > -1)
{
ClientManager.SendMsgToClientList(address + "已从服务器断开", address);
ClientManager.Close(address);
Console.WriteLine(address + "已从服务器断开");
Console.WriteLine(address + " " + DateTimeOffset.Now.ToString("G"));
return;
}
else
{
Console.WriteLine(stringdata);
Console.WriteLine(address + " " + DateTimeOffset.Now.ToString("G"));
ClientManager.SendMsgToClientList(stringdata, address);
}
}
// 继续等待
client.BeginReceive(Bytes, 0, Bytes.Length, SocketFlags.None, new AsyncCallback(ReceiveTarget), client);
}
}
// 监听请求
class AcceptHelper
{
public byte[] Bytes { get; set; }
public void AcceptTarget(IAsyncResult res)
{
Socket server = (Socket)res.AsyncState;
Socket client = server.EndAccept(res);
string address = client.RemoteEndPoint.ToString();
ClientManager.Add(new ClientInfo() { Socket = client, Address = address, IsOpen = false });
ReceiveHelper rs = new ReceiveHelper() { Bytes = this.Bytes };
IAsyncResult recres = client.BeginReceive(rs.Bytes, 0, rs.Bytes.Length, SocketFlags.None, new AsyncCallback(rs.ReceiveTarget), client);
// 继续监听
server.BeginAccept(new AsyncCallback(AcceptTarget), server);
}
}
class Program
{
static void Main(string[] args)
{
Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
server.Bind(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 200)); // 绑定IP+端口
server.Listen(10); // 开始监听
Console.WriteLine("等待连接...");
AcceptHelper ca = new AcceptHelper() { Bytes = new byte[2048] };
IAsyncResult res = server.BeginAccept(new AsyncCallback(ca.AcceptTarget), server);
string str = string.Empty;
while (str != "exit")
{
str = Console.ReadLine();
Console.WriteLine("ME: " + DateTimeOffset.Now.ToString("G"));
ClientManager.SendMsgToClientList(str);
}
ClientManager.Close();
server.Close();
}
}
}
Client:
<!DOCTYPE html>
<script>
var mySocket;
function Star() {
mySocket = new WebSocket("ws://127.0.0.1:200", "my-custom-protocol");
mySocket.onopen = function Open() {
Show("连接打开");
};
mySocket.onmessage = function (evt) {
Show(evt.data);
};
mySocket.onclose = function Close() {
Show("连接关闭");
mySocket.close();
};
}
function Send() {
var content = document.getElementById("content").value;
Show(content);
mySocket.send(content);
}
function Show(msg) {
var roomContent = document.getElementById("roomContent");
roomContent.innerHTML = msg + "<br/>" + roomContent.innerHTML;
}
</script>
<html>
<head>
<title></title>
</head>
<body>
<div id="roomContent" style="width: 500px; height: 200px; overflow: hidden; border: 2px solid #686868;
margin-bottom: 10px; padding: 10px 0px 0px 10px;">
</div>
<div>
<textarea id="content" cols="50" rows="3" style="padding: 10px 0px 0px 10px;"></textarea>
</div>
<input type="button" value="Connection" οnclick="Star()" />
<input type="button" value="Send" οnclick="Send()" />
</body>
</html>