局域网内的UDP传输——实例讲解 (Java)

先确定一个服务端,服务端要有固定的IP和端口用来接受数据,且服务端IP要已知

例如,目前已知一个服务端IP为192.168.137.20,端口10025

服务端与客户端是一对多的关系

第零步

在客户端向服务端发送数据之前,我们需要先给服务端注册一个监听器,用来监听是否有客户端发来数据
步骤
先来封装一个类,专门用来向客户端发送数据和接收客户端发来的数据

向客户端发送数据我们先不讲,因为此时我们并不知道客户端的IP地址和端口号,所以只能先注册一个监听器,当有客户端向服务端发送数据且服务端收到后,服务端就可以知道客户端的IP地址和端口号了,这时就可以对客户端发送数据进行回复了,所以我们先来看怎么注册监听器。

SendToClient


public class SendToClient {

    private DatagramSocket serverSocket = new DatagramSocket(10025);//创建一个socket,端口号为10025(上文假设),即监听自己的10025这个端口号是否收到数据

    public SendToClient() throws SocketException {

    }

	/*
	* 该函数主要用来向客户端发送数据,详细讲解见下文
	*/
    public void sendDataToClient(StringBuilder str,SocketAddress socketAddress) {
        ......
    }

	/*
	* 该函数为注册监听器
	*/
    public void ReceiveDataFromClient() {
        try {
            while (true) { //开启一个死循环,不断的查看是否有客户端发来数据
                byte data[] = new byte[8 * 1024];//创建一个足够长的字节来作为接收到的数据的容器

				//创建一个packet,用来接收数据
                DatagramPacket packet = new DatagramPacket(data, data.length);

                serverSocket.receive(packet);//接收信息(如果有客户端发送过来)
				
				/*将收到的信息转换成String,使用split是因为发来的数据可能包含多个数据,
				在客户端我使用#来拼接起来了,
				例如,客户端要给服务端发送一个名字和分数 ,
				则客户端发送的数据为 乎如#90,
				这样服务端收到后使用split("#")就可以将名字和分数分离了*/
                String[] result = new String(packet.getData(), 
                					packet.getOffset(), packet.getLength()).split("#");


				//下面是对收到的数据的处理
				
                if (!TextUtils.isEmpty(result[0])) {//如果收到的数据不为空
                    //TODO: 处理收到的数据
					//注意,在这里,我们不仅收到了客户端发来的数据,同时也知道了客户端的IP地址和端口号
					//通过  packet.getSocketAddress()  便可以获取到客户端的IP地址和端口号,具体用法见下文
                }
            }

        } catch (SocketException e) {
            e.printStackTrace();
            Log.e("######","SocketException"+e);
        } catch (IOException e) {
            e.printStackTrace();
            Log.e("######","IOException"+e);
        }
    }

	//关闭socket,释放资源
    public void disconnect(){
        serverSocket.close();
        serverSocket.disconnect();
    }

}

用法

/*
*封装到一个函数中
*/
private void registerServerUDP(){
        try {
            sendToClient = new SendToClient();//实例化SendToClient类
            //Log.e("######ServerService","已实例化");

        } catch (SocketException e) {
            e.printStackTrace();
            //Log.e("######ServerService","非法的ip地址");
        }

		//开启一个线程,死循环不能开在主线程中,而且所有的网络操作都需要在子线程中
        new Thread(new Runnable() {
            @Override
            public void run() {
           		//调用ReceiveDataFromClient()函数,开启监听器
                sendToClient.ReceiveDataFromClient();
                //Log.e("######ServerService","已创建服务器接收器");
            }
        }).start();
    }

这样服务端的监听器就建好了,当有客户端向服务端发送数据时,服务端就能够收到,并进行相应的处理了。

第一步:

接下来,客户端就可以向服务端发送数据了

因为服务端IP和端口已知,且服务端已经建好了监听器,所以可以向服务端发送数据了,而现在服务端无法向客户端发送数据,因为我们现在是不知道客户端的IP和端口号的。

步骤:
首先,封装一个类,专门用来 向服务端发送数据和接受从服务端发过来的数据。

SendToServer

public class SendToServer {

    private DatagramSocket clientSocket = new DatagramSocket();//创建一个socket用来向服务端发送数据

