首先是服务端:服务端采用控制台程序实现!
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Text.RegularExpressions;
using System.Security.Cryptography;
namespace socket
{
class Program
{
static void Main(string[] args)
{
new socketSe().Run();
}
}
public class socketSe
{
private Dictionary<Socket, ClientInfo> clientPool = new Dictionary<Socket, ClientInfo>();
private List<SocketMessage> msgPool = new List<SocketMessage>();
private bool isClear = true;
Socket soke = null;
Thread trad = null;
public void Run()
{
trad = new Thread(() =>
{
soke = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress ia = IPAddress.Parse("192.168.8.106");
IPEndPoint ie = new IPEndPoint(ia, 8043);
soke.Bind(ie);
//参数:挂起连接队列的最大长度。
soke.Listen(10);
soke.BeginAccept(new AsyncCallback(Accept), soke);
});
trad.Start();
Console.WriteLine("服务器已启动");
Broadcast();
}
private void Broadcast()
{
Thread broadcast = new Thread(() =>
{
while (true)
{
if (!isClear)
{
byte[] msg = PackageServerData(msgPool[0]);
foreach (KeyValuePair<Socket, ClientInfo> cs in clientPool)
{
Socket client = cs.Key;
if (client.Poll(10, SelectMode.SelectWrite))
{
client.Send(msg, msg.Length, SocketFlags.None);
}
}
msgPool.RemoveAt(0);
isClear = msgPool.Count == 0 ? true : false;
}
}
});
broadcast.Start();
}
private void Accept(IAsyncResult result)
{
Socket server = result.AsyncState as Socket;
Socket client = server.EndAccept(result);
try
{
server.BeginAccept(new AsyncCallback(Accept), server);
byte[] buffer = new byte[1024];
client.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(Recieve), client);
ClientInfo info = new ClientInfo();
info.Id = client.RemoteEndPoint;
info.handle = client.Handle;
info.buffer = buffer;
this.clientPool.Add(client, info);
Console.WriteLine(string.Format("客户端 {0} 连接", client.RemoteEndPoint));
}
catch (Exception ex)
{
Console.WriteLine("报错 :\r\n\t" + ex.ToString());
}
}
private void Recieve(IAsyncResult result)
{
Socket client = result.AsyncState as Socket;
if (client == null || !clientPool.ContainsKey(client))
{
return;
}
try
{
int length = client.EndReceive(result);
byte[] buffer = clientPool[client].buffer;
client.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(Recieve), client);
string msg = Encoding.UTF8.GetString(buffer, 0, length);
if (!clientPool[client].IsHandShaked && msg.Contains("Sec-WebSocket-Key"))
{
client.Send(PackageHandShakeData(buffer, length));
clientPool[client].IsHandShaked = true;
return;
}
msg = AnalyzeClientData(buffer, length);
SocketMessage sm = new SocketMessage();
sm.Client = clientPool[client];
sm.Time = DateTime.Now;
Regex reg = new Regex(@"{<(.*?)>}");
Match m = reg.Match(msg);
if (m.Value != "")
{ //处理客户端传来的用户名
clientPool[client].NickName = Regex.Replace(m.Value, @"{<(.*?)>}", "$1");
sm.isLoginMessage = true;
sm.Message = "登录";
Console.WriteLine("{0} login @ {1}", client.RemoteEndPoint, DateTime.Now);
}
else
{
sm.isLoginMessage = false;
sm.Message = msg;
Console.WriteLine("{0} @ {1}\r\n {2}", client.RemoteEndPoint, DateTime.Now, sm.Message);
}
msgPool.Add(sm);
isClear = false;
}
catch
{
client.Disconnect(true);
Console.WriteLine("客户端 {0} 报错", clientPool[client].Name);
clientPool.Remove(client);
}
}
private byte[] PackageHandShakeData(byte[] handShakeBytes, int length)
{
string handShakeText = Encoding.UTF8.GetString(handShakeBytes, 0, length);
string key = string.Empty;
Regex reg = new Regex(@"Sec\-WebSocket\-Key:(.*?)\r\n");
Match m = reg.Match(handShakeText);
if (m.Value != "")
{
key = Regex.Replace(m.Value, @"Sec\-WebSocket\-Key:(.*?)\r\n", "$1").Trim();
}
byte[] secKeyBytes = SHA1.Create().ComputeHash(
Encoding.ASCII.GetBytes(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"));
string secKey = Convert.ToBase64String(secKeyBytes);
var responseBuilder = new StringBuilder();
responseBuilder.Append("HTTP/1.1 101 Switching Protocols" + "\r\n");
responseBuilder.Append("Upgrade: websocket" + "\r\n");
responseBuilder.Append("Connection: Upgrade" + "\r\n");
responseBuilder.Append("Sec-WebSocket-Accept: " + secKey + "\r\n\r\n");
return Encoding.UTF8.GetBytes(responseBuilder.ToString());
}
private string AnalyzeClientData(byte[] recBytes, int length)
{
if (length < 2)
{
return string.Empty;
}
bool fin = (recBytes[0] & 0x80) == 0x80;
if (!fin)
{
return string.Empty;
}
bool mask_flag = (recBytes[1] & 0x80) == 0x80;
if (!mask_flag)
{
return string.Empty;
}
int payload_len = recBytes[1] & 0x7F;
byte[] masks = new byte[4];
byte[] payload_data;
if (payload_len == 126)
{
Array.Copy(recBytes, 4, masks, 0, 4);
payload_len = (UInt16)(recBytes[2] << 8 | recBytes[3]);
payload_data = new byte[payload_len];
Array.Copy(recBytes, 8, payload_data, 0, payload_len);
}
else if (payload_len == 127)
{
Array.Copy(recBytes, 10, masks, 0, 4);
byte[] uInt64Bytes = new byte[8];
for (int i = 0; i < 8; i++)
{
uInt64Bytes[i] = recBytes[9 - i];
}
UInt64 len = BitConverter.ToUInt64(uInt64Bytes, 0);
payload_data = new byte[len];
for (UInt64 i = 0; i < len; i++)
{
payload_data[i] = recBytes[i + 14];
}
}
else
{
Array.Copy(recBytes, 2, masks, 0, 4);
payload_data = new byte[payload_len];
Array.Copy(recBytes, 6, payload_data, 0, payload_len);
}
for (var i = 0; i < payload_len; i++)
{
payload_data[i] = (byte)(payload_data[i] ^ masks[i % 4]);
}
return Encoding.UTF8.GetString(payload_data);
}
private byte[] PackageServerData(SocketMessage sm)
{
StringBuilder msg = new StringBuilder();
if (!sm.isLoginMessage)
{
msg.AppendFormat("{0} @ {1}:\r\n ", sm.Client.Name, sm.Time.ToShortTimeString());
msg.Append(sm.Message);
}
else
{
msg.AppendFormat("{0} 登录 @ {1}", sm.Client.Name, sm.Time.ToShortTimeString());
}
byte[] content = null;
byte[] temp = Encoding.UTF8.GetBytes(msg.ToString());
if (temp.Length < 126)
{
content = new byte[temp.Length + 2];
content[0] = 0x81;
content[1] = (byte)temp.Length;
Array.Copy(temp, 0, content, 2, temp.Length);
}
else if (temp.Length < 0xFFFF)
{
content = new byte[temp.Length + 4];
content[0] = 0x81;
content[1] = 126;
content[2] = (byte)(temp.Length & 0xFF);
content[3] = (byte)(temp.Length >> 8 & 0xFF);
Array.Copy(temp, 0, content, 4, temp.Length);
}
else
{
// 不处理超长内容
}
return content;
}
}
public class ClientInfo
{
public byte[] buffer;
public string NickName { get; set; }
public EndPoint Id { get; set; }
public IntPtr handle { get; set; }
public string Name
{
get
{
if (!string.IsNullOrEmpty(NickName))
{
return NickName;
}
else
{
return string.Format("{0}#{1}", Id, handle);
}
}
}
public bool IsHandShaked { get; set; }
}
public class SocketMessage
{
public bool isLoginMessage { get; set; }
public ClientInfo Client { get; set; }
public string Message { get; set; }
public DateTime Time { get; set; }
}
}客户端:使用一个html文件实现即可
<!DOCTYPE html>
<html>
<head>
<title>简简单单的聊天</title>
<meta charset="utf-8" />
</head>
<body style="padding:10px;">
<h1 style="text-align: center;color: cornflowerblue;">聊天室</h1>
<div style="margin:5px 0px;">
<div><input id="name" type="text" value="" placeholder="请输入用户名" style="width:100%;height: 30px; border-radius: 5px;border: 1px green solid;" /></div>
</div>
<div>
<button id="connect" onclick="connect();">连接服务器</button>
<button id="disconnect" onclick="quit();">下线</button>
<button id="clear" onclick="clearMsg();">清空</button>
</div>
<div id="message" style="border:solid 1px #333; border-radius: 5px; padding:0px; width:100%; overflow:auto;
background-color:#404040; height:300px; margin-bottom:8px; font-size:14px;">
<h5 style="margin:4px 0px; color: white;font-size: 15px;font-family: '微软雅黑'; text-align: center;border-bottom: 1px red dashed;">消息</h5>
</div>
<input id="text" type="text" onkeypress="enter(event);" placeholder="消息" style="width:100%;height: 30px; border-radius: 5px;border: 1px green solid;" />
<button id="send" onclick="send();" style="background-color: goldenrod;border: none;float: right;height: 30px;width: 50px;margin-top: 5px;border-radius: 5px;">发送</button>
<script type="text/javascript">
var name = document.getElementById('name').value;
var msgContainer = document.getElementById('message');
var text = document.getElementById('text');
function connect() {
ws = new WebSocket("ws://192.168.8.106:8043");
ws.onopen = function (e) {
var msg = document.createElement('div');
msg.style.color = '#0f0';
msg.innerHTML = "Server > 已连接,可以聊天了.";
msgContainer.appendChild(msg);
ws.send('{<' + document.getElementById('name').value + '>}');
};
ws.onmessage = function (e) {
var msg = document.createElement('div');
msg.style.color = '#fff';
msg.innerHTML = e.data;
msgContainer.appendChild(msg);
};
ws.onerror = function (e) {
var msg = document.createElement('div');
msg.style.color = '#0f0';
msg.innerHTML = 'Server > ' + e.data;
msgContainer.appendChild(msg);
};
ws.onclose = function (e) {
var msg = document.createElement('div');
msg.style.color = '#0f0';
msg.innerHTML = "Server > 服务器连接不上或已关闭";
msgContainer.appendChild(msg);
};
text.focus();
}
function quit() {
if (ws) {
ws.close();
var msg = document.createElement('div');
msg.style.color = '#0f0';
msg.innerHTML = 'Server > 连接关闭.';
msgContainer.appendChild(msg);
ws = null;
}
}
function send() {
ws.send(text.value);
setTimeout(function () {
msgContainer.scrollTop = msgContainer.getBoundingClientRect().height;
}, 100);
text.value = '';
text.focus();
}
function clearMsg() {
msgContainer.innerHTML = "";
}
function enter(event) {
if (event.keyCode == 13) {
send();
}
}
</script>
</body>
</html>
川公网安备 51010702003150号
留下您的脚步
最近评论