一、产品外观
因为项目中需要使用网络接口ETHICR IC读卡器,公司购买了科星互联产品,外观如下:
二、开发前设置
使用产品提供的开发工具,可以对读卡器进行配置,其中最主要的就是两点:
1、IP地址配置:
本地IP为读卡器的IP地址:192.168.1.230
子网掩码:255.255.255.0
网关地址为网络中路由器的IP地址:192.168.1.1
远程IP为工作模式使用TCP客户端时使用:192.168.1.251
端品号为工作模式使用TCP时使用:50000
数据监测:0Min(不用修改)
2、工作模式配置:
TCP服务器:
使用此模式时,上位机必须连接到上面设置的IP地址及端口号,如果有读卡时,将实时通过TCP传送卡号。
使用此模式前,必须创建 Socket连接。
TCP客户端:(我是使用此模式,这样节约资源,方便管理,如果存在多个读卡器时)
此模式与TCP服务器模式差不多,差别就是创建 Socket连接时,读卡器连接到上位机。
使用此模式前,必须创建 Socket连接。
其他暂不用就不多说了。
配置软件界面如下:
三、C#开发
1、创建读卡器类 Reader:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace NetReader
{
public class Reader
{
public int Port { get; set; }
public bool Reading { get; set; } //读卡器读与设置状态
public bool IsOpen { get; set; } //端口连接状态
public int Index { get; set; }
public int ReaderType { get; set; } //读卡器类型,3=二维码,4=科星互联网口读卡器
private Socket socketSend; //负责发送信息的socket
private Socket socketWatch; //监听socket
//将远程连接的客户端的IP地址和Socket存入集合中
private Dictionary<string, Socket> dicSocket = new Dictionary<string, Socket>();
public delegate void QRCodeReaderDataReceive(int index, string cardId);
public event QRCodeReaderDataReceive dataReceive;
public delegate void QRCodeReaderStatus(int index, string status);
public event QRCodeReaderStatus readerStatus;
//缺省9999端口
public Server(int _index, int _port = 9999)
{
Index = _index;
Port = _port;
}
/*
* 开启监听
*/
public void StartMonitor()
{
try
{
//当点击开始监听的时候 在服务器端创建一个负责监听IP地址和端口号的Socket
//addressFamily: Socket:使用的寻址方案,socketType:Socket 的类型,protocolType:Socket 使用的协议。
socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress ip = IPAddress.Any;
//创建服务器端网络端点对象
IPEndPoint point = new IPEndPoint(ip, Port);
//侦听Soket与本地终结点绑定
socketWatch.Bind(point);
//设置为监听状态
socketWatch.Listen(10);
readerStatus?.Invoke(Index, String.Format("{1} 服务在端口:{0} 监听成功!" , Port, GetReaderTypeName()));
//新建线程等待连接,创建通信Socket
Thread th = new Thread(Listen);
th.IsBackground = true;//设置为后台线程
th.Start(socketWatch);
IsOpen = true;
}
catch(Exception ex)
{
readerStatus?.Invoke(Index, string.Format("启动监听 {0} 失败:{1}", GetReaderTypeName(), ex.Message));
IsOpen = false;
}
}
private string GetReaderTypeName()
{
return ReaderType == 3 ? "二维码读卡器" : "网口读卡器";
}
/*
* 监听服务
*/
private void Listen(object o)
{
Socket sWatch = o as Socket;
//循环目的在于使得多个客户端可以连接服务器
while (Reading)
{
try
{
//等待客户端连接,由侦听的Socket创建一个负责与客户端通信的Socket
socketSend = sWatch.Accept();
//将远程连接的客户端的IP地址和Socket存入集合中
dicSocket.Add(socketSend.RemoteEndPoint.ToString(), socketSend);
//将远程连接的客户端的IP地址和端口号存入下拉框中
readerStatus?.Invoke(Index, socketSend.RemoteEndPoint.ToString() + ":" + string.Format(" {0} 连接成功", GetReaderTypeName())); //获得远程的客户端的IP地址和端口号
//创建新线程不停的接受客户端发过来的消息
Thread th = new Thread(Receive);
th.IsBackground = true;
th.Start(socketSend);
}
catch(Exception ex)
{
readerStatus?.Invoke(Index, string.Format("监听 {0} 失败:{1}", GetReaderTypeName(), ex.Message));
}
}
}
/// <summary>
/// 服务器端不停的接受客户端发来的消息
/// </summary>
/// <param name="o"></param>
private void Receive(object o)
{
Socket sSend = o as Socket;
while (Reading)
{
if (sSend.Connected)
{
try
{
//客户端连接成功后接受客户端发来的消息
byte[] buf = new byte[1024 * 1024 * 2];
int r = sSend.Receive(buf); //实际接受的字节数
string data = "";
if (r == 0) //如果客户端关闭,发送的有效字节数为0
{
readerStatus?.Invoke(Index, string.Format("接收 {0} 数据长度为零,终止连接。", GetReaderTypeName()));
IsOpen = false;
break;
}
else
{
byte[] recData = new byte[r];
Array.Copy(buf, 0, recData, 0, r); //5A AA
switch (r)
{
case 6: //QRcode connected.
data = Encoding.UTF8.GetString(buf, 0, r);
if (data.Equals("dragon"))
{
Debug.Print("only receive heardbeat information, don't send heardbeat");
}
break;
case 7: //Network interface reader connected.
if (recData[0] == 0x55 && recData[1] == 0xAA) //heardbeat
{
try
{
if (dicSocket.Count > 0)
{
dicSocket[sSend.RemoteEndPoint.ToString()].Send(recData);
}
}
catch
{
}
}
break;
default:
switch (ReaderType)
{
case 3:
data = Encoding.UTF8.GetString(buf, 0, r);
string id = getCardId(data);
if (!id.Equals(""))
{
//QRcode include 'code=0000' flag.
byte[] bufSend = Encoding.UTF8.GetBytes("code=0000");
try
{
if (dicSocket.Count > 0)
{
//Notify device receive success.
dicSocket[sSend.RemoteEndPoint.ToString()].Send(bufSend);
}
Debug.Print("Scan data:" + id);
dataReceive?.Invoke(Index, id);
}
catch
{
}
}
break;
case 4:
if (r > 5) // && r == 10
{
try
{
//byte[] cardId = new byte[recData.Length - 5];
//Array.Copy(recData, 5, cardId, 0, recData.Length - 5);
//string ids = byteToHexStr(recData, recData.Length);
string ids = Encoding.ASCII.GetString(recData);
Debug.Print(ids);
dataReceive?.Invoke(Index, ids);
}
catch
{
}
}
break;
}
break;
}
}
}
catch (Exception ex)
{
readerStatus?.Invoke(Index, string.Format("接收 {0} 数据发生错误:{1}", GetReaderTypeName(), ex.Message));
IsOpen = false;
break;
}
}
else
{
readerStatus?.Invoke(Index, string.Format("接收 {0} 数据发生错误:连接断开!", GetReaderTypeName()));
IsOpen = false;
break;
}
}
}
/**
* 给出当前卡号
* vgdecoderesult=1333470964&&devicenumber=1&&otherparams=
*/
private string getCardId(string raw)
{
if (raw.Length > 17)
{
int start = raw.IndexOf("vgdecoderesult=");
int end = raw.IndexOf("&&", start);
try
{
return raw.Substring(start + 15, end - start - 15);
}
catch (Exception)
{
return "";
}
}
else
{
return "";
}
}
/*
* 关闭监听服务
*/
public void Close()
{
try
{
IsOpen = false;
Reading = false;
if (socketWatch != null)
{
if (socketWatch.Connected) socketWatch.Disconnect(true);
socketWatch.Close();
socketWatch.Dispose();
}
if (dicSocket.Count > 0)
{
dicSocket.Remove(socketSend.RemoteEndPoint.ToString());
}
if (socketSend != null)
{
if (socketSend.Connected) socketSend.Disconnect(true);
socketSend.Close();
socketSend.Dispose();
}
}
catch (Exception ex)
{
readerStatus?.Invoke(Index, "关闭QRCode监听服务发生错误:" + ex.Message);
}
}
/**
* 字符串转16进制
*/
public byte[] HexStringToByteArray(string s)
{
try
{
s = s.Replace(" ", "");
byte[] buffer = new byte[s.Length / 2];
for (int i = 0; i < s.Length; i += 2)
buffer[i / 2] = (byte)Convert.ToByte(s.Substring(i, 2), 16);
return buffer;
}
catch (Exception)
{
return null;
}
}
//字节数组转16进制字符串
private static string byteToHexStr(byte[] bytes, int length)
{
try
{
string returnStr = "";
if (bytes != null)
{
for (int i = 0; i < length; i++)
{
returnStr += bytes[i].ToString("X2");
}
}
return returnStr;
}
catch (Exception)
{
return "";
}
}
}
}
2、获取卡号问题:(上面代码可以正常获取卡号)
使用Receive获取卡号方法中,如果使用 byte转hex字符串时,与开发工具中读卡的卡号不一样。
//与读卡器获取的卡号不一致
string ids = byteToHexStr(recData, recData.Length);
读卡器卡号是通过 byte转string方式
//此方法与开发工具获取的卡号一致
string ids = Encoding.ASCII.GetString(recData);