    public SendToServer() throws SocketException {

    }

/**
* 向服务端发送数据  str:要发送的数据 IP:服务端IP地址
*/
    public void sendDataToServer(StringBuilder str,String IP) {

        try {
            InetAddress serverAddress = InetAddress.getByName(IP);//这个IP是已知的,即服务端IP,192.168.137.20(上文假设)
            byte data[] = str.toString().getBytes();//将发送的数据转换成字节
            DatagramPacket packetToServer = new DatagramPacket(data, data.length ,serverAddress,10025);//将上述数据封装到一个packet中,然后才可以用socket发送出去
            clientSocket.send(packetToServer);//向服务端的10025(上文假设)发送数据
        } catch (SocketException e) {
            e.printStackTrace();
            Log.e("######SendToServer","SocketException"+e);
        } catch (UnknownHostException e) {
            e.printStackTrace();
            Log.e("######SendToServer","UnknownHostException"+e);
        } catch (IOException e) {
            e.printStackTrace();
            Log.e("######SendToServer","IOException"+e);
        }
    }

	/*
	* 接受服务端发来的数据,注册客户端监听器,原理和上述服务端注册监听器相同
	*/
   public void ReceiveDataFromServer() {

        try {
            while(true) {//开启一个死循环,不断的查看是否有客户端发来数据
                byte fromServer[] = new byte[4 * 1024];//创建一个足够长的字节来作为接收到的数据的容器
                //创建一个packet,用来接收数据
                DatagramPacket packet = new DatagramPacket(fromServer, fromServer.length);
                clientSocket.receive(packet);//接收信息(如果有客户端发送过来)

				//将收到的信息转换成String
                String result = new String(packet.getData(), packet.getOffset(), packet.getLength());

				//下面是对收到的数据的处理
                if (!TextUtils.isEmpty(result)) {//如果收到的数据不为空
                    //TODO: 处理收到的数据
                    //在这里,我们不仅可以收到服务端发来的数据,同时也可以知道服务端的IP地址和端口号
                    //其实就是192.168.137.20(开头假设)和10025(开头假设)
                    //同样是通过packet.getSocketAddress()来获取
                }
                //Log.e("######SendToServer", "收到服务器返回的信息为:" + result);
            }

        } catch (SocketException e) {
            e.printStackTrace();
            Log.e("######SendToServer","SocketException"+e);
        } catch (IOException e) {
            e.printStackTrace();
            Log.e("######SendToServer","IOException"+e);
        }
    }

	//关闭socket,释放资源
    public void disconnect(){
        clientSocket.close();
        clientSocket.disconnect();
    }

}

用法:

/*
* 实例化并注册客户端监听器
*/
private void registerClientUDP(){
        try {
            sendToServer = new SendToServer();//实例化SendToServer类
            Log.e("######GameActivity","已实例化");
        } catch (SocketException e) {
            e.printStackTrace();
            //Log.e("######GameActivity","非法的ip地址");
        }
		//此处为注册客户端监听器,原理同服务端注册监听器相同
		//开启一个线程,死循环不能开在主线程中,而且所有的网络操作都需要在子线程中
        new Thread(new Runnable() {
            @Override
            public void run() {
           		//调用ReceiveDataFromClient()函数,开启监听器
                sendToServer.ReceiveDataFromServer();
                Log.e("######GameActivity","已创建客户端接收器");
            }
        }).start();
    } 
    
	private void sendMessageToServer(final StringBuilder s){//封装成一个函数
        new Thread(new Runnable() {//开启一个线程来发送数据(所有的网络请求都需要在子线程执行)
            @Override
            public void run() {
                sendToServer.sendDataToServer(s,serverIP);//调用我们封装好的类里的函数,传入两个参数,s:要发送的数据,serverIP:服务端IP
            }
        }).start();
    }

变量 s 类型我设置成了StringBuilder,在使用中完全可以设置成String

这样我们就向服务端发送了一条数据。

第二步

服务端接受客户端发来的数据,同时回复客户端

在SendToClient类里,上文中有一个类的代码没有贴出来,代码如下:
SendToClient里的sendDataToClient()函数

