因项目所需,分别用Java、Python、PHP实现socket进行Udp的通讯,主要功能就是服务器监听Udp通讯端口,当服务器端口接收到客户机发送过来的数据后,对数据进行解析并回应客户机。代码在局域网内使用正常,服务器可以接收到客户机发送过来的数据,客户机也能接收到服务器的回应。可是将代码放在云服务器上运行时发现:云服务器可以正常接收到客户机发送的数据并做出了回应,可是客户机接收不到云服务器的回应数据。 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.net.DatagramPacket; import java.net.DatagramSocket; 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+"\n\n"); 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+"\n\n"); String[] strArr = ReceiveDataStr.split("\\,"); /*分割接收到的数据后再分析、处理、返回指令 */ switch(strArr[0]){ case "100": /*设备接收到搜索指令后返回的设备信息*/ break; case "101": /*有设备开机的信号*/ break; 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 + "\\n姓名:张三丰\\n余额:888.88\\n状态:卡可正常使用\\n,20,1,0" ; 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+"\n\n"); } 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{ 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+"\n\n"); } catch (IOException e) { System.out.println("发送失败: "); e.printStackTrace(); } } 2、调用时传入接收数据的Socket参数: SendInfToDiv(s,SendInfStr,RemoteHostIP,RemotePort); 3、局域网内、云服务器上运行修改后的代码,服务器端与客户端都可以收发信息,问题解决。