评论

收藏

[通信技术] socket Udp通讯在云服务器上的应用

网络安全 网络安全 发布于:2021-12-10 11:32 | 阅读数:460 | 评论:0

因项目所需,分别用Java、Python、PHP实现socket进行Udp的通讯,主要功能就是服务器监听Udp通讯端口,当服务器端口接收到客户机发送过来的数据后,对数据进行解析并回应客户机。代码在局域网内使用正常,服务器可以接收到客户机发送过来的数据,客户机也能接收到服务器的回应。可是将代码放在云服务器上运行时发现:云服务器可以正常接收到客户机发送的数据并做出了回应,可是客户机接收不到云服务器的回应数据。
DSC0000.png

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 "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;


            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(s,SendInfStr,RemoteHostIP,RemotePort);


                    /*此处加入业务对数据库的查、增、删、减操作*/


                    /*008指令返回本次消费成功,006指令返回本次消费失败,正式系统开发时要有重发机制*/
                    SendInfStr="008," + DevBufferMachinStr + "," + DevBufferCardidStr+ ","+DevBufferUseMoneryStr+"," + "姓名:张三丰 {123.45}\\n,20,0,1" ;
                    SendInfToDiv(s,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<NetworkInterface> netInterfaces;
        ArrayList<String> IpAddList = new ArrayList<String>();
        try {
            netInterfaces = NetworkInterface.getNetworkInterfaces();    // 拿到所有网卡
            InetAddress ip;
            while (netInterfaces.hasMoreElements()) {
                NetworkInterface ni = netInterfaces.nextElement();
                Enumeration<InetAddress> 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、局域网内、云服务器上运行修改后的代码,服务器端与客户端都可以收发信息,问题解决。


关注下面的标签,发现更多相似文章