直接上代碼:
1. WebSocket.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Net.Sockets;
using System.Net;
using System.Text.RegularExpressions;
using System.Security.Cryptography;
using System.Reflection;
namespace BH.WebSocketServer
{
public class WebSocket
{
log4net.ILog log = log4net.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private Dictionary<string, Session> SessionPool = new Dictionary<string, Session>();
private Dictionary<string, string> MsgPool = new Dictionary<string, string>();
private Socket _socket=null;
public Socket SockeServer
{
get
{
return this._socket;
}
}
#region 啟動WebSocket服務
/// <summary>
/// 啟動WebSocket服務
/// </summary>
public void start(int port)
{
this._socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
SockeServer.Bind(new IPEndPoint(IPAddress.Any, port));
SockeServer.Listen(20);
SockeServer.BeginAccept(new AsyncCallback(Accept), SockeServer);
log.Info("Socket服務已啟動,PORT:"+port);
Console.WriteLine("服務已啟動");
Console.WriteLine("按任意鍵關閉服務");
while (true)
{
Thread.Sleep(20 * 1000);
}
//this._socket = null;
log.Info("Socket服務已停止");
}
#endregion
#region 處理客戶端連接請求
/// <summary>
/// 處理客戶端連接請求
/// </summary>
/// <param name="result"></param>
private void Accept(IAsyncResult socket)
{
// 還原傳入的原始套接字
Socket SockeServer = (Socket)socket.AsyncState;
// 在原始套接字上調用EndAccept方法,返回新的套接字
Socket SockeClient = SockeServer.EndAccept(socket);
byte[] buffer = new byte[4096];
try
{
//接收客戶端的數據
SockeClient.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(Recieve), SockeClient);
//保存登錄的客戶端
Session session = new Session();
session.SockeClient = SockeClient;
session.IP = SockeClient.RemoteEndPoint.ToString();
session.buffer = buffer;
lock (SessionPool)
{
if (SessionPool.ContainsKey(session.IP))
{
this.SessionPool.Remove(session.IP);
}
this.SessionPool.Add(session.IP, session);
}
//准備接受下一個客戶端
SockeServer.BeginAccept(new AsyncCallback(Accept), SockeServer);
Console.WriteLine(string.Format("Client {0} connected", SockeClient.RemoteEndPoint));
}
catch (Exception ex)
{
Console.WriteLine("Error : " + ex.ToString());
}
}
#endregion
public delegate void EventRecieveData(string msg,Socket sockeClient);
public event EventRecieveData onRecievData;
#region 處理接收的數據
/// <summary>
/// 處理接受的數據
/// </summary>
/// <param name="socket"></param>
private void Recieve(IAsyncResult socket)
{
Socket SockeClient = (Socket)socket.AsyncState;
string IP = SockeClient.RemoteEndPoint.ToString();
if (SockeClient == null || !SessionPool.ContainsKey(IP))
{
return;
}
try
{
int length = SockeClient.EndReceive(socket);
byte[] buffer = SessionPool[IP].buffer;
SockeClient.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(Recieve), SockeClient);
string msg = Encoding.UTF8.GetString(buffer, 0, length);
// websocket建立連接的時候,除了TCP連接的三次握手,websocket協議中客戶端與服務器想建立連接需要一次額外的握手動作
if (msg.Contains("Sec-WebSocket-Key"))
{
SockeClient.Send(PackageHandShakeData(buffer, length));
SessionPool[IP].isWeb = true;
return;
}
if (SessionPool[IP].isWeb)
{
msg = AnalyzeClientData(buffer, length);
}
//byte[] msgBuffer = PackageServerData(msg);
//foreach (Session se in SessionPool.Values)
//{
// //se.SockeClient.Send(msgBuffer, msgBuffer.Length, SocketFlags.None);
//}
if (this.onRecievData!=null)
{
onRecievData(msg,SockeClient);
}
//add by mo 這里的msg就是客戶端發過來的消息。要處理就這里寫邏輯代碼。
}
catch
{
try
{
SockeClient.Disconnect(true);
Console.WriteLine("客戶端 {0} 斷開連接", IP);
SessionPool.Remove(IP);
}
catch (System.Exception ex)
{
}
}
}
#endregion
#region 客戶端和服務端的響應
/*
* 客戶端向服務器發送請求
*
* GET / HTTP/1.1
* Origin: http://localhost:1416
* Sec-WebSocket-Key: vDyPp55hT1PphRU5OAe2Wg==
* Connection: Upgrade
* Upgrade: Websocket
*Sec-WebSocket-Version: 13
* User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
* Host: localhost:8064
* DNT: 1
* Cache-Control: no-cache
* Cookie: DTRememberName=admin
*
* 服務器給出響應
*
* HTTP/1.1 101 Switching Protocols
* Upgrade: websocket
* Connection: Upgrade
* Sec-WebSocket-Accept: xsOSgr30aKL2GNZKNHKmeT1qYjA=
*
* 在請求中的“Sec-WebSocket-Key”是隨機的,服務器端會用這些數據來構造出一個SHA-1的信息摘要。把“Sec-WebSocket-Key”加上一個魔幻字符串
* “258EAFA5-E914-47DA-95CA-C5AB0DC85B11”。使用 SHA-1 加密,之后進行 BASE-64編碼,將結果做為 “Sec-WebSocket-Accept” 頭的值,返回給客戶端
*/
#endregion
#region 打包請求連接數據
/// <summary>
/// 打包請求連接數據
/// </summary>
/// <param name="handShakeBytes"></param>
/// <param name="length"></param>
/// <returns></returns>
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());
}
#endregion
#region 處理接收的數據
/// <summary>
/// 處理接收的數據
/// 參考 http://www.cnblogs.com/smark/archive/2012/11/26/2789812.html
/// </summary>
/// <param name="recBytes"></param>
/// <param name="length"></param>
/// <returns></returns>
private string AnalyzeClientData(byte[] recBytes, int length)
{
int start = 0;
// 如果有數據則至少包括3位
if (length < 2) return "";
// 判斷是否為結束針
bool IsEof = (recBytes[start] >> 7) > 0;
// 暫不處理超過一幀的數據
if (!IsEof) return "";
start++;
// 是否包含掩碼
bool hasMask = (recBytes[start] >> 7) > 0;
// 不包含掩碼的暫不處理
if (!hasMask) return "";
// 獲取數據長度
UInt64 mPackageLength = (UInt64)recBytes[start] & 0x7F;
start++;
// 存儲4位掩碼值
byte[] Masking_key = new byte[4];
// 存儲數據
byte[] mDataPackage;
if (mPackageLength == 126)
{
// 等於126 隨后的兩個字節16位表示數據長度
mPackageLength = (UInt64)(recBytes[start] << 8 | recBytes[start + 1]);
start += 2;
}
if (mPackageLength == 127)
{
// 等於127 隨后的八個字節64位表示數據長度
mPackageLength = (UInt64)(recBytes[start] << (8 * 7) | recBytes[start] << (8 * 6) | recBytes[start] << (8 * 5) | recBytes[start] << (8 * 4) | recBytes[start] << (8 * 3) | recBytes[start] << (8 * 2) | recBytes[start] << 8 | recBytes[start + 1]);
start += 8;
}
mDataPackage = new byte[mPackageLength];
for (UInt64 i = 0; i < mPackageLength; i++)
{
mDataPackage[i] = recBytes[i + (UInt64)start + 4];
}
Buffer.BlockCopy(recBytes, start, Masking_key, 0, 4);
for (UInt64 i = 0; i < mPackageLength; i++)
{
mDataPackage[i] = (byte)(mDataPackage[i] ^ Masking_key[i % 4]);
}
return Encoding.UTF8.GetString(mDataPackage);
}
#endregion
#region 發送數據
/// <summary>
/// 把發送給客戶端消息打包處理(拼接上誰什么時候發的什么消息)
/// </summary>
/// <returns>The data.</returns>
/// <param name="message">Message.</param>
public static byte[] PackageServerData(string msg)
{
byte[] content = null;
byte[] temp = Encoding.UTF8.GetBytes(msg);
if (temp.Length < 126)
{
content = new byte[temp.Length + 2];
content[0] = 0x81;
content[1] = (byte)temp.Length;
Buffer.BlockCopy(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 >> 8);
content[3] = (byte)(temp.Length & 0xFF);
Array.Copy(temp, 0, content, 4, temp.Length);
}
else
{
content = new byte[temp.Length + 10];
content[0] = 0x81;
content[1] = 127;
content[2] = 0;
content[3] = 0;
content[4] = 0;
content[5] = 0;
content[6] = (byte)(temp.Length >> 24);
content[7] = (byte)(temp.Length >> 16);
content[8] = (byte)(temp.Length >> 8);
content[9] = (byte)(temp.Length & 0xFF);
Array.Copy(temp, 0, content, 10, temp.Length);
}
return content;
}
#endregion
}
}
2. Session.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
namespace BH.WebSocketServer
{
public class Session
{
private Socket _sockeclient;
private byte[] _buffer;
private string _ip;
private bool _isweb = false;
public Socket SockeClient
{
set { _sockeclient = value; }
get { return _sockeclient; }
}
public byte[] buffer
{
set { _buffer = value; }
get { return _buffer; }
}
public string IP
{
set { _ip = value; }
get { return _ip; }
}
public bool isWeb
{
set { _isweb = value; }
get { return _isweb; }
}
}
}
4. 使用方法
WebSockete服務器類庫:
使用的時候,綁定事件,EventRecieveData處理客戶端發來的消息。
` WebSocket socket = new WebSocket();
socket.onRecievData += new WebSocket.EventRecieveData(socket_onRecievData);
socket.start(要監聽的端口號);
void socket_onRecievData(string msg, System.Net.Sockets.Socket sockeClient)
msg就是收到的消息。
sockeClient是發來消息的客戶
5.使用例子:
以下代碼,做成服務,監聽指定端口
private void socket_onRecievData(string msg, System.Net.Sockets.Socket sockeClient)
{
SendData sd = new SendData();
//下面的按真實情況返回。這里只是測試
BH.JsonHelp.SqlResult result = new BH.JsonHelp.SqlResult()
{
result = false,
data = "",
errorMessage = ""
};
sd.data = result;
try
{
string recvMsg = msg;
RecieveData rd = null;
try
{
rd= BH.JsonHelp.JsonHelper.DeserializeJsonToObject<RecieveData>(msg);
}
catch (System.Exception ex)
{
LogOuts.Info("msg:" + msg);
}
if (rd == null)
{
throw new Exception("收到數據格式不對:"+msg);
}
int funcId = rd.functionId;
string recvData = rd.data;
//讀取到的所有EPC標簽
List<string> listRecv = new List<string>();
string errMsg = string.Empty;
//回寫給客戶端
sd.functionId = rd.functionId;
switch (rd.functionId)
{
case 1:
//讀卡的邏輯
result.result = GetEPCInventory(out listRecv, out errMsg);
if (result.result)
{
if (listRecv.Count != 1)
{
if (listRecv.Count > 1)
{
result.result = false;
result.errorMessage = "讀卡器讀取的標簽超過一張";
}
else if (listRecv.Count == 0)
{
result.result = false;
result.errorMessage = "讀卡器附近沒有標簽可操作";
}
}
else
{
string cardData = "";
//以下代碼讀數據是OK的。
//循環讀取確保能讀到數據
for (int i = 0; i < 10; i++)
{
result.result = ReadEPCTag(listRecv[0], out cardData, out errMsg);
if (result.result)
{
result.data = cardData;
result.errorMessage = listRecv[0];
}
else
{
result.result = false;
result.errorMessage = errMsg;
}
if (cardData != "")
{
break;
}
}
//end
/*result.result = ReadEPCTag(listRecv[0], out cardData, out errMsg);
if (result.result)
{
result.errorMessage = listRecv[0];
result.data = cardData;
}
else
{
result.result = false;
result.errorMessage = errMsg;
}*/
}
}
else
{
result.errorMessage = errMsg;
}
break;
case 2:
//寫卡的邏輯。
result.result = GetEPCInventory(out listRecv, out errMsg);
if (result.result)
{
if (listRecv.Count != 1)
{
if (listRecv.Count > 1)
{
result.result = false;
result.errorMessage = "讀卡器讀取的標簽超過一張";
}
else if (listRecv.Count == 0)
{
result.result = false;
result.errorMessage = "讀卡器附近沒有標簽可操作";
}
}
else
{
if (string.IsNullOrEmpty(recvData))
{
result.result = false;
result.errorMessage = "標簽寫入內容不能為空,請輸入一些內容!";
}
else
{
recvData = recvData.Replace(" ", "").Replace("\r\n", "");
//加循環確保寫入成功
for (int i = 0; i < 10; i++)
{
result.result = WriteEPCTag(listRecv[0], recvData, out errMsg);
if (result.result)
{
result.data = listRecv[0];
result.errorMessage = listRecv[0];
break;
}
else
{
result.result = false;
result.errorMessage = errMsg;
}
}
/*result.result = WriteEPCTag(listRecv[0], recvData, out errMsg);
if (result.result)
{
result.data = listRecv[0];
result.errorMessage = "標簽寫入成功!";
}
else
{
result.result = false;
result.errorMessage = errMsg;
}*/
}
}
}
else
{
result.errorMessage = errMsg;
}
break;
case 3:
//獲取標簽ID
result.result = GetEPCInventory(out listRecv, out errMsg);
if (result.result)
{
if (listRecv.Count != 1)
{
if (listRecv.Count > 1)
{
result.result = false;
result.errorMessage = "讀卡器讀取的標簽超過一張";
}
else if (listRecv.Count == 0)
{
result.result = false;
result.errorMessage = "讀卡器附近沒有標簽可操作";
}
}
else
{
string cardData = "";
// edit my mo 2021-1-6 不讀取數據。直接返加ID
result.result = true;
result.errorMessage = listRecv[0];
//以下代碼讀數據是OK的。
//循環讀取確保能讀到數據
//for (int i = 0; i < 10; i++)
//{
// result.result = ReadEPCTag(listRecv[0], out cardData, out errMsg);
// if (result.result)
// {
// result.data = cardData;
// result.errorMessage = listRecv[0];
// }
// else
// {
// result.result = false;
// result.errorMessage = errMsg;
// }
// if (cardData != "")
// {
// break;
// }
//}
//end
/*result.result = ReadEPCTag(listRecv[0], out cardData, out errMsg);
if (result.result)
{
result.errorMessage = listRecv[0];
result.data = cardData;
}
else
{
result.result = false;
result.errorMessage = errMsg;
}*/
}
}
else
{
result.errorMessage = errMsg;
}
break;
}
//LogOuts.Info("返回結果:" + JsonHelper.SerializeObject(sd));
}
catch (System.Exception ex)
{
result.errorMessage = ex.Message;
LogOuts.Info("ex:"+ex.ToString());
}
try
{
byte[] msgBuffer = BH.WebSocketServer.WebSocket.PackageServerData(JsonHelper.SerializeObject(sd));
sockeClient.Send(msgBuffer, msgBuffer.Length, SocketFlags.None);
}
catch (Exception ex)
{
LogOuts.Info(ex.Message.ToString());
}
}
6. web通過js 連接socket服務。
文件名:
rwinterface.js
var FUNCIDS = {
_read:1,
_write:2,
_getId:3,
};
var SmartReader={
OBJ:function(){
var reader={};
var SocketOpen=false;
var socket=null;
var target=null;
reader.onResult=function(func){
target.addEvent("Result", func);
};
var WSonOpen=function(){
SocketOpen=true;
};
var WSonMessage=function(msg){
//回調定義,返回json,json有一個data字段,是數據
var str = "";
str = msg.data;
console.log(str);
var resultData=eval("("+msg.data+")");
resultData.type="Result";
if(target!=null){
target.fireEvent(resultData);
}
};
var WSonClose=function(){
SocketOpen=false;
};
var WSonError=function(){
SocketOpen=false;
//alert("RFID讀寫服務未開啟,先下載服務並安裝!");
};
reader.createSocket=function(){
try{
if ("WebSocket" in window){
socket = new WebSocket("ws://localhost:13001/");
}
else if("MozWebSocket" in window){
socket = new MozWebSocket("ws://localhost:13001/");
}
else{
alert("None");
return false;
}
socket.onopen= WSonOpen;
socket.onmessage= WSonMessage;
socket.onclose= WSonClose;
socket.onerror= WSonError;
target = new EventTarget();
return true;
}
catch (ex){
return false;
}
};
reader.Disconnect=function(){
if(socket!=null)
socket.close();
};
reader.getOBJ = function(id){
return reader;
};
var SendCmd=function(FunctionID, ParamStr){
var entryCmd;
if(true == SocketOpen)
{
entryCmd = {functionId:FunctionID,data:ParamStr}
socket.send(JSON.stringify(entryCmd));
}
};
reader.write=function(data){
if (!SocketOpen){
alert("RFID讀寫服務未開啟,先下載服務並安裝!");
}
SendCmd(FUNCIDS._write, data);
};
reader.read=function(){
if (!SocketOpen){
alert("RFID讀寫服務未開啟,先下載服務並安裝!");
}
SendCmd(FUNCIDS._read);
};
reader.getId=function(){
if (!SocketOpen){
alert("RFID讀寫服務未開啟,先下載服務並安裝!");
}
SendCmd(FUNCIDS._getId);
}
return reader;
}
};
function EventTarget()
{
this.handlers = {};
}
EventTarget.prototype = {
constructor: EventTarget,addEvent: function(type, handler){
if(typeof this.handlers[type] == 'undefined'){this.handlers[type] = [];}this.handlers[type].push(handler);
},fireEvent: function(event){
if(!event.target){
event.target = this;
}
if(this.handlers[event.type] instanceof Array){
var handlers = this.handlers[event.type];for(var i = 0; i < handlers.length; i++){
handlers[i](event);
}
}},removeEvent: function(type, handler){
if(this.handlers[type] instanceof Array){
var handlers = this.handlers[type];
for(var i = 0; i < handlers.length; i++){
if(handlers[i] == handler){break;}
}
handlers.splice(i, 1);
}
}
};
function _getCmdResult(relPara)
{
var iRel;
var separator = relPara.indexOf(",");
if(separator != -1)
{
iRel = relPara.substr(0, separator);
}
else
iRel = relPara.substr(0);
return iRel;
}
function _getResultPara(relPara)
{
var szPara="";
var separator = relPara.indexOf(",");
if(separator != -1)
{
szPara = relPara.substr(separator+1);
}
return szPara;
}
try
{
var embed_reader = SmartReader.OBJ();
}
catch(e)
{
}
if(!embed_reader.createSocket())
{
}
使用:
<script type="text/javascript" charset="utf-8" src="/js/rwinterface.js?v=1" charset="utf-8"></script>
<script type="text/javascript">
var rfid_obj = embed_reader.getOBJ();
rfid_obj.onResult(function(rData){
var result = rData.data;
var cCaseCode = document.getElementById("cCaseCode");
switch(rData.functionId)
{
case FUNCIDS._getId:
//讀取回調
if (result.result)
{
//var str = unescape(result.data.replace(/\u/g, "%u"));
//cCaseCode.value=str; //把返回的
$.post("AjaxEdit.aspx",{"iOPType":2,"cID":result.errorMessage},function(data){
var res = data.split('|@|');
if (res[0] == "-1") {
alert(res[1]);
return;
}
else {
//cCaseCode.value=res[1];
getCaseInfo(res[1]);
}
},"");
setTimeout("ReadRFID()",100);
}
else {
alert("讀RFID出錯:" + result.errorMessage);
}
break;
}
});
//讀取RFID
function ReadRFID(){
rfid_obj.getId();
}
</script>
