PHP Socket UDP网络读卡器使用示例
<?PHP
header("content-type:text/html;charset=GBK");
$ThisIp=get_local_ip(); //获取电脑IP地址
$server = 'udp://'.$ThisIp.':39169';
$socket = stream_socket_server($server, $errno, $errstr, STREAM_SERVER_BIND);
if (!$socket)
{
die("$errstr ($errno)");
}
echo "系统绑定的IP :".$ThisIp.":39169\n";
$LastBuf= array(9); //保存最后接收到的信息,用于比较是否重复接收的信息
do{
//监听UDP端口,并将接收到的数据以16进制方式显示出来,正式项目只解析数据不需要显示
$inMsg = stream_socket_recvfrom($socket, 1024, 0, $peer);
$len = strlen($inMsg);
$bytes = array();
$Getinf='';
for($i=0;$i<$len;$i++)
{
$byte = ord($inMsg[$i]);
$bytes[] = $byte ;
$b=substr('00'.dechex($byte ),-2);
$Getinf=$Getinf.$b.' ';
}
echo "From Ip :$peer\n";
echo "GetData :{$Getinf}\n\n";
//对接收到的数进行解析
if($len >8){
if($bytes[0]==$LastBuf[0] && $bytes[1]==$LastBuf[1] && $bytes[2]==$LastBuf[2] && $bytes[3]==$LastBuf[3] && $bytes[4]==$LastBuf[4] && $bytes[5]==$LastBuf[5] && $bytes[6]==$LastBuf[6] && $bytes[7]==$LastBuf[7] && $bytes[8]==$LastBuf[8]){
DisableSendAge($bytes,$peer ) ; //接收到相同的信息,确定接收到刷卡器数据,否则读卡器会重发三次,不用处理
}else{
for($i=0;$i<9;$i++){ //将本次接收到且已解析处理的信息保存,用于下次接收到信息时比对是否重复接收到的信息
$LastBuf[$i]=$bytes[$i];
}
switch($bytes[0]){
case hexdec('0xF2'): //接收到读卡器开机、搜索在线设备的返回信息
echo "接收到读卡器开机、搜索在线设备的返回信息\n";
echo "指令码 :".substr('00'.dechex($bytes[0]),-2)."\n";
echo "设备IP :".$bytes[1].".".$bytes[2].".".$bytes[3] .".".$bytes[4] . "\n";
echo "掩 码 :".$bytes[5].".".$bytes[6].".".$bytes[7] .".".$bytes[8] . "\n";
echo "通讯端口:".($bytes[9]+$bytes[10]*256) . "\n";
echo "网 关 :".$bytes[13].".".$bytes[14].".".$bytes[15] .".".$bytes[16] . "\n";
$MAC="";
for($i=17;$i<23;$i++){
$MAC=$MAC.substr('00'.dechex($bytes[$i]),-2);
if($i<22){$MAC=$MAC."-";}
}
echo "网关MAC:".$MAC."\n";
$ServiceIp=$bytes[23].".".$bytes[24].".".$bytes[25] .".".$bytes[26];
echo "远程服务器IP:".$ServiceIp . "\n"; //如果$ServiceIp!=$ThisIp,需将设备的服务器IP设为$ThisIp,否则无法接收到数据。
$MAC="";
for($i=27;$i<33;$i++){
$MAC=$MAC.substr('00'.dechex($bytes[$i]),-2);
if($i<32){$MAC=$MAC."-";}
}
echo "服务器MAC:".$MAC."\n";
echo "网络标识:".substr('00'.dechex($bytes[33]),-2)."\n";
echo "响声标识:".substr('00'.dechex($bytes[34]),-2)."\n";
$MAC="16-88-";
for($i=35;$i<39;$i++){
$MAC=$MAC.substr('00'.dechex($bytes[$i]),-2);
if($i<38){$MAC=$MAC."-";}
}
echo "读卡器MAC:".$MAC."\n";
if(sizeof($bytes)>39){
$SerialNum="";
for($i=39;$i<sizeof($bytes);$i++){
$SerialNum=$SerialNum.substr('00'.dechex($bytes[$i]),-2);
}
echo "设备唯一序号:".$SerialNum."\n";
}
echo "\n";
break;
case hexdec('0xF3'): //接收到读卡器的心跳信息
echo "接收到读卡器心跳数据信息\n";
echo "指令码 :".substr('00'.dechex($bytes[0]),-2)."\n";
echo "设备IP :".$bytes[1].".".$bytes[2].".".$bytes[3] .".".$bytes[4] . "\n";
echo "机号 :".($bytes[5]+$bytes[6]*256) . "\n";
echo "包序号 :".($bytes[7]+$bytes[8]*256) . "\n";
echo "心跳类型:".substr('00'.dechex($bytes[9]),-2)."\n";
echo "长度 :".substr('00'.dechex($bytes[10]),-2)."\n";
echo "继电器状态:".substr('00'.dechex($bytes[11]),-2)."\n";
echo "外部输入状态:".substr('00'.dechex($bytes[12]),-2)."\n";
echo "随机动态码:".substr('00'.dechex($bytes[13]),-2).substr('00'.dechex($bytes[14]),-2).substr('00'.dechex($bytes[15]),-2).substr('00'.dechex($bytes[16]),-2)."\n";
$SerialNum="";
for($i=17;$i<sizeof($bytes);$i++){
$SerialNum=$SerialNum.substr('00'.dechex($bytes[$i]),-2);
}
echo "设备唯一序号:".$SerialNum."\n\n";
break;
case hexdec('0xD1'): //接收到ID卡信息
DisableSendAge($bytes,$peer ) ; //确定接收到刷卡器数据,否则读卡器会重发三次
echo "接收到的信息为ID读卡器上传的卡号\n";
echo "指令码 :".substr('00'.dechex($bytes[0]),-2)."\n";
echo "设备IP :".$bytes[1].".".$bytes[2].".".$bytes[3] .".".$bytes[4] . "\n";
echo "机号 :".($bytes[5]+$bytes[6]*256) . "\n";
echo "包序号 :".($bytes[7]+$bytes[8]*256) . "\n";
$CardNo=$bytes[9] & 0xff;
$CardNo=$CardNo+($bytes[10] & 0xff) *256;
$CardNo=$CardNo+($bytes[11] & 0xff) *65536;
$CardNo=$CardNo+($bytes[12] & 0xff) *16777216;
$CardNo=substr("0000000000".$CardNo,-10);
echo "十进制卡号 :".$CardNo . "\n";
$SerialNum ="";
for($i=14;$i<$len;$i++){
$SerialNum=$SerialNum.substr('00'.dechex($bytes[$i]),-2);
}
echo "设备*** :".$SerialNum . "\n";
$DispStr="卡号:".$CardNo." ".substr(date('Y-m-d h:i:s', time()),0,16)." "; //全屏显示信息长度>=34
//可以在此处加入业务的数据库查、增、删等操作
//将系统处理结果发送到设备上显示,根据设备类型使用以下不同的发送显示、声音指令
SendDispBeep($DispStr,$peer); //向读卡器发送显示及蜂鸣响声
//SendDispSpk($DispStr,$peer);//向读卡器发送显示+继电器+固定组合语音
//SendDispTTS($DispStr,$peer);//向读卡器发送显示+继电器+TTS语音
break;
case hexdec('0xC1'): //接收到IC卡信息
DisableSendAge($bytes,$peer ) ; //确定接收到刷卡器数据,否则读卡器会重发三次
echo "接收到的信息为IC读卡器上传的卡号\n";
echo "指令码 :".substr('00'.dechex($bytes[0]),-2)."\n";
echo "设备IP :".$bytes[1].".".$bytes[2].".".$bytes[3] .".".$bytes[4] . "\n";
echo "机号 :".($bytes[5]+$bytes[6]*256) . "\n";
echo "包序号 :".($bytes[7]+$bytes[8]*256) . "\n";
$CardNo=$bytes[10] & 0xff;
$CardNo=$CardNo+($bytes[11] & 0xff) *256;
$CardNo=$CardNo+($bytes[12] & 0xff) *65536;
$CardNo=$CardNo+($bytes[13] & 0xff) *16777216;
$CardNo=substr("0000000000".$CardNo,-10);
echo "十进制卡号 :".$CardNo . "\n";
$SerialNum ="";
for($i=14;$i<$len;$i++){
$SerialNum=$SerialNum.substr('00'.dechex($bytes[$i]),-2);
}
echo "设备*** :".$SerialNum . "\n";
$DispStr="卡号:".$CardNo." ".substr(date('Y-m-d h:i:s', time()),0,16)." "; //全屏显示信息长度>=34
//可以在此处加入业务的数据库查、增、删等操作
//将系统处理结果发送到设备上显示,根据设备类型使用以下不同的发送显示、声音指令
//SendDispBeep($DispStr,$peer); //向读卡器发送显示及蜂鸣响声
SendDispSpk($DispStr,$peer);//向读卡器发送显示+继电器+固定组合语音
//SendDispTTS($DispStr,$peer);//向读卡器发送显示+继电器+TTS语音
break;
default:
}
}
}
} while ($inMsg !== false);
//向读卡器发送显示+继电器+TTS语音-----------------------------------------------------------------------------------------------------
function SendDispTTS($DispStr,$peer)
{
$SpeakStr="[v10]".$DispStr; //[v10]是语音大小,取值范围0-16,可加在任何地方,其他语音策略请看文档说明
$SpeakStr=$SpeakStr."感谢您的使用"; //要播报的TTS语音,每条指令最大不能超过126字符
$Splen = strlen($SpeakStr); //TTS语音长度
$bytes[0]=hexdec('0x5C'); //指令码
$bytes[1]=0; //机号低,机号为0表示广播
$bytes[2]=0; //机号高
$bytes[3]=2; //蜂鸣声代码
$bytes[4]=hexdec('0xF0'); //继电器代码 F0表示全部继电器 F1表示1号继电器 F2表示2号继电器
$bytes[5]=hexdec('0x20'); //继电器开启时长低位
$bytes[6]=0; //继电器开启时长高位
$bytes[7]=hexdec('0x14'); //显示保留时间,单位为秒,为0xFF时表示永久显示
$bytes[8]=0; //在显示屏中的哪个位置开始
$bytes[9]=hexdec('0x22'); //显示字符串长度 为全屏显示为 34 个字符
$bytes[10]=$Splen; //语音长度
$suffixStr=chr(hexdec('0x55')).chr(hexdec('0xaa')).chr(hexdec('0x66')).chr(hexdec('0x99')); //固定的抗干扰后缀
$sendBuf='';
for($i=0;$i<11;$i++)
{
$sendBuf=$sendBuf.chr($bytes[$i]);
}
$sendBuf=$sendBuf.substr($DispStr,0,34).$SpeakStr.$suffixStr; //最长显示34个字符
$handle = stream_socket_client("udp://".$peer, $errno, $errstr);
fwrite($handle, $sendBuf);
$Sendinf=''; //将发送的信息用16进制数显示,方便调式
for($i=0;$i<strlen($sendBuf);$i++){
$byte = ord($sendBuf[$i]);
$b=substr('00'.dechex($byte ),-2);
$Sendinf=$Sendinf.$b.' ';
}
echo "SendTo IP:".$peer."\n";
echo "SendData :{$Sendinf}\n\n";
}
//向读卡器发送显示+继电器+固定组合语音-------------------------------------------------------------------------------------------------
function SendDispSpk($DispStr,$peer)
{
$Splen=3; //语音代码条数,最多21条
$SpeakStr=chr(55).chr(41).chr(53); //语音代码,根据条数增加,代码取值见通讯协议
$bytes[0]=hexdec('0x5B'); //指令码
$bytes[1]=0; //机号低,机号为0表示广播
$bytes[2]=0; //机号高
$bytes[3]=2; //蜂鸣声代码
$bytes[4]=hexdec('0xF0'); //继电器代码 F0表示全部继电器 F1表示1号继电器 F2表示2号继电器
$bytes[5]=hexdec('0x20'); //继电器开启时长低位
$bytes[6]=0; //继电器开启时长高位
$bytes[7]=hexdec('0x14'); //显示保留时间,单位为秒,为0xFF时表示永久显示
$bytes[8]=0; //在显示屏中的哪个位置开始
$bytes[9]=hexdec('0x24'); //显示字符串长度 为全屏显示为 34 个字符
$bytes[10]=$Splen; //语音代码条数
$suffixStr=chr(hexdec('0x55')).chr(hexdec('0xaa')).chr(hexdec('0x66')).chr(hexdec('0x99')); //固定的抗干扰后缀
$sendBuf='';
for($i=0;$i<11;$i++)
{
$sendBuf=$sendBuf.chr($bytes[$i]);
}
$sendBuf=$sendBuf.substr($DispStr,0,36).$SpeakStr.$suffixStr; //最长显示34个字符
$handle = stream_socket_client("udp://".$peer, $errno, $errstr);
fwrite($handle, $sendBuf);
$Sendinf=''; //将发送的信息用16进制数显示,方便调式
for($i=0;$i<strlen($sendBuf);$i++){
$byte = ord($sendBuf[$i]);
$b=substr('00'.dechex($byte ),-2);
$Sendinf=$Sendinf.$b.' ';
}
echo "SendTo IP:".$peer."\n";
echo "SendData :{$Sendinf}\n\n";
}
//向读卡器发送显示及蜂鸣响声-----------------------------------------------------------------------------------------------------
function SendDispBeep($DispStr,$peer)
{
$bytes[0]=hexdec('0x5A'); //指令码
$bytes[1]=0; //机号低,机号为0表示广播
$bytes[2]=0; //机号高
$bytes[3]=2; //蜂鸣声代码
$bytes[4]=20; //显示时长
$sendBuf='';
for($i=0;$i<5;$i++)
{
$sendBuf=$sendBuf.chr($bytes[$i]);
}
$sendBuf=$sendBuf.$DispStr;
$handle = stream_socket_client("udp://".$peer, $errno, $errstr);
fwrite($handle, $sendBuf);
$Sendinf=''; //将发送的信息用16进制数显示,方便调式
for($i=0;$i<strlen($sendBuf);$i++){
$byte = ord($sendBuf[$i]);
$b=substr('00'.dechex($byte ),-2);
$Sendinf=$Sendinf.$b.' ';
}
echo "SendTo IP:".$peer."\n";
echo "SendData :{$Sendinf}\n\n";
}
//确定接收到刷卡器数据,以免读卡器重复发送--------------------------------------------------------------------------------------------------
function DisableSendAge($bytes,$peer)
{
$bytes[0]=105;
$sendBuf='';
for($i=0;$i<9;$i++)
{
$sendBuf=$sendBuf.chr($bytes[$i]);
}
$handle = stream_socket_client("udp://".$peer, $errno, $errstr);
fwrite($handle, $sendBuf);
$Sendinf=''; //将发送的信息用16进制数显示,方便调式
for($i=0;$i<strlen($sendBuf);$i++){
$byte = ord($sendBuf[$i]);
$b=substr('00'.dechex($byte ),-2);
$Sendinf=$Sendinf.$b.' ';
}
echo "SendTo IP:".$peer."\n";
echo "SendData :{$Sendinf}\n\n";
}
//获取本机IP地址------------------------------------------------------------------------------------------------------------------------
function get_local_ip()
{
$preg = "/\A((([0-9]?[0-9])|(1[0-9]{2})|(2[0-4][0-9])|(25[0-5]))\.){3}(([0-9]?[0-9])|(1[0-9]{2})|(2[0-4][0-9])|(25[0-5]))\Z/";
//获取操作系统为win2000/xp、win7的本机IP真实地址
exec("ipconfig", $out, $stats);
if (!empty($out)) {
foreach ($out AS $row) {
if (strstr($row, "IP") && strstr($row, ":") && !strstr($row, "IPv6")) {
$tmpIp = explode(":", $row);
if (preg_match($preg, trim($tmpIp[1]))) {
return trim($tmpIp[1]);
}
}
}
}
//获取操作系统为linux类型的本机IP真实地址
exec("ifconfig", $out, $stats);
if (!empty($out)) {
if (isset($out[1]) && strstr($out[1], 'addr:')) {
$tmpArray = explode(":", $out[1]);
$tmpIp = explode("", $tmpArray[1]);
if (preg_match($preg, trim($tmpIp[0]))) {
return trim($tmpIp[0]);
}
}
}
return '127.0.0.1';
}
?>