/*
* 这个类即服务端向客户端发送数据的函数,
* 注意这个函数和客户端向服务端发送数据那个函数的不同
* 传入参数由String IP变成了SocketAddress socketAddress
* 这是因为服务端收到客户端发来的数据后,通过packet.getSocketAddress()就可以获取到一个SocketAddress对象
* 该对象存储了客户端的IP地址和端口号,在SendToServer类的sendDataToServer函数里
* 我们通过DatagramPacket packetToServer = new DatagramPacket(data, data.length ,serverAddress,10025);
* 这句话创建了一个服务端的packet对象,通过socket(客户端的)和packet(指向服务端)发送给了服务端
* 但是在这个函数里,我们可以直接使用packet.getSocketAddress()来创建客户端的packet(指向客户端)
* 通过socket(服务端)和packet(指向客户端)来向客户端发送数据了
*/
public void sendDataToClient(StringBuilder str,SocketAddress socketAddress) {
        try {
            byte data[] = str.toString().getBytes();//讲要发送的数据转换成字节
            //通过传进来的参数直接创建一个packet
            DatagramPacket packetToClient = new DatagramPacket(data, data.length ,socketAddress);
            //通过socket向客户端发送数据
            serverSocket.send(packetToClient);
        } catch (SocketException e) {
            e.printStackTrace();
            Log.e("######","SocketException"+e);
        } catch (UnknownHostException e) {
            e.printStackTrace();
            Log.e("######","UnknownHostException"+e);
        } catch (IOException e) {
            e.printStackTrace();
            Log.e("######","IOException"+e);
        }
    }

用法


	private void sendMessageToServer(final StringBuilder s,SocketAddress socketAddress){//封装成一个函数
        new Thread(new Runnable() {//开启一个线程来发送数据(所有的网络请求都需要在子线程执行)
            @Override
            public void run() {
            	sendToClient.sendDataToClient(s,socketAddress);//调用我们封装好的类里的函数,传入两个参数,s:要发送的数据,SocketAddress :接收到的客户端的数据
            }
        }).start();
    }

这样,服务端就成功回复客户端了

第三步

客户端接收服务端发来的数据

在第一步中我们也已经建好了客户端的监听器,当收到数据时,监听器就会监听到,然后就可以对收到的数据进行处理了

SendToServer里的ReceiveDataFromServer()函数

/*
	* 接受服务端发来的数据,注册客户端监听器,原理和上述服务端注册监听器相同
	*/
   public void ReceiveDataFromServer() {

        try {
            while(true) {//开启一个死循环,不断的查看是否有客户端发来数据
                byte fromServer[] = new byte[4 * 1024];//创建一个足够长的字节来作为接收到的数据的容器
                //创建一个packet,用来接收数据
                DatagramPacket packet = new DatagramPacket(fromServer, fromServer.length);
                clientSocket.receive(packet);//接收信息(如果有客户端发送过来)

				//将收到的信息转换成String
                String result = new String(packet.getData(), packet.getOffset(), packet.getLength());

				//下面是对收到的数据的处理
                if (!TextUtils.isEmpty(result)) {//如果收到的数据不为空
                    //TODO: 处理收到的数据
                    //在这里,我们不仅可以收到服务端发来的数据,同时也可以知道服务端的IP地址和端口号
                    //其实就是192.168.137.20(开头假设)和10025(开头假设)
                    //同样是通过packet.getSocketAddress()来获取
                }
                //Log.e("######SendToServer", "收到服务器返回的信息为:" + result);
            }

        } catch (SocketException e) {
            e.printStackTrace();
            Log.e("######SendToServer","SocketException"+e);
        } catch (IOException e) {
            e.printStackTrace();
            Log.e("######SendToServer","IOException"+e);
        }
    }

最后

1、客户端向服务端发送数据
2、服务端接收数据
3、服务端回复客户端
4、客户端接收数据

不断的这样循环,就完成了不同设备下的UDP通讯了,另外了解一下UDP的特点
1、相比于TCP,传输前不需要建立连接,知道IP就可以直接发送数据
2、传输数据快,占用内存小
3、尽最大速度传输数据,但是并不能保证数据的正确性

以及,服务端和客户端需要在同一个局域网下才可以噢!

                                                                                                                    to my dear 琪琪.
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值