Java利用UDP实现客户端之间通信

题目描述

利用数据报通信方式编写一程序,该程序生成两个客户端,一个服务器端,两个客户端可以相互进行简短的文字交流。

实现思路

为使客户端能够接收到其他客户端的数据报,客户端在创建时应该指定端口号,开启接收数据的线程和等待发送消息的线程,当接收到数据或发送数据后,重启线程。并且发送方在数据报中应包含接收端的地址,由服务器端对地址进行解析并转达数据包。

实现过程

1. 服务器端:

在Server.java中实现Server类,静态变量指定服务器端口,接收数据报的大小

public class Server {  
    static int serverPort = 8000;  
    static int packetSize = 1024;  

进入主函数时,初始化数据报套接字及接收数据报

    public static void main(String[] args) throws Exception {  
            DatagramSocket server = new DatagramSocket(serverPort);  
            byte[] data = new byte[packetSize];  
            DatagramPacket packet = new DatagramPacket(data, packetSize);  
            System.out.println("server is ready!!");

使用死循环处理每一个客户端发送的数据报;
将数据报分割为地址及信息两部分;
根据地址及信息创建新数据报,并发送到目标客户端,完成对消息的转述

    while (true) {  
        server.receive(packet);  
        System.out.println("server has received data from sender");  
        String dataStr = new String(data, 0, packet.getLength());  
        String[] dataSegments = dataStr.split(" ");  
        if(dataSegments.length >= 2){  
            InetAddress receiverAddress = InetAddress.getByName(dataSegments[0].split(":")[0]);  
            int receiverPort = Integer.parseInt(dataSegments[0].split(":")[1]);  
            DatagramPacket trulyData = new DatagramPacket(dataSegments[1].getBytes(),  
            dataSegments[1].length(), receiverAddress, receiverPort);  
            System.out.println("the message: "+dataSegments[1]);  
            server.send(trulyData);  
            System.out.println("server has sent data to receiver");  
        }  
    }

2. 客户端

客户端在创建时需指定端口号,这里使用命令行参数传入

public class Client {  
    public static void main(String[] args) throws Exception {  
        int port;  
        if (args.length > 0) {  
            port = Integer.parseInt(args[0]);  
            DatagramSocket socket = new DatagramSocket(port);  
            System.out.println("client is ready!!");

创建接收消息的线程及发送消息的线程等待输入

            // create a receiver thread  
            Thread receiveThread = new Thread(new ReceiverRunnable(socket));  
            receiveThread.start(); // receive message at any time  
            // create a sender thread  
            Thread sendThread = new Thread(new ClientSenderRunnable(socket));  
            sendThread.start();  
        }else{  
            System.out.println("you must indicate the port by adding argument");  
        }   
    }
}

(1)接收消息的线程

在Receiver.java中实现一个实现Runnable接口的可执行类
在构造方法中传入客户端数据报套接字,初始化接收端
在静态变量中定义数据报大小,以及接收数据报的字符数组

public class ReceiverRunnable implements Runnable {  
    static int dataSize = 1024;  
    DatagramPacket receivePacket;  
    byte[] data;  
    public DatagramSocket socket;  
    public ReceiverRunnable(DatagramSocket socket){  
        this.socket = socket;  
        data = new byte[dataSize];  
        receivePacket = new DatagramPacket(data, dataSize);  
    }

重写run方法,接收数据报并打印消息
在线程终点启动下一个接收消息的线程

    @Override  
    public void run(){  
        try{  
            socket.receive(receivePacket);  
            System.out.println("You have received a message: "+new String(data, dataSize));  
            Thread thread = new Thread(new ReceiverRunnable(socket));  
            thread.start();  
        }catch (IOException e){  
            e.fillInStackTrace();  
        }  
    }
}

(2)发送消息的线程

在ClientSenderRunnable.java中实现一个实现Runnable接口的可执行类
在构造方法中传入客户端数据报套接字,初始化发送端
在静态变量中指定服务器地址及端口,用于初始化数据报

public class ClientSenderRunnable implements Runnable{  
    static String serverName = "localhost";  
    static int serverPort = 8000;  
    public DatagramSocket socket;  
    public ClientSenderRunnable(DatagramSocket socket){  
        this.socket = socket;  
    }

重写run方法,读取键盘输入

    @Override  
    public void run() {  
        System.out.println("type as (address:port message):");  
        Scanner scanner = new Scanner(System.in);  
        String dataStr;  
        if (scanner.hasNextLine()) {  
            dataStr = scanner.nextLine();  
            System.out.println("your header and message: " + dataStr);  
            byte[] data = dataStr.getBytes();

根据静态变量指定的服务端地址打包数据报并发送

// pack up the data  
            try {  
                InetAddress serverAddress = InetAddress.getByName(serverName);  
                DatagramPacket packet = new DatagramPacket(data, data.length, serverAddress, serverPort);  
                // create socket and send data packed  
                socket.send(packet);  
                System.out.println("client has sent data to server!!");  
            } catch (UnknownHostException e) {  
                e.printStackTrace();  
            } catch (IOException e) {  
                e.printStackTrace();  
            }  
        }

在线程终点启动下一个发送消息的线程

        Thread thread = new Thread(new ClientSenderRunnable(socket));  
        thread.start();  
    }  
}

运行结果

启动服务端
image.png

启动两个客户端
image.png
image.png

由客户端一(8001端口)向客户端二(8002端口)发送消息
image.png

服务器端成功接收到消息并转发
image.png

客户端二(8002端口)成功监听到接收的消息
image.png

同理,由客户端二向客户端一发送消息也同样得以完成

客户端二:
image.png

服务器端:
image.png

客户端一:
image.png

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值