有客户在使用我公司实时在线消费机进行Java二次开发时反馈如下问题:
1、在局域网使用时,内网中的服务器可以接收到消费机发送过来的消费请求,消费机也能接收到服务器反回的消费结果。
2、将相同的代码放在云服务器上运行、云服务器可以正常接收到消费机发送的消费请求并做出了回应,可是消费机接收不到云服务器的回应数据。
本示例使用的设备说明:人脸消费机 扫码消费机 云消费机 指纹售饭机 IC实时消费机 韦根
原Java代码如下:
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Arrays;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.net.*;
import java.util.*;
public class PosDemo {
public static void main(String[] args) throws Exception{
String MyIpAdd=getIP(); //获取电脑网卡IP
InetAddress Addip=InetAddress.getByName(MyIpAdd) ;
DatagramSocket s = new DatagramSocket(39192); /** 1、建立udp socket端点 */
System.out.println("当前软件绑定的网卡:"+MyIpAdd+"nn");
boolean i=true;
while(i){
byte [] bbuf = new byte [1024]; /**3、预先创建接收数据存放的位置,封装*/
DatagramPacket rdp = new DatagramPacket(bbuf,bbuf.length);
s.receive(rdp); /**4、使用receive阻塞式接收*/
String ReceiveDataStr=new String(rdp.getData());
String RemoteHostIP=rdp.getAddress().getHostAddress();
int RemotePort=rdp.getPort();
System.out.println("From ip::"+rdp.getAddress().getHostAddress()+"nport::"+rdp.getPort()+"ndata::"+ReceiveDataStr+"nn");
String[] strArr = ReceiveDataStr.split(","); /*分割接收到的数据后再分析、处理、返回指令 */
switch(strArr[0]){
case "102": /*接收到直接刷卡的信息*/
String DevRecFramesStr = strArr[1]; /*包序列号 */
String DevBufferIpAddrStr = strArr[2]; /*终端IP */
String DevBufferRemoteAddrStr = strArr[3]; /*远程电脑指机IP*/
String DevBufferMachinStr = strArr[4]; /*机号*/
String DevBufferCardidStr = strArr[5]; /*卡号*/
if(strArr.length>6){ /*2018年以后的设备有唯一硬件序号*/
DevBufferSerialNumStr=strArr[6];
}
String SendInfStr="001,"+DevRecFramesStr; /*向设备发此数据表示已收到信息,否则设备会连续发三次*/
SendInfToDiv(SendInfStr,RemoteHostIP,RemotePort);
/*此处加入业务对数据库的查、增、删、减操作*/
/*009指令返回持卡人信息*/
SendInfStr="009," + DevBufferMachinStr + ",卡号:" + DevBufferCardidStr;
SendInfStr=SendInfStr+"n姓名:张三丰n余额:888.88n状态:卡可正常使用n,20,1,0" ;
SendInfToDiv(SendInfStr,RemoteHostIP,RemotePort);
break;
case "103": /*接收到 输入消费金额后刷卡、消费机定额消费、消费机计次消费 的上传信息 */
DevRecFramesStr = strArr[1]; /*包序列号 */
DevBufferIpAddrStr = strArr[2]; /*终端IP */
DevBufferRemoteAddrStr = strArr[3]; /*远程电脑指机IP*/
DevBufferMachinStr = strArr[4]; /*机号*/
DevBufferCardidStr = strArr[5]; /*卡号*/
DevBufferUseMoneryStr= strArr[6]; /*消费金额*/
DevBufferUseTimeStr= strArr[7]; /*消费时间*/
if(strArr.length>8){ /*2018年以后的设备有唯一硬件序号*/
DevBufferSerialNumStr=strArr[8];
}
SendInfStr="001,"+DevRecFramesStr; /*向设备发此数据表示已收到信息,否则设备会连续发三次*/
SendInfToDiv(SendInfStr,RemoteHostIP,RemotePort);
/*此处加入业务对数据库的查、增、删、减操作*/
/*008指令返回本次消费成功,006指令返回本次消费失败,正式系统开发时要有重发机制*/
SendInfStr="008," + DevBufferMachinStr + "," + DevBufferCardidStr+ ","+DevBufferUseMoneryStr+"," + "姓名:张三丰 {123.45}n,20,0,1" ;
SendInfToDiv(SendInfStr,RemoteHostIP,RemotePort);
break;
default: //更多的指令说明请参看通讯协议说明
}
}
s.close(); /**5、关闭资源*/
}
/*------------------------------------------------------------------服务器向客户机回复信息*/
static void SendInfToDiv(String Sendinf,String RemoIp,int RemoPro) throws Exception{
DatagramSocket s1 = new DatagramSocket(0); /** 1、建立udp socket端点 */
byte[] SendBuf = Sendinf.getBytes("gb2312"); /** 2、提供发送数据,封装打包 */
DatagramPacket dp1 = new DatagramPacket(SendBuf, SendBuf.length, InetAddress.getByName(RemoIp), RemoPro);
try {
s1.send(dp1);
System.out.println("SendTo ip::"+RemoIp+"nport::"+String.valueOf(RemoPro)+"ndata::"+Sendinf+"nn");
} catch (IOException e) {
System.out.println("发送失败: ");
e.printStackTrace();
}
s1.close(); /**关闭资源*/
}
/*------------------------------------------------------------------------------取电脑IP*/
public static String getIP() {
Enumeration netInterfaces;
ArrayList IpAddList = new ArrayList();
try {
netInterfaces = NetworkInterface.getNetworkInterfaces(); // 拿到所有网卡
InetAddress ip;
while (netInterfaces.hasMoreElements()) {
NetworkInterface ni = netInterfaces.nextElement();
Enumeration addresses = ni.getInetAddresses();
while (addresses.hasMoreElements()) {
ip = addresses.nextElement();
if (!ip.isLoopbackAddress() && ip.getHostAddress().indexOf(':') == -1) {
IpAddList.add(ip.getHostAddress());
System.out.println((IpAddList.size() - 1) + "" + " " + ni.getName() + " " + ip.getHostAddress());
}
}
}
} catch (Exception e) {
}
if (IpAddList.isEmpty()) {
return "127.0.0.1";
} else {
return IpAddList.get(1); //如有多张网卡,请选择与设备相连的网卡,否则无法与设备正常通讯
}
}
}
问题分析:
1、代码在局域网内运行正常,服务器端和客户端都可以接收到对方发送过来的数据,说明代码语法、结构是没问题的。
2、代码在云服务器上,端口可以接收到客户机发送过去的数据,说明端口监听的代码是没有问题的。
3、服务器回应信息遵循原路返回的原理,接收的信息来自哪个IP、端口,回应也是发到相同的IP、端口,这也没有问题。
4、分析服务器回应客户机的函数 SendInfToDiv发现: DatagramSocket s1 = new DatagramSocket(0); 是定义了一个新的Socket来回应客户端的Socket连接,极有可能是这个操作违背了广域网通讯原路返回的原则。
解决问题:
1、修改服务器的回应函数SendInfToDiv,将接收时的Socket做为参数, 用接收的Socket来回应。
static void SendInfToDiv(DatagramSocket s,String Sendinf,String RemoIp,int RemoPro) throws Exception{
//DatagramSocket s1 = new DatagramSocket(0); /** 1、建立udp socket端点 */
byte[] SendBuf = Sendinf.getBytes("gb2312"); /** 2、提供发送数据,封装打包 */
DatagramPacket dp1 = new DatagramPacket(SendBuf, SendBuf.length, InetAddress.getByName(RemoIp), RemoPro);
try {
s.send(dp1);
System.out.println("SendTo ip::"+RemoIp+"nport::"+String.valueOf(RemoPro)+"ndata::"+Sendinf+"nn");
} catch (IOException e) {
System.out.println("发送失败: ");
e.printStackTrace();
}
//s1.close();
}
2、调用时传入接收数据的Socket参数:
SendInfToDiv(s,SendInfStr,RemoteHostIP,RemotePort);
3、局域网内、云服务器上运行修改后的代码,服务器端与客户端都可以收发信息,问题解决。
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
机房租用,北京机房租用,IDC机房托管, http://www.fwqtg.net
继昨天微软E3大会上介绍了Xbox 新特点后,网友们还在猜测苹果是否应该收购任天堂来对抗微软,但今天局势很快就改变了,三星正计划进军游戏界,而其采用的独门暗器是流媒体公司Gaikai的云游戏平台。 三星将于下周的E3大会上发布这项新平台服务,旨在成为继索尼,